diff --git a/.gitignore b/.gitignore
index 6078f6b1f..309d1e914 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,5 @@ yarn-debug.log*
yarn-error.log*
store.db
store.db.compacted
+.idea
+.vscode
diff --git a/packages/util-crypto/src/nacl/box/fromSecret.spec.ts b/packages/util-crypto/src/nacl/box/fromSecret.spec.ts
new file mode 100644
index 000000000..376632378
--- /dev/null
+++ b/packages/util-crypto/src/nacl/box/fromSecret.spec.ts
@@ -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
+ });
+ });
+});
diff --git a/packages/util-crypto/src/nacl/box/fromSecret.ts b/packages/util-crypto/src/nacl/box/fromSecret.ts
new file mode 100644
index 000000000..220b2d880
--- /dev/null
+++ b/packages/util-crypto/src/nacl/box/fromSecret.ts
@@ -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
+ *
+ *
+ * ```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));
+}
diff --git a/packages/util-crypto/src/nacl/index.ts b/packages/util-crypto/src/nacl/index.ts
index e246737d3..6dad09818 100644
--- a/packages/util-crypto/src/nacl/index.ts
+++ b/packages/util-crypto/src/nacl/index.ts
@@ -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';
diff --git a/packages/util-crypto/src/nacl/open.spec.ts b/packages/util-crypto/src/nacl/open.spec.ts
new file mode 100644
index 000000000..4dbe0be59
--- /dev/null
+++ b/packages/util-crypto/src/nacl/open.spec.ts
@@ -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);
+ });
+});
diff --git a/packages/util-crypto/src/nacl/open.ts b/packages/util-crypto/src/nacl/open.ts
new file mode 100644
index 000000000..8506a85d2
--- /dev/null
+++ b/packages/util-crypto/src/nacl/open.ts
@@ -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
+ *
+ *
+ * ```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;
+}
diff --git a/packages/util-crypto/src/nacl/seal.spec.ts b/packages/util-crypto/src/nacl/seal.spec.ts
new file mode 100644
index 000000000..ffc916e2e
--- /dev/null
+++ b/packages/util-crypto/src/nacl/seal.spec.ts
@@ -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)
+ });
+ });
+});
diff --git a/packages/util-crypto/src/nacl/seal.ts b/packages/util-crypto/src/nacl/seal.ts
new file mode 100644
index 000000000..eff7a178d
--- /dev/null
+++ b/packages/util-crypto/src/nacl/seal.ts
@@ -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
+ *
+ *
+ * ```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
+ };
+}