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:
Hyungsuk Kang
2019-12-17 11:12:06 +01:00
committed by Jaco Greeff
parent 8d80bf869b
commit 4e4ff5de4b
8 changed files with 227 additions and 0 deletions
@@ -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));
}
+3
View File
@@ -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);
});
});
+23
View File
@@ -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)
});
});
});
+33
View File
@@ -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
};
}