Add cross client Encryption/Decryption (#496)
* chore: add a test * Add KDF case for double ratchet * chore: add .idea in .gitignore * chore: add .vscode to .gitignore * Remove comma-dangle
This commit is contained in:
committed by
Jaco Greeff
parent
8d80bf869b
commit
4e4ff5de4b
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { naclBoxKeypairFromSecret } from '..';
|
||||
|
||||
describe('naclBoxKeypairFromSecret', (): void => {
|
||||
const secretKey = new Uint8Array([
|
||||
18, 52, 86, 120, 144, 18, 52, 86,
|
||||
120, 144, 18, 52, 86, 120, 144, 18,
|
||||
18, 52, 86, 120, 144, 18, 52, 86,
|
||||
120, 144, 18, 52, 86, 120, 144, 18
|
||||
]);
|
||||
|
||||
it('generates a valid publicKey/secretKey pair', (): void => {
|
||||
expect(
|
||||
naclBoxKeypairFromSecret(secretKey)
|
||||
).toEqual({
|
||||
publicKey: new Uint8Array([
|
||||
206, 110, 228, 222, 155, 43, 229, 254,
|
||||
38, 117, 157, 116, 224, 87, 139, 171,
|
||||
17, 195, 144, 167, 106, 36, 161, 28,
|
||||
25, 232, 127, 47, 219, 165, 44, 102
|
||||
]),
|
||||
secretKey
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Keypair } from '../../types';
|
||||
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
/**
|
||||
* @name naclBoxKeypairFromSecret
|
||||
* @summary Creates a new public/secret box keypair from a secret.
|
||||
* @description
|
||||
* Returns a object containing a box `publicKey` & `secretKey` generated from the supplied secret.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { naclBoxKeypairFromSecret } from '@polkadot/util-crypto';
|
||||
*
|
||||
* naclBoxKeypairFromSecret(...); // => { secretKey: [...], publicKey: [...] }
|
||||
* ```
|
||||
*/
|
||||
export default function naclBoxKeypairFromSecret (secret: Uint8Array): Keypair {
|
||||
return nacl.box.keyPair.fromSecretKey(secret.slice(0, 32));
|
||||
}
|
||||
@@ -14,3 +14,6 @@ export { default as naclKeypairFromSeed } from './keypair/fromSeed';
|
||||
export { default as naclKeypairFromString } from './keypair/fromString';
|
||||
export { default as naclSign } from './sign';
|
||||
export { default as naclVerify } from './verify';
|
||||
export { default as naclBoxKeypairFromSecret } from './box/fromSecret';
|
||||
export { default as naclOpen } from './open';
|
||||
export { default as naclSeal } from './seal';
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { naclBoxKeypairFromSecret, naclKeypairFromString, naclOpen, naclSeal } from '.';
|
||||
import fromPath from '@polkadot/util-crypto/key/fromPath';
|
||||
import { keyExtractPath } from '@polkadot/util-crypto/key';
|
||||
|
||||
describe('naclOpen', (): void => {
|
||||
it('opens a sealed message by the sender', (): void => {
|
||||
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
|
||||
const sender = naclKeypairFromString('sender');
|
||||
const receiver = naclKeypairFromString('receiver');
|
||||
const senderBox = naclBoxKeypairFromSecret(sender.secretKey);
|
||||
const receiverBox = naclBoxKeypairFromSecret(receiver.secretKey);
|
||||
|
||||
const { sealed, nonce } = naclSeal(message, senderBox.secretKey, receiverBox.publicKey);
|
||||
expect(
|
||||
naclOpen(sealed, nonce, senderBox.publicKey, receiverBox.secretKey)
|
||||
).toEqual(
|
||||
message
|
||||
);
|
||||
});
|
||||
|
||||
it('polkadot does double ratchet', () => {
|
||||
const sender = naclKeypairFromString('sender');
|
||||
const receiver = naclKeypairFromString('receiver');
|
||||
|
||||
// Make id key for identifying each other
|
||||
const senderIdKey = fromPath(sender, keyExtractPath('//1//1//1//1').path, 'ed25519');
|
||||
const receiverIdKey = fromPath(receiver, keyExtractPath('//2//2//2//2').path, 'ed25519');
|
||||
|
||||
// Receiver sends encrypting public key to receive message to decrypt with his private key
|
||||
const receiverIdBoxKey = naclBoxKeypairFromSecret(receiverIdKey.secretKey);
|
||||
console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey.publicKey}`);
|
||||
|
||||
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
|
||||
const senderIdBoxKey = naclBoxKeypairFromSecret(senderIdKey.secretKey);
|
||||
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
|
||||
const { sealed, nonce } = naclSeal(message, senderIdBoxKey.secretKey, receiverIdBoxKey.publicKey);
|
||||
console.log(`Sender sends encrypted message to receiver ${sealed}, ${nonce}`);
|
||||
|
||||
// Receiver opens encrypted message from the sender
|
||||
let opened = naclOpen(sealed, nonce, senderIdBoxKey.publicKey, receiverIdBoxKey.secretKey);
|
||||
console.log(`The sealed message is ${opened}`);
|
||||
|
||||
// Oh no the Id key is hacked, derive Id key again from another index
|
||||
const senderIdKey2 = fromPath(sender, keyExtractPath('//1//1//1//2').path, 'ed25519');
|
||||
const receiverIdKey2 = fromPath(receiver, keyExtractPath('//2//2//2//3').path, 'ed25519');
|
||||
|
||||
// Receiver sends encrypting public key to receive message to decrypt with his private key
|
||||
const receiverIdBoxKey2 = naclBoxKeypairFromSecret(receiverIdKey2.secretKey);
|
||||
console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey2.publicKey}`);
|
||||
|
||||
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
|
||||
const senderIdBoxKey2 = naclBoxKeypairFromSecret(senderIdKey2.secretKey);
|
||||
const message2 = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
|
||||
let ctx = naclSeal(message2, senderIdBoxKey2.secretKey, receiverIdBoxKey2.publicKey);
|
||||
const sealed2 = ctx.sealed;
|
||||
const nonce2 = ctx.nonce;
|
||||
console.log(`Sender sends encrypted message to receiver ${sealed}, ${nonce}`);
|
||||
|
||||
// Receiver opens encrypted message from the sender
|
||||
opened = naclOpen(sealed2, nonce2, senderIdBoxKey2.publicKey, receiverIdBoxKey2.secretKey);
|
||||
console.log(`The sealed message is ${opened}`);
|
||||
|
||||
// This time derive key for each message to send and receive
|
||||
// Receiver sends encrypting public key to receive message to decrypt with his private key
|
||||
const receiverIdBoxKey2One = naclBoxKeypairFromSecret(receiverIdBoxKey2.secretKey);
|
||||
console.log(`Receiver sends receiver's public key to sender ${receiverIdBoxKey2One.publicKey}`);
|
||||
|
||||
// Sender encrypts message to send with the public key the receiver sent and send it to receiver
|
||||
const senderIdBoxKey2One = naclBoxKeypairFromSecret(senderIdBoxKey2.secretKey);
|
||||
const message3 = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
|
||||
ctx = naclSeal(message3, senderIdBoxKey2One.secretKey, receiverIdBoxKey2One.publicKey);
|
||||
const sealed3 = ctx.sealed;
|
||||
const nonce3 = ctx.nonce;
|
||||
console.log(`Sender sends encrypted message to receiver ${sealed3}, ${nonce3}`);
|
||||
|
||||
// Receiver opens encrypted message from the sender
|
||||
opened = naclOpen(sealed2, nonce2, senderIdBoxKey2One.publicKey, receiverIdBoxKey2One.secretKey);
|
||||
console.log(`The sealed message is ${opened}`);
|
||||
});
|
||||
|
||||
it('returns null on invalid', (): void => {
|
||||
expect(
|
||||
naclOpen(new Uint8Array(), new Uint8Array(24), new Uint8Array(32), new Uint8Array(32))
|
||||
).toEqual(null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
/**
|
||||
* @name naclOpen
|
||||
* @summary Opens a message using the receiver's secretKey and nonce
|
||||
* @description
|
||||
* Returns a message sealed by the sender, using the receiver's `secret` and `nonce`.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { naclOpen } from '@polkadot/util-crypto';
|
||||
*
|
||||
* naclOpen([...], [...], [...]); // => [...]
|
||||
* ```
|
||||
*/
|
||||
export default function naclOpen (sealed: Uint8Array, nonce: Uint8Array, senderBoxPublic: Uint8Array, receiverBoxSecret: Uint8Array): Uint8Array | null {
|
||||
return nacl.box.open(sealed, nonce, senderBoxPublic, receiverBoxSecret) || null;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { naclSeal, naclKeypairFromString, naclBoxKeypairFromSecret } from '.';
|
||||
|
||||
describe('naclSeal', (): void => {
|
||||
it('seals a message', (): void => {
|
||||
const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]);
|
||||
const sender = naclKeypairFromString('sender');
|
||||
const receiver = naclKeypairFromString('receiver');
|
||||
const senderBox = naclBoxKeypairFromSecret(sender.secretKey);
|
||||
const receiverBox = naclBoxKeypairFromSecret(receiver.secretKey);
|
||||
const senderBoxSecret = senderBox.secretKey;
|
||||
const receiverBoxPublic = receiverBox.publicKey;
|
||||
expect(
|
||||
naclSeal(message, senderBoxSecret, receiverBoxPublic, new Uint8Array(24))
|
||||
).toEqual({
|
||||
sealed: new Uint8Array([137, 195, 104, 255, 67, 199, 40, 235, 148, 177, 180, 73, 239, 131, 215, 80, 61, 191, 167, 213, 152, 138, 5, 47, 135]),
|
||||
nonce: new Uint8Array(24)
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2017-2019 @polkadot/util-crypto authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import nacl from 'tweetnacl';
|
||||
|
||||
import randomAsU8a from '../random/asU8a';
|
||||
|
||||
interface Sealed {
|
||||
sealed: Uint8Array;
|
||||
nonce: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name naclSeal
|
||||
* @summary Seals a message using the sender's encrypting secretKey, receiver's public key, and nonce
|
||||
* @description
|
||||
* Returns an encrypted message which can be open only by receiver's secretKey. If the `nonce` was not supplied, a random value is generated.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { naclEncrypt } from '@polkadot/util-crypto';
|
||||
*
|
||||
* naclSeal([...], [...], [...], [...]); // => [...]
|
||||
* ```
|
||||
*/
|
||||
export default function naclSeal (message: Uint8Array, senderBoxSecret: Uint8Array, receiverBoxPublic: Uint8Array, nonce: Uint8Array = randomAsU8a(24)): Sealed {
|
||||
return {
|
||||
sealed: nacl.box(message, nonce, receiverBoxPublic, senderBoxSecret),
|
||||
nonce
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user