Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Crypto): Implement asymmetric encoding/decoding using tweennacl and Ed25519 keypair #466

Merged
merged 8 commits into from Nov 7, 2019
54 changes: 53 additions & 1 deletion es/utils/crypto.js
Expand Up @@ -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
Expand Down Expand Up @@ -659,3 +661,53 @@ export function deserialize (binary, opts = { prettyTags: false }) {
})
}
}

/**
* This function encrypts a message using base58check encoded and 'ak' prefixed
* publicKey such that only the corresponding secretKey will
* be able to decrypt
* @rtype (msg: String, publicKey: String) => Object
* @param {Buffer} msg - Data to encode
* @param {String} publicKey - Public key
* @return {Object}
*/
export function encryptData (msg, publicKey) {
const ephemeralKeyPair = nacl.box.keyPair()
const pubKeyUInt8Array = decodeBase58Check(assertedType(publicKey, 'ak'))
const nonce = nacl.randomBytes(nacl.box.nonceLength)

const encryptedMessage = nacl.box(
Buffer.from(msg),
nonce,
ed2curve.convertPublicKey(pubKeyUInt8Array),
ephemeralKeyPair.secretKey
)

return {
ciphertext: Buffer.from(encryptedMessage).toString('hex'),
ephemPubKey: Buffer.from(ephemeralKeyPair.publicKey).toString('hex'),
nonce: Buffer.from(nonce).toString('hex'),
version: 'x25519-xsalsa20-poly1305'
}
}

/**
* 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 {Buffer|null}
*/
export function decryptData (secretKey, encryptedData) {
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')
const decrypted = nacl.box.open(
ciphertext,
nonce,
ephemPubKey,
receiverSecretKeyUint8Array
)
return decrypted ? Buffer.from(decrypted) : decrypted
}
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -38,6 +38,7 @@
"blakejs": "^1.1.0",
"bs58check": "^2.1.1",
"commander": "^4.0.0",
"ed2curve": "^0.2.1",
"joi-browser": "^13.4.0",
"libsodium-wrappers-sumo": "0.7.6",
"ramda": "^0.26.1",
Expand Down
26 changes: 26 additions & 0 deletions test/unit/crypto.js
Expand Up @@ -159,4 +159,30 @@ describe('crypto', () => {
buildTxHash(txRaw).should.be.equal(expectedHash)
buildTxHash(rlpEncodedTx).should.be.equal(expectedHash)
})
describe('Encrypt data using nacl box asymmetric encryption', async () => {
const { publicKey, secretKey } = Crypto.generateKeyPair()
const msgString = 'Test string'
const msgBuffer = Buffer.from(msgString)

it('Encrypt String/Buffer and decrypt', () => {
const encryptedString = Crypto.encryptData(msgString, publicKey)
const encryptedBuffer = Crypto.encryptData(msgBuffer, publicKey)

const decryptedString = Crypto.decryptData(secretKey, encryptedString)
const decryptedBuffer = Crypto.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 = Crypto.encryptData(msgString, keyPair.publicKey)
const encryptedBuffer = Crypto.encryptData(msgBuffer, keyPair.publicKey)

const decryptedString = Crypto.decryptData(secretKey, encryptedString)
const decryptedBuffer = Crypto.decryptData(secretKey, encryptedBuffer)
const isNull = decryptedBuffer === null && decryptedString === null
isNull.should.be.equal(true)
})
})
})