Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
.DS_Store
package-lock.json
97 changes: 95 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const ethUtil = require('ethereumjs-util')
const ethAbi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util');
const ethAbi = require('ethereumjs-abi');
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');

const TYPED_MESSAGE_SCHEMA = {
type: 'object',
Expand Down Expand Up @@ -225,6 +227,88 @@ module.exports = {
return ethUtil.bufferToHex(sender)
},

encrypt: function(receiverPublicKey, msgParams, version) {

switch(version) {
case 'x25519-xsalsa20-poly1305':
console.log(typeof msgParams.data )
if( typeof msgParams.data == 'undefined'){
throw new Error('Cannot detect secret message, message params should be of the form {data: "secret message"} ')
}
//generate ephemeral keypair
var ephemeralKeyPair = nacl.box.keyPair()

// assemble encryption parameters - from string to UInt8
try {
var pubKeyUInt8Array = nacl.util.decodeBase64(receiverPublicKey);
} catch (err){
throw new Error('Bad public key')
}

var msgParamsUInt8Array = nacl.util.decodeUTF8(msgParams.data);
var nonce = nacl.randomBytes(nacl.box.nonceLength);

// encrypt
var encryptedMessage = nacl.box(msgParamsUInt8Array, nonce, pubKeyUInt8Array, ephemeralKeyPair.secretKey);

// handle encrypted data
var output = {
version: 'x25519-xsalsa20-poly1305',
nonce: nacl.util.encodeBase64(nonce),
ephemPublicKey: nacl.util.encodeBase64(ephemeralKeyPair.publicKey),
ciphertext: nacl.util.encodeBase64(encryptedMessage)
};
// return encrypted msg data
return output;

default:
throw new Error('Encryption type/version not supported')

}
},

decrypt: function(encryptedData, receiverPrivateKey) {

switch(encryptedData.version) {
case 'x25519-xsalsa20-poly1305':
//string to buffer to UInt8Array
var recieverPrivateKeyUint8Array = nacl_decodeHex(receiverPrivateKey)
var recieverEncryptionPrivateKey = nacl.box.keyPair.fromSecretKey(recieverPrivateKeyUint8Array).secretKey

// assemble decryption parameters
var nonce = nacl.util.decodeBase64(encryptedData.nonce);
var ciphertext = nacl.util.decodeBase64(encryptedData.ciphertext);
var ephemPublicKey = nacl.util.decodeBase64(encryptedData.ephemPublicKey);

// decrypt
var decryptedMessage = nacl.box.open(ciphertext, nonce, ephemPublicKey, recieverEncryptionPrivateKey);

// return decrypted msg data
try {
var output = nacl.util.encodeUTF8(decryptedMessage);
}catch(err) {
throw new Error('Decryption failed.')
}

if (output){
return output;
}else{
throw new Error('Decryption failed.')
}


default:
throw new Error('Encryption type/version not supported.')
}

},

getEncryptionPublicKey: function(privateKey){
var privateKeyUint8Array = nacl_decodeHex(privateKey)
var encryptionPublicKey = nacl.box.keyPair.fromSecretKey(privateKeyUint8Array).publicKey
return nacl.util.encodeBase64(encryptionPublicKey)
}

signTypedData: function (privateKey, msgParams) {
const message = TypedDataUtils.sign(msgParams.data)
const sig = ethUtil.ecsign(message, privateKey)
Expand Down Expand Up @@ -286,3 +370,12 @@ function padWithZeroes (number, length) {
}
return myString
}

//converts hex strings to the Uint8Array format used by nacl
function nacl_decodeHex(msgHex) {
var msgBase64 = (new Buffer(msgHex, 'hex')).toString('base64');
return nacl.util.decodeBase64(msgBase64);
}



5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
},
"homepage": "https://github.com/MetaMask/eth-sig-util#readme",
"dependencies": {
"elliptic": "^6.4.0",
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"ethereumjs-util": "^5.1.1",
"tweetnacl": "^1.0.0",
"tweetnacl-util": "^0.15.0"
"ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1"
},
Expand Down
102 changes: 102 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,108 @@ function typedSignatureHashThrowsTest(opts) {
})
}

const bob = {
ethereumPrivateKey: '7e5374ec2ef0d91761a6e72fdf8f6ac665519bfdf6da0a2329cf0d804514b816',
encryptionPrivateKey: 'flN07C7w2Rdhpucv349qxmVRm/322gojKc8NgEUUuBY=',
encryptionPublicKey: 'C5YMNdqE4kLgxQhJO1MfuQcHP5hjVSXzamzd/TxlR0U=' }

const secretMessage = {data:'My name is Satoshi Buterin'};

const encryptedData = { version: 'x25519-xsalsa20-poly1305',
nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej',
ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' };

test("Getting bob's encryptionPublicKey", async t => {
t.plan(1);

const result = await sigUtil.getEncryptionPublicKey(bob.ethereumPrivateKey)
t.equal(result, bob.encryptionPublicKey);
});

//encryption test
test("Alice encrypts message with bob's encryptionPublicKey", async t => {


t.plan(4);

const result = await sigUtil.encrypt(
bob.encryptionPublicKey,
secretMessage,
'x25519-xsalsa20-poly1305'
);

console.log("RESULT", result)

t.ok(result.version);
t.ok(result.nonce);
t.ok(result.ephemPublicKey);
t.ok(result.ciphertext);

});

// decryption test
test("Bob decrypts message that Alice sent to him", t => {
t.plan(1);

const result = sigUtil.decrypt(encryptedData, bob.ethereumPrivateKey);
t.equal(result, secretMessage.data);
});

test('Decryption failed because version is wrong or missing', t =>{
t.plan(1)

const badVersionData = { version: 'x256k1-aes256cbc',
nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej',
ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' };

t.throws( function() { sigUtil.decrypt(badVersionData, bob.ethereumPrivateKey)}, 'Encryption type/version not supported.')
});

test('Decryption failed because nonce is wrong or missing', t => {
t.plan(1);

//encrypted data
const badNonceData = { version: 'x25519-xsalsa20-poly1305',
nonce: '',
ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' };

t.throws(function() { sigUtil.decrypt(badNonceData, bob.ethereumPrivateKey)}, 'Decryption failed.')

});

test('Decryption failed because ephemPublicKey is wrong or missing', t => {
t.plan(1);

//encrypted data
const badEphemData = { version: 'x25519-xsalsa20-poly1305',
nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej',
ephemPublicKey: 'FFFF/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' };

t.throws(function() { sigUtil.decrypt(badEphemData, bob.ethereumPrivateKey)}, 'Decryption failed.')
});

test('Decryption failed because cyphertext is wrong or missing', async t => {
t.plan(1);

//encrypted data
const badCypherData = { version: 'x25519-xsalsa20-poly1305',
nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej',
ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'ffffff/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' };

t.throws(function() { sigUtil.decrypt(badEphemData, bob.ethereumPrivateKey)}, 'Decryption failed.')
});

test("Decryption fails because you are not the recipient", t => {
t.plan(1);

t.throws(function() { sigUtil.decrypt(encryptedData, alice.ethereumPrivateKey)}, 'Decryption failed.')
});

test('signedTypeData', (t) => {
t.plan(8)
const utils = sigUtil.TypedDataUtils
Expand Down
Loading