From 65e3cc5057f41e48299b792931944994d3f539ad Mon Sep 17 00:00:00 2001 From: nduchak Date: Mon, 10 Jun 2019 16:50:42 +0300 Subject: [PATCH] feat(Crypto): Implement asymetric encoding/decoding using tweennacl and Ed25519 keypair Converts a 32-byte Ed25519 public key into a 32-byte Curve25519 public key.Converts a 64-byte Ed25519 secret key (or just the first 32-byte part of it, which is the secret value) into a 32-byte Curve25519 secret key #465 --- es/utils/crypto.js | 49 +++++++++----------------- package-lock.json | 85 ++++++++++++++++++++++++++++++++++----------- package.json | 1 + test/unit/crypto.js | 30 ++++++++++++---- 4 files changed, 107 insertions(+), 58 deletions(-) diff --git a/es/utils/crypto.js b/es/utils/crypto.js index 5fc85e8b35..663fe23953 100644 --- a/es/utils/crypto.js +++ b/es/utils/crypto.js @@ -24,10 +24,12 @@ import bs58check from 'bs58check' import * as RLP from 'rlp' import { blake2b } from 'blakejs' +import ed2curve from 'ed2curve' import nacl from 'tweetnacl' import aesjs from 'aes-js' -import { leftPad, rightPad, toBytes } from './bytes' import shajs from 'sha.js' + +import { leftPad, rightPad, toBytes } from './bytes' import { decode as decodeNode } from '../tx/builder/helpers' const Ecb = aesjs.ModeOfOperation.ecb @@ -664,30 +666,22 @@ export function deserialize (binary, opts = { prettyTags: false }) { * publicKey such that only the corresponding secretKey will * be able to decrypt * @rtype (msg: String, publicKey: String) => Object - * @param {String} msg - Data to encode + * @param {Buffer} msg - Data to encode * @param {String} publicKey - Public key * @return {Object} */ -export function encryptData (msg, {publicKey, secretKey}) { +export function encryptData (msg, publicKey) { const ephemeralKeyPair = nacl.box.keyPair() - const pubKeyUInt8Array = Buffer.from(decodeBase58Check(assertedType(publicKey, 'ak'))) - const msgParamsUInt8Array = Buffer.from(new TextEncoder().encode(msg)) - const nonce = Buffer.from(nacl.randomBytes(nacl.box.nonceLength)) + const pubKeyUInt8Array = decodeBase58Check(assertedType(publicKey, 'ak')) + const nonce = nacl.randomBytes(nacl.box.nonceLength) const encryptedMessage = nacl.box( - msgParamsUInt8Array, + Buffer.from(msg), nonce, - pubKeyUInt8Array, - Buffer.from(ephemeralKeyPair.secretKey) + ed2curve.convertPublicKey(pubKeyUInt8Array), + ephemeralKeyPair.secretKey ) - console.log(ephemeralKeyPair.secretKey.length) - console.log() - console.log(nacl.box.open( - encryptedMessage, - nonce, - ephemeralKeyPair.publicKey, - Buffer.from(secretKey, 'hex') - )) + return { ciphertext: Buffer.from(encryptedMessage).toString('hex'), ephemPubKey: Buffer.from(ephemeralKeyPair.publicKey).toString('hex'), @@ -697,31 +691,22 @@ export function encryptData (msg, {publicKey, secretKey}) { } /** - * This function encrypts a message using base58check encoded and 'ak' prefixed - * publicKey such that only the corresponding secretKey will - * be able to decrypt - * @rtype (secretKey: String, publicKey: String) => Object + * This function decrypt a message using secret key + * @rtype (secretKey: String, encryptedData: Object) => Buffer|null * @param {String} secretKey - Secret key * @param {Object} encryptedData - Encrypted data - * @return {Object} + * @return {Buffer|null} */ export function decryptData (secretKey, encryptedData) { - const receiverSecretKeyUint8Array = Buffer.from(secretKey, 'hex').slice(0, 32) + const receiverSecretKeyUint8Array = ed2curve.convertSecretKey(Buffer.from(secretKey, 'hex')) const nonce = Buffer.from(encryptedData.nonce, 'hex') const ciphertext = Buffer.from(encryptedData.ciphertext, 'hex') const ephemPubKey = Buffer.from(encryptedData.ephemPubKey, 'hex') - - console.log(Buffer.from(secretKey, 'hex')) - console.log(receiverSecretKeyUint8Array.length) - console.log(nonce) - console.log(ciphertext) - console.log(ephemPubKey) - - const decryptedMessage = nacl.box.open( + const decrypted = nacl.box.open( ciphertext, nonce, ephemPubKey, receiverSecretKeyUint8Array ) - return decryptedMessage + return decrypted ? Buffer.from(decrypted) : decrypted } diff --git a/package-lock.json b/package-lock.json index 6c43674015..1f085589df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aeternity/aepp-sdk", - "version": "3.4.0", + "version": "3.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1863,7 +1863,8 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", - "dev": true + "dev": true, + "optional": true }, "acorn-dynamic-import": { "version": "3.0.0", @@ -2121,7 +2122,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "dev": true, + "optional": true }, "assertion-error": { "version": "1.1.0", @@ -3290,6 +3292,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -3596,7 +3599,8 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true + "dev": true, + "optional": true }, "cssstyle": { "version": "0.2.37", @@ -3798,7 +3802,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "dev": true, + "optional": true }, "delegates": { "version": "1.0.0", @@ -3995,6 +4000,21 @@ "safer-buffer": "^2.1.0" } }, + "ed2curve": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.2.1.tgz", + "integrity": "sha1-Iuaqo1aePE2/Tu+ilhLsMp5YGQw=", + "requires": { + "tweetnacl": "0.x.x" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4926,7 +4946,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dev": true, + "optional": true }, "fast-deep-equal": { "version": "1.1.0", @@ -5284,7 +5305,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5305,12 +5327,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5325,17 +5349,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5452,7 +5479,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5464,6 +5492,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5478,6 +5507,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5485,12 +5515,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5509,6 +5541,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5596,7 +5629,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5608,6 +5642,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5693,7 +5728,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5729,6 +5765,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5748,6 +5785,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5791,12 +5829,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -7426,13 +7466,15 @@ "version": "1.35.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", - "dev": true + "dev": true, + "optional": true }, "mime-types": { "version": "2.1.19", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "dev": true, + "optional": true, "requires": { "mime-db": "~1.35.0" } @@ -8432,7 +8474,8 @@ "version": "1.1.29", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", - "dev": true + "dev": true, + "optional": true }, "public-encrypt": { "version": "4.0.2", @@ -10339,6 +10382,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, + "optional": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -10348,7 +10392,8 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index f09c70859a..63ba189e41 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "blakejs": "^1.1.0", "bs58check": "^2.1.1", "commander": "^2.14.1", + "ed2curve": "^0.2.1", "joi-browser": "^13.4.0", "json-bigint": "github:davidyuk/json-bigint", "libsodium-wrappers-sumo": "0.7.3", diff --git a/test/unit/crypto.js b/test/unit/crypto.js index 746e5ea120..f7cb70ee58 100644 --- a/test/unit/crypto.js +++ b/test/unit/crypto.js @@ -144,12 +144,30 @@ describe('crypto', () => { const fromHexAddress = 'ak_' + encodeBase58Check(Buffer.from(hex.slice(2), 'hex')) fromHexAddress.should.be.equal(address) }) - it.only('Encrypt data using nacl box asymmetric encryption', () => { - const msg = 'Test string asasassasaasasasasas' + describe('Encrypt data using nacl box asymmetric encryption', async () => { const { publicKey, secretKey } = Crypto.generateKeyPair() - const encrypted = encryptData(msg, {publicKey, secretKey}) - console.log(encrypted) - // console.log('////////////////////') - // console.log(decryptData(secretKey, encrypted)) + const msgString = 'Test string' + const msgBuffer = Buffer.from(msgString) + + it('Encrypt String/Buffer and decrypt', () => { + const encryptedString = encryptData(msgString, publicKey) + const encryptedBuffer = encryptData(msgBuffer, publicKey) + + const decryptedString = decryptData(secretKey, encryptedString) + const decryptedBuffer = decryptData(secretKey, encryptedBuffer) + Buffer.from(decryptedString).toString().should.be.equal(msgString) + decryptedBuffer.equals(msgBuffer).should.be.equal(true) + }) + it('Decrypt with wrong secret', () => { + const keyPair = Crypto.generateKeyPair() + + const encryptedString = encryptData(msgString, keyPair.publicKey) + const encryptedBuffer = encryptData(msgBuffer, keyPair.publicKey) + + const decryptedString = decryptData(secretKey, encryptedString) + const decryptedBuffer = decryptData(secretKey, encryptedBuffer) + const isNull = decryptedBuffer === null && decryptedString === null + isNull.should.be.equal(true) + }) }) })