diff --git a/browser.js b/browser.js index a143a70..05ac3d7 100644 --- a/browser.js +++ b/browser.js @@ -6,9 +6,12 @@ var ec = new EC("secp256k1"); var browserCrypto = global.crypto || global.msCrypto || {}; var subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; -var nodeCrypto = require('crypto'); +var nodeCrypto = require("crypto"); -const EC_GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex'); +const EC_GROUP_ORDER = Buffer.from( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "hex" +); const ZERO32 = Buffer.alloc(32, 0); function assert(condition, message) { @@ -17,17 +20,17 @@ function assert(condition, message) { } } -function isScalar (x) { +function isScalar(x) { return Buffer.isBuffer(x) && x.length === 32; } function isValidPrivateKey(privateKey) { - if (!isScalar(privateKey)) - { + if (!isScalar(privateKey)) { return false; } - return privateKey.compare(ZERO32) > 0 && // > 0 - privateKey.compare(EC_GROUP_ORDER) < 0; // < G + return ( + privateKey.compare(ZERO32) > 0 && privateKey.compare(EC_GROUP_ORDER) < 0 // > 0 + ); // < G } // Compare two buffers in constant time to prevent timing attacks. @@ -37,7 +40,7 @@ function equalConstTime(b1, b2) { } var res = 0; for (var i = 0; i < b1.length; i++) { - res |= b1[i] ^ b2[i]; // jshint ignore:line + res |= b1[i] ^ b2[i]; // jshint ignore:line } return res === 0; } @@ -47,7 +50,7 @@ not, since the functions are different and does not convert using browserify */ function randomBytes(size) { var arr = new Uint8Array(size); - if (typeof window === 'undefined') { + if (typeof window === "undefined") { return Buffer.from(nodeCrypto.randomBytes(size)); } else { browserCrypto.getRandomValues(arr); @@ -57,7 +60,7 @@ function randomBytes(size) { function sha512(msg) { return new Promise(function(resolve) { - var hash = nodeCrypto.createHash('sha512'); + var hash = nodeCrypto.createHash("sha512"); var result = hash.update(msg).digest(); resolve(new Uint8Array(result)); }); @@ -67,24 +70,24 @@ function getAes(op) { return function(iv, key, data) { return new Promise(function(resolve) { if (subtle) { - var importAlgorithm = {name: "AES-CBC"}; + var importAlgorithm = { name: "AES-CBC" }; var keyp = subtle.importKey("raw", key, importAlgorithm, false, [op]); - return keyp.then(function(cryptoKey) { - var encAlgorithm = {name: "AES-CBC", iv: iv}; - return subtle[op](encAlgorithm, cryptoKey, data); - }).then(function(result) { - resolve(Buffer.from(new Uint8Array(result))); - }); + return keyp + .then(function(cryptoKey) { + var encAlgorithm = { name: "AES-CBC", iv: iv }; + return subtle[op](encAlgorithm, cryptoKey, data); + }) + .then(function(result) { + resolve(Buffer.from(new Uint8Array(result))); + }); } else { - if (op === 'encrypt') { - var cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv); - cipher.update(data); - resolve(cipher.final()); - } - else if (op === 'decrypt') { - var decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv); - decipher.update(data); - resolve(decipher.final()); + if (op === "encrypt") { + var cipher = nodeCrypto.createCipheriv("aes-256-cbc", key, iv); + + resolve(Buffer.concat([cipher.update(data), cipher.final()])); + } else if (op === "decrypt") { + var decipher = nodeCrypto.createDecipheriv("aes-256-cbc", key, iv); + resolve(Buffer.concat([decipher.update(data), decipher.final()])); } } }); @@ -96,7 +99,7 @@ var aesCbcDecrypt = getAes("decrypt"); function hmacSha256Sign(key, msg) { return new Promise(function(resolve) { - var hmac = nodeCrypto.createHmac('sha256', Buffer.from(key)); + var hmac = nodeCrypto.createHmac("sha256", Buffer.from(key)); hmac.update(msg); var result = hmac.digest(); resolve(result); @@ -105,7 +108,7 @@ function hmacSha256Sign(key, msg) { function hmacSha256Verify(key, msg, sig) { return new Promise(function(resolve) { - var hmac = nodeCrypto.createHmac('sha256', Buffer.from(key)); + var hmac = nodeCrypto.createHmac("sha256", Buffer.from(key)); hmac.update(msg); var expectedSig = hmac.digest(); resolve(equalConstTime(expectedSig, sig)); @@ -113,12 +116,12 @@ function hmacSha256Verify(key, msg, sig) { } /** - * Generate a new valid private key. Will use the window.crypto or window.msCrypto as source - * depending on your browser. - * @return {Buffer} A 32-byte private key. - * @function - */ -exports.generatePrivate = function () { + * Generate a new valid private key. Will use the window.crypto or window.msCrypto as source + * depending on your browser. + * @return {Buffer} A 32-byte private key. + * @function + */ +exports.generatePrivate = function() { var privateKey = randomBytes(32); while (!isValidPrivateKey(privateKey)) { privateKey = randomBytes(32); @@ -126,24 +129,27 @@ exports.generatePrivate = function () { return privateKey; }; -var getPublic = exports.getPublic = function(privateKey) { +var getPublic = (exports.getPublic = function(privateKey) { // This function has sync API so we throw an error immediately. assert(privateKey.length === 32, "Bad private key"); assert(isValidPrivateKey(privateKey), "Bad private key"); // XXX(Kagami): `elliptic.utils.encode` returns array for every // encoding except `hex`. return Buffer.from(ec.keyFromPrivate(privateKey).getPublic("arr")); -}; +}); /** * Get compressed version of public key. */ -var getPublicCompressed = exports.getPublicCompressed = function(privateKey) { // jshint ignore:line +exports.getPublicCompressed = function(privateKey) { + // jshint ignore:line assert(privateKey.length === 32, "Bad private key"); assert(isValidPrivateKey(privateKey), "Bad private key"); // See https://github.com/wanderer/secp256k1-node/issues/46 let compressed = true; - return Buffer.from(ec.keyFromPrivate(privateKey).getPublic(compressed, "arr")); + return Buffer.from( + ec.keyFromPrivate(privateKey).getPublic(compressed, "arr") + ); }; // NOTE(Kagami): We don't use promise shim in Browser implementation @@ -157,19 +163,20 @@ exports.sign = function(privateKey, msg) { assert(isValidPrivateKey(privateKey), "Bad private key"); assert(msg.length > 0, "Message should not be empty"); assert(msg.length <= 32, "Message is too long"); - resolve(Buffer.from(ec.sign(msg, privateKey, {canonical: true}).toDER())); + resolve(Buffer.from(ec.sign(msg, privateKey, { canonical: true }).toDER())); }); }; exports.verify = function(publicKey, msg, sig) { return new Promise(function(resolve, reject) { - assert(publicKey.length === 65 || publicKey.length === 33, "Bad public key"); - if (publicKey.length === 65) - { + assert( + publicKey.length === 65 || publicKey.length === 33, + "Bad public key" + ); + if (publicKey.length === 65) { assert(publicKey[0] === 4, "Bad public key"); } - if (publicKey.length === 33) - { + if (publicKey.length === 33) { assert(publicKey[0] === 2 || publicKey[0] === 3, "Bad public key"); } assert(msg.length > 0, "Message should not be empty"); @@ -182,27 +189,28 @@ exports.verify = function(publicKey, msg, sig) { }); }; -var derive = exports.derive = function(privateKeyA, publicKeyB) { +var derive = (exports.derive = function(privateKeyA, publicKeyB) { return new Promise(function(resolve) { assert(Buffer.isBuffer(privateKeyA), "Bad private key"); assert(Buffer.isBuffer(publicKeyB), "Bad public key"); assert(privateKeyA.length === 32, "Bad private key"); assert(isValidPrivateKey(privateKeyA), "Bad private key"); - assert(publicKeyB.length === 65 || publicKeyB.length === 33, "Bad public key"); - if (publicKeyB.length === 65) - { + assert( + publicKeyB.length === 65 || publicKeyB.length === 33, + "Bad public key" + ); + if (publicKeyB.length === 65) { assert(publicKeyB[0] === 4, "Bad public key"); } - if (publicKeyB.length === 33) - { + if (publicKeyB.length === 33) { assert(publicKeyB[0] === 2 || publicKeyB[0] === 3, "Bad public key"); } var keyA = ec.keyFromPrivate(privateKeyA); var keyB = ec.keyFromPublic(publicKeyB); - var Px = keyA.derive(keyB.getPublic()); // BN instance + var Px = keyA.derive(keyB.getPublic()); // BN instance resolve(Buffer.from(Px.toArray())); }); -}; +}); exports.encrypt = function(publicKeyTo, msg, opts) { opts = opts || {}; @@ -211,52 +219,58 @@ exports.encrypt = function(publicKeyTo, msg, opts) { return new Promise(function(resolve) { var ephemPrivateKey = opts.ephemPrivateKey || randomBytes(32); // There is a very unlikely possibility that it is not a valid key - while(!isValidPrivateKey(ephemPrivateKey)) - { + while (!isValidPrivateKey(ephemPrivateKey)) { ephemPrivateKey = opts.ephemPrivateKey || randomBytes(32); } ephemPublicKey = getPublic(ephemPrivateKey); resolve(derive(ephemPrivateKey, publicKeyTo)); - }).then(function(Px) { - return sha512(Px); - }).then(function(hash) { - iv = opts.iv || randomBytes(16); - var encryptionKey = hash.slice(0, 32); - macKey = hash.slice(32); - return aesCbcEncrypt(iv, encryptionKey, msg); - }).then(function(data) { - ciphertext = data; - var dataToMac = Buffer.concat([iv, ephemPublicKey, ciphertext]); - return hmacSha256Sign(macKey, dataToMac); - }).then(function(mac) { - return { - iv: iv, - ephemPublicKey: ephemPublicKey, - ciphertext: ciphertext, - mac: mac, - }; - }); + }) + .then(function(Px) { + return sha512(Px); + }) + .then(function(hash) { + iv = opts.iv || randomBytes(16); + var encryptionKey = hash.slice(0, 32); + macKey = hash.slice(32); + return aesCbcEncrypt(iv, encryptionKey, msg); + }) + .then(function(data) { + ciphertext = data; + var dataToMac = Buffer.concat([iv, ephemPublicKey, ciphertext]); + return hmacSha256Sign(macKey, dataToMac); + }) + .then(function(mac) { + return { + iv: iv, + ephemPublicKey: ephemPublicKey, + ciphertext: ciphertext, + mac: mac + }; + }); }; exports.decrypt = function(privateKey, opts) { // Tmp variable to save context from flat promises; var encryptionKey; - return derive(privateKey, opts.ephemPublicKey).then(function(Px) { - return sha512(Px); - }).then(function(hash) { - encryptionKey = hash.slice(0, 32); - var macKey = hash.slice(32); - var dataToMac = Buffer.concat([ - opts.iv, - opts.ephemPublicKey, - opts.ciphertext - ]); - return hmacSha256Verify(macKey, dataToMac, opts.mac); - }).then(function(macGood) { - assert(macGood, "Bad MAC"); - return aesCbcDecrypt(opts.iv, encryptionKey, opts.ciphertext); - }).then(function(msg) { - return Buffer.from(new Uint8Array(msg)); - }); + return derive(privateKey, opts.ephemPublicKey) + .then(function(Px) { + return sha512(Px); + }) + .then(function(hash) { + encryptionKey = hash.slice(0, 32); + var macKey = hash.slice(32); + var dataToMac = Buffer.concat([ + opts.iv, + opts.ephemPublicKey, + opts.ciphertext + ]); + return hmacSha256Verify(macKey, dataToMac, opts.mac); + }) + .then(function(macGood) { + assert(macGood, "Bad MAC"); + return aesCbcDecrypt(opts.iv, encryptionKey, opts.ciphertext); + }) + .then(function(msg) { + return Buffer.from(new Uint8Array(msg)); + }); }; -