From 59bac969275c0916846c68e654d63e8f133e41d5 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 00:03:25 +0200 Subject: [PATCH 1/8] Removing secp256k1 and uses ethereum-cryptography instead. --- index.js | 2 +- package.json | 3 +- secp256k1-adapter.js | 231 +++++++ secp256k1-lib/der.js | 647 ++++++++++++++++++++ secp256k1-lib/index.js | 70 +++ test/index.js | 1327 ++++++++++++++++++++++++++++++++++++++++ test/util.js | 79 +++ 7 files changed, 2357 insertions(+), 2 deletions(-) create mode 100644 secp256k1-adapter.js create mode 100644 secp256k1-lib/der.js create mode 100644 secp256k1-lib/index.js create mode 100644 test/util.js diff --git a/index.js b/index.js index 47e745ef..c621bee3 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ const SHA3 = require('keccakjs') -const secp256k1 = require('secp256k1') +const secp256k1 = require('./secp256k1-adapter') const assert = require('assert') const rlp = require('rlp') const BN = require('bn.js') diff --git a/package.json b/package.json index 37bda9a1..9fd625c5 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "create-hash": "^1.1.2", "keccakjs": "^0.2.0", "rlp": "^2.0.0", - "secp256k1": "^3.0.1" + "ethereum-cryptography": "^0.1.3", + "elliptic": "^6.5.2" }, "devDependencies": { "browserify": "^13.0.0", diff --git a/secp256k1-adapter.js b/secp256k1-adapter.js new file mode 100644 index 00000000..62a772a3 --- /dev/null +++ b/secp256k1-adapter.js @@ -0,0 +1,231 @@ +const secp256k1 = require('ethereum-cryptography/secp256k1') + +const secp256k1v3 = require('./secp256k1-lib/index') +const der = require('./secp256k1-lib/der') + +const privateKeyVerify = function (privateKey) { + if (privateKey.length !== 32) { + return false + } + + return secp256k1.privateKeyVerify(Uint8Array.from(privateKey)) +} + +const privateKeyExport = function (privateKey, compressed) { + if (privateKey.length !== 32) { + throw new RangeError('private key length is invalid') + } + + const publicKey = secp256k1v3.privateKeyExport(privateKey, compressed) + + return der.privateKeyExport(privateKey, publicKey, compressed) +} + +const privateKeyImport = function (privateKey) { + privateKey = der.privateKeyImport(privateKey) + if (privateKey !== null && privateKey.length === 32 && privateKeyVerify(privateKey)) { + return privateKey + } + + throw new Error("couldn't import from DER format") +} + +const privateKeyNegate = function (privateKey) { + return Buffer.from(secp256k1.privateKeyNegate(Uint8Array.from(privateKey))) +} + +const privateKeyModInverse = function (privateKey) { + if (privateKey.length !== 32) { + throw new Error('private key length is invalid') + } + + return Buffer.from(secp256k1v3.privateKeyModInverse(Uint8Array.from(privateKey))) +} + +const privateKeyTweakAdd = function (privateKey, tweak) { + return Buffer.from(secp256k1.privateKeyTweakAdd(Uint8Array.from(privateKey), tweak)) +} + +const privateKeyTweakMul = function (privateKey, tweak) { + return Buffer.from( + secp256k1.privateKeyTweakMul(Uint8Array.from(privateKey), Uint8Array.from(tweak)) + ) +} + +const publicKeyCreate = function (privateKey, compressed) { + return Buffer.from(secp256k1.publicKeyCreate(Uint8Array.from(privateKey), compressed)) +} + +const publicKeyConvert = function (publicKey, compressed) { + return Buffer.from(secp256k1.publicKeyConvert(Uint8Array.from(publicKey), compressed)) +} + +const publicKeyVerify = function (publicKey) { + if (publicKey.length !== 33 && publicKey.length !== 65) { + return false + } + + return secp256k1.publicKeyVerify(Uint8Array.from(publicKey)) +} + +const publicKeyTweakAdd = function (publicKey, tweak, compressed) { + return Buffer.from( + secp256k1.publicKeyTweakAdd(Uint8Array.from(publicKey), Uint8Array.from(tweak), compressed) + ) +} + +const publicKeyTweakMul = function (publicKey, tweak, compressed) { + return Buffer.from( + secp256k1.publicKeyTweakMul(Uint8Array.from(publicKey), Uint8Array.from(tweak), compressed) + ) +} + +const publicKeyCombine = function (publicKeys, compressed) { + const keys = [] + publicKeys.forEach((publicKey) => { + keys.push(Uint8Array.from(publicKey)) + }) + + return Buffer.from(secp256k1.publicKeyCombine(keys, compressed)) +} + +const signatureNormalize = function (signature) { + return Buffer.from(secp256k1.signatureNormalize(Uint8Array.from(signature))) +} + +const signatureExport = function (signature) { + return Buffer.from(secp256k1.signatureExport(Uint8Array.from(signature))) +} + +const signatureImport = function (signature) { + return Buffer.from(secp256k1.signatureImport(Uint8Array.from(signature))) +} + +const signatureImportLax = function (signature) { + if (signature.length === 0) { + throw new RangeError('signature length is invalid') + } + + const sigObj = der.signatureImportLax(signature) + if (sigObj === null) { + throw new Error("couldn't parse DER signature") + } + + return secp256k1v3.signatureImport(sigObj) +} + +const sign = function (message, privateKey, options) { + if (options === null) { + throw new TypeError('options should be an Object') + } + + let signOptions + + if (options) { + signOptions = {} + + if (options.data === null) { + throw new TypeError('options.data should be a Buffer') + } + + if (options.data) { + if (options.data.length !== 32) { + throw new RangeError('options.data length is invalid') + } + + signOptions.data = new Uint8Array(options.data) + } + + if (options.noncefn === null) { + throw new TypeError('options.noncefn should be a Function') + } + + if (options.noncefn) { + signOptions.noncefn = (message, privateKey, algo, data, attempt) => { + const bufferAlgo = algo != null ? Buffer.from(algo) : null + const bufferData = data != null ? Buffer.from(data) : null + + let buffer = Buffer.from('') + + if (options.noncefn) { + buffer = options.noncefn(Buffer.from(message), + Buffer.from(privateKey), + bufferAlgo, + bufferData, + attempt + ) + } + + return new Uint8Array(buffer) + } + } + } + + const sig = secp256k1.ecdsaSign( + Uint8Array.from(message), + Uint8Array.from(privateKey), + signOptions + ) + + return { + signature: Buffer.from(sig.signature), + recovery: sig.recid + } +} + +const verify = function (message, signature, publicKey) { + return secp256k1.ecdsaVerify(Uint8Array.from(signature), Uint8Array.from(message), publicKey) +} + +const recover = function (message, signature, recid, compressed) { + return Buffer.from( + secp256k1.ecdsaRecover(Uint8Array.from(signature), recid, Uint8Array.from(message), compressed) + ) +} + +const ecdh = function (publicKey, privateKey) { + return Buffer.from(secp256k1.ecdh(Uint8Array.from(publicKey), Uint8Array.from(privateKey), {})) +} + +const ecdhUnsafe = function (publicKey, privateKey, compressed) { + if (publicKey.length !== 33 && publicKey.length !== 65) { + throw new RangeError('public key length is invalid') + } + + if (privateKey.length !== 32) { + throw new RangeError('private key length is invalid') + } + + return Buffer.from( + secp256k1v3.ecdhUnsafe(Uint8Array.from(publicKey), Uint8Array.from(privateKey), compressed) + ) +} + +module.exports = { + privateKeyVerify: privateKeyVerify, + privateKeyExport: privateKeyExport, + privateKeyImport: privateKeyImport, + privateKeyNegate: privateKeyNegate, + privateKeyModInverse: privateKeyModInverse, + privateKeyTweakAdd: privateKeyTweakAdd, + privateKeyTweakMul: privateKeyTweakMul, + + publicKeyCreate: publicKeyCreate, + publicKeyConvert: publicKeyConvert, + publicKeyVerify: publicKeyVerify, + publicKeyTweakAdd: publicKeyTweakAdd, + publicKeyTweakMul: publicKeyTweakMul, + publicKeyCombine: publicKeyCombine, + + signatureNormalize: signatureNormalize, + signatureExport: signatureExport, + signatureImport: signatureImport, + signatureImportLax: signatureImportLax, + + sign: sign, + verify: verify, + recover: recover, + + ecdh: ecdh, + ecdhUnsafe: ecdhUnsafe +} diff --git a/secp256k1-lib/der.js b/secp256k1-lib/der.js new file mode 100644 index 00000000..83091dac --- /dev/null +++ b/secp256k1-lib/der.js @@ -0,0 +1,647 @@ +// This file is imported from secp256k1 v3 +// https://github.com/cryptocoinjs/secp256k1-node/blob/master/LICENSE + +const EC_PRIVKEY_EXPORT_DER_COMPRESSED = Buffer.from([ + // begin + 0x30, + 0x81, + 0xd3, + 0x02, + 0x01, + 0x01, + 0x04, + 0x20, + // private key + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + // middle + 0xa0, + 0x81, + 0x85, + 0x30, + 0x81, + 0x82, + 0x02, + 0x01, + 0x01, + 0x30, + 0x2c, + 0x06, + 0x07, + 0x2a, + 0x86, + 0x48, + 0xce, + 0x3d, + 0x01, + 0x01, + 0x02, + 0x21, + 0x00, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xfe, + 0xff, + 0xff, + 0xfc, + 0x2f, + 0x30, + 0x06, + 0x04, + 0x01, + 0x00, + 0x04, + 0x01, + 0x07, + 0x04, + 0x21, + 0x02, + 0x79, + 0xbe, + 0x66, + 0x7e, + 0xf9, + 0xdc, + 0xbb, + 0xac, + 0x55, + 0xa0, + 0x62, + 0x95, + 0xce, + 0x87, + 0x0b, + 0x07, + 0x02, + 0x9b, + 0xfc, + 0xdb, + 0x2d, + 0xce, + 0x28, + 0xd9, + 0x59, + 0xf2, + 0x81, + 0x5b, + 0x16, + 0xf8, + 0x17, + 0x98, + 0x02, + 0x21, + 0x00, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xfe, + 0xba, + 0xae, + 0xdc, + 0xe6, + 0xaf, + 0x48, + 0xa0, + 0x3b, + 0xbf, + 0xd2, + 0x5e, + 0x8c, + 0xd0, + 0x36, + 0x41, + 0x41, + 0x02, + 0x01, + 0x01, + 0xa1, + 0x24, + 0x03, + 0x22, + 0x00, + // public key + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 +]) + +const EC_PRIVKEY_EXPORT_DER_UNCOMPRESSED = Buffer.from([ + // begin + 0x30, + 0x82, + 0x01, + 0x13, + 0x02, + 0x01, + 0x01, + 0x04, + 0x20, + // private key + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + // middle + 0xa0, + 0x81, + 0xa5, + 0x30, + 0x81, + 0xa2, + 0x02, + 0x01, + 0x01, + 0x30, + 0x2c, + 0x06, + 0x07, + 0x2a, + 0x86, + 0x48, + 0xce, + 0x3d, + 0x01, + 0x01, + 0x02, + 0x21, + 0x00, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xfe, + 0xff, + 0xff, + 0xfc, + 0x2f, + 0x30, + 0x06, + 0x04, + 0x01, + 0x00, + 0x04, + 0x01, + 0x07, + 0x04, + 0x41, + 0x04, + 0x79, + 0xbe, + 0x66, + 0x7e, + 0xf9, + 0xdc, + 0xbb, + 0xac, + 0x55, + 0xa0, + 0x62, + 0x95, + 0xce, + 0x87, + 0x0b, + 0x07, + 0x02, + 0x9b, + 0xfc, + 0xdb, + 0x2d, + 0xce, + 0x28, + 0xd9, + 0x59, + 0xf2, + 0x81, + 0x5b, + 0x16, + 0xf8, + 0x17, + 0x98, + 0x48, + 0x3a, + 0xda, + 0x77, + 0x26, + 0xa3, + 0xc4, + 0x65, + 0x5d, + 0xa4, + 0xfb, + 0xfc, + 0x0e, + 0x11, + 0x08, + 0xa8, + 0xfd, + 0x17, + 0xb4, + 0x48, + 0xa6, + 0x85, + 0x54, + 0x19, + 0x9c, + 0x47, + 0xd0, + 0x8f, + 0xfb, + 0x10, + 0xd4, + 0xb8, + 0x02, + 0x21, + 0x00, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xfe, + 0xba, + 0xae, + 0xdc, + 0xe6, + 0xaf, + 0x48, + 0xa0, + 0x3b, + 0xbf, + 0xd2, + 0x5e, + 0x8c, + 0xd0, + 0x36, + 0x41, + 0x41, + 0x02, + 0x01, + 0x01, + 0xa1, + 0x44, + 0x03, + 0x42, + 0x00, + // public key + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 +]) + +exports.privateKeyExport = function (privateKey, publicKey, compressed) { + const result = Buffer.from( + compressed ? EC_PRIVKEY_EXPORT_DER_COMPRESSED : EC_PRIVKEY_EXPORT_DER_UNCOMPRESSED + ) + privateKey.copy(result, compressed ? 8 : 9) + publicKey.copy(result, compressed ? 181 : 214) + return result +} + +exports.privateKeyImport = function (privateKey) { + const length = privateKey.length + + // sequence header + let index = 0 + if (length < index + 1 || privateKey[index] !== 0x30) return null + index += 1 + + // sequence length constructor + if (length < index + 1 || !(privateKey[index] & 0x80)) return null + + const lenb = privateKey[index] & 0x7f + index += 1 + if (lenb < 1 || lenb > 2) return null + if (length < index + lenb) return null + + // sequence length + const len = privateKey[index + lenb - 1] | (lenb > 1 ? privateKey[index + lenb - 2] << 8 : 0) + index += lenb + if (length < index + len) return null + + // sequence element 0: version number (=1) + if ( + length < index + 3 || + privateKey[index] !== 0x02 || + privateKey[index + 1] !== 0x01 || + privateKey[index + 2] !== 0x01 + ) { + return null + } + index += 3 + + // sequence element 1: octet string, up to 32 bytes + if ( + length < index + 2 || + privateKey[index] !== 0x04 || + privateKey[index + 1] > 0x20 || + length < index + 2 + privateKey[index + 1] + ) { + return null + } + + return privateKey.slice(index + 2, index + 2 + privateKey[index + 1]) +} + +exports.signatureImportLax = function (signature) { + const r = Buffer.alloc(32, 0) + const s = Buffer.alloc(32, 0) + + const length = signature.length + let index = 0 + + // sequence tag byte + if (signature[index++] !== 0x30) { + return null + } + + // sequence length byte + let lenbyte = signature[index++] + if (lenbyte & 0x80) { + index += lenbyte - 0x80 + if (index > length) { + return null + } + } + + // sequence tag byte for r + if (signature[index++] !== 0x02) { + return null + } + + // length for r + let rlen = signature[index++] + if (rlen & 0x80) { + lenbyte = rlen - 0x80 + if (index + lenbyte > length) { + return null + } + for (; lenbyte > 0 && signature[index] === 0x00; index += 1, lenbyte -= 1) ; + for (rlen = 0; lenbyte > 0; index += 1, lenbyte -= 1) rlen = (rlen << 8) + signature[index] + } + if (rlen > length - index) { + return null + } + let rindex = index + index += rlen + + // sequence tag byte for s + if (signature[index++] !== 0x02) { + return null + } + + // length for s + let slen = signature[index++] + if (slen & 0x80) { + lenbyte = slen - 0x80 + if (index + lenbyte > length) { + return null + } + for (; lenbyte > 0 && signature[index] === 0x00; index += 1, lenbyte -= 1) ; + for (slen = 0; lenbyte > 0; index += 1, lenbyte -= 1) slen = (slen << 8) + signature[index] + } + if (slen > length - index) { + return null + } + let sindex = index + index += slen + + // ignore leading zeros in r + for (; rlen > 0 && signature[rindex] === 0x00; rlen -= 1, rindex += 1) ; + // copy r value + if (rlen > 32) { + return null + } + const rvalue = signature.slice(rindex, rindex + rlen) + rvalue.copy(r, 32 - rvalue.length) + + // ignore leading zeros in s + for (; slen > 0 && signature[sindex] === 0x00; slen -= 1, sindex += 1) ; + // copy s value + if (slen > 32) { + return null + } + const svalue = signature.slice(sindex, sindex + slen) + svalue.copy(s, 32 - svalue.length) + + return {r: r, s: s} +} diff --git a/secp256k1-lib/index.js b/secp256k1-lib/index.js new file mode 100644 index 00000000..2f4f74b7 --- /dev/null +++ b/secp256k1-lib/index.js @@ -0,0 +1,70 @@ +// This file is imported from secp256k1 v3 +// https://github.com/cryptocoinjs/secp256k1-node/blob/master/LICENSE + +const BN = require('bn.js') +const EC = require('elliptic').ec + +const ec = new EC('secp256k1') +const ecparams = ec.curve + +exports.privateKeyExport = function (privateKey, compressed) { + const d = new BN(privateKey) + if (d.ucmp(ecparams.n) >= 0) { + throw new Error('couldn\'t export to DER format') + } + + const point = ec.g.mul(d) + return toPublicKey(point.getX(), point.getY(), compressed) +} + +exports.privateKeyModInverse = function (privateKey) { + const bn = new BN(privateKey) + if (bn.ucmp(ecparams.n) >= 0 || bn.isZero()) { + throw new Error('private key range is invalid') + } + + return bn.invm(ecparams.n).toArrayLike(Buffer, 'be', 32) +} + +exports.signatureImport = function (sigObj) { + let r = new BN(sigObj.r) + if (r.ucmp(ecparams.n) >= 0) { + r = new BN(0) + } + + let s = new BN(sigObj.s) + if (s.ucmp(ecparams.n) >= 0) { + s = new BN(0) + } + + return Buffer.concat([r.toArrayLike(Buffer, 'be', 32), s.toArrayLike(Buffer, 'be', 32)]) +} + +exports.ecdhUnsafe = function (publicKey, privateKey, compressed) { + const point = ec.keyFromPublic(publicKey) + + const scalar = new BN(privateKey) + if (scalar.ucmp(ecparams.n) >= 0 || scalar.isZero()) { + throw new Error('scalar was invalid (zero or overflow)') + } + + const shared = point.pub.mul(scalar) + return toPublicKey(shared.getX(), shared.getY(), compressed) +} + +const toPublicKey = function (x, y, compressed) { + let publicKey + + if (compressed) { + publicKey = Buffer.alloc(33) + publicKey[0] = y.isOdd() ? 0x03 : 0x02 + x.toArrayLike(Buffer, 'be', 32).copy(publicKey, 1) + } else { + publicKey = Buffer.alloc(65) + publicKey[0] = 0x04 + x.toArrayLike(Buffer, 'be', 32).copy(publicKey, 1) + y.toArrayLike(Buffer, 'be', 32).copy(publicKey, 33) + } + + return publicKey +} diff --git a/test/index.js b/test/index.js index b9811590..bcda2328 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,8 @@ var assert = require('assert') var ethUtils = require('../index.js') var BN = require('bn.js') +const util = require('./util') +const getRandomBytes = require('crypto').randomBytes describe('zeros function', function () { it('should produce lots of 0s', function () { @@ -536,3 +538,1328 @@ describe('message sig', function () { }) }) }) + +describe('privateKeyVerify', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyVerify(null) + }) + }) + + it('invalid length', function () { + assert.equal(ethUtils.secp256k1.privateKeyVerify(util.getPrivateKey().slice(1)), false) + }) + + it('zero key', function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) + }) + + it('equal to N', function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), true) + }) + }) +}) + +describe('privateKeyExport', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyExport(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyExport(util.getPrivateKey().slice(1)) + }) + }) + + it('private key is invalid', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyExport(privateKey) + }) + }) +}) + +describe('privateKeyImport', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyImport(null) + }) + }) + + it('invalid format', function () { + const buffers = [ + Buffer.from([0x00]), + Buffer.from([0x30, 0x7b]), + Buffer.from([0x30, 0x87]), + Buffer.from([0x30, 0x81]), + Buffer.from([0x30, 0x82, 0x00, 0xff]), + Buffer.from([0x30, 0x82, 0x00, 0x00]), + Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]) + ] + + buffers.forEach((buffer) => { + assert.throws(function () { + ethUtils.secp256k1.privateKeyImport(buffer) + }) + }) + }) +}) + +describe('privateKeyExport/privateKeyImport', function () { + it('export/import', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const der1 = ethUtils.secp256k1.privateKeyExport(privateKey, true) + const privateKey1 = ethUtils.secp256k1.privateKeyImport(der1) + assert.deepEqual(privateKey1, privateKey) + + const der2 = ethUtils.secp256k1.privateKeyExport(privateKey, false) + const privateKey2 = ethUtils.secp256k1.privateKeyImport(der2) + assert.deepEqual(privateKey2, privateKey) + + const der3 = ethUtils.secp256k1.privateKeyExport(privateKey) + const privateKey3 = ethUtils.secp256k1.privateKeyImport(der3) + assert.deepEqual(privateKey3, privateKey) + }) + }) +}) + +describe('privateKeyNegate', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyNegate(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyNegate(util.getPrivateKey().slice(1)) + }) + }) + + it('private key is 0', function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + + const expected = Buffer.alloc(32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('private key equal to N', function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + + const expected = Buffer.alloc(32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('private key overflow', function () { + const privateKey = util.ec.curve.n.addn(10).toArrayLike(Buffer, 'be', 32) + + const expected = util.ec.curve.n.subn(10).toArrayLike(Buffer, 'be', 32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + const expected = util.ec.curve.n.sub(new BN(privateKey)) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + }) + }) +}) + +describe('privateKeyModInverse', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyModInverse(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('private key is 0', function () { + assert.throws(function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('private key equal to N', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + const expected = new BN(privateKey).invm(new BN(util.ec.curve.n.toArrayLike(Buffer, 'be', 32))) + const result = ethUtils.secp256k1.privateKeyModInverse(privateKey) + + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + }) + }) +}) + +describe('privateKeyTweakAdd', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.privateKeyTweakAdd(null, tweak) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('result is zero: (N - 1) + 1', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.sub(util.BN_ONE).toArrayLike(Buffer, 'be', 32) + const tweak = util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + const tweak = util.getTweak() + + privateKeys.forEach((privateKey) => { + const expected = new BN(privateKey).add(new BN(tweak)).mod(util.ec.curve.n) + if (expected.cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + } else { + const result = ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + } + }) + }) +}) + +describe('privateKeyTweakMul', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(null, tweak) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const tweak = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(privateKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak equal N', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak is 0', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + const tweak = util.getTweak() + + privateKeys.forEach((privateKey) => { + if (new BN(tweak).cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + } else { + const expected = new BN(privateKey).mul(new BN(tweak)).mod(util.ec.curve.n) + const result = ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + } + }) + }) +}) + +describe('publicKeyCreate', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCreate(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('overflow', function () { + assert.throws(function () { + const privateKey = util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('equal zero', function () { + assert.throws(function () { + const privateKey = new BN(0).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.publicKeyCreate(privateKey, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + const compressed = ethUtils.secp256k1.publicKeyCreate(privateKey, true) + assert.deepEqual(compressed, expected.compressed) + + const uncompressed = ethUtils.secp256k1.publicKeyCreate(privateKey, false) + assert.deepEqual(uncompressed, expected.uncompressed) + }) + }) +}) + +describe('publicKeyConvert', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyConvert(null) + }) + }) + + it('length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.publicKeyConvert(publicKey) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyConvert(publicKey, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + const compressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, true) + assert.deepEqual(compressed, expected.compressed) + + const uncompressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, false) + assert.deepEqual(uncompressed, expected.uncompressed) + }) + }) +}) + +describe('publicKeyVerify', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyVerify(null) + }) + }) + + it('invalid length', function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('invalid first byte', function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('x overflow (first byte is 0x03)', function () { + const publicKey = Buffer.concat([ + Buffer.from([0x03]), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('x overflow', function () { + const publicKey = Buffer.concat([ + Buffer.from([0x04]), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y overflow', function () { + const publicKey = Buffer.concat([ + Buffer.from([0x04]), + Buffer.alloc(32), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y is even, first byte is 0x07', function () { + const publicKey = Buffer.concat([ + Buffer.from([0x07]), + Buffer.alloc(32), + util.ec.curve.p.subn(1).toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y**2 !== x*x*x + 7', function () { + const publicKey = Buffer.concat([Buffer.from([0x04]), util.getTweak(), util.getTweak()]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) + assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) + }) + }) +}) + +describe('publicKeyTweakAdd', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(null, tweak) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, null) + }) + }) + + it('tweak length length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak produce infinity point', function () { + // G * 1 - G = 0 + assert.throws(function () { + const publicKey = Buffer.from(util.ec.g.encode(null, true)) + publicKey[0] = publicKey[0] ^ 0x01 // change sign of G + const tweak = new BN(1).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const tweak = util.getTweak() + const publicPoint = util.ec.g.mul(new BN(privateKey)) + const publicKey = Buffer.from(publicPoint.encode(null, true)) + const expected = util.ec.g.mul(new BN(tweak)).add(publicPoint) + + const compressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + }) + }) +}) + +describe('publicKeyTweakMul', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(null, tweak) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyTweakMul(publicKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak is zero', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = new BN(0).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const tweak = util.getTweak() + const publicPoint = util.ec.g.mul(new BN(privateKey)) + const publicKey = Buffer.from(publicPoint.encode(null, true)) + + if (new BN(tweak).cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + } else { + const expected = publicPoint.mul(tweak) + + const compressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + } + }) + }) +}) + +describe('publicKeyCombine', function () { + it('public keys should be an Array', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine(null) + }) + }) + + it('public keys should have length greater that zero', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine([]) + }) + }) + + it('public key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine([null]) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.publicKeyCombine([publicKey]) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + ethUtils.secp256k1.publicKeyCombine([publicKey]) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyCombine([publicKey], null) + }) + }) + + it('P + (-P) = 0', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey).compressed + const publicKey2 = Buffer.from(publicKey1) + publicKey2[0] = publicKey2[0] ^ 0x01 + ethUtils.secp256k1.publicKeyCombine([publicKey1, publicKey2], true) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const cnt = 1 + Math.floor(Math.random() * 3) // 1 <= cnt <= 3 + const privateKeys = [] + while (privateKeys.length < cnt) privateKeys.push(util.getPrivateKey()) + const publicKeys = privateKeys.map(function (privateKey) { + return util.getPublicKey(privateKey).compressed + }) + + let expected = util.ec.g.mul(new BN(privateKeys[0])) + for (let i = 1; i < privateKeys.length; ++i) { + const publicPoint = util.ec.g.mul(new BN(privateKeys[i])) + expected = expected.add(publicPoint) + } + + const compressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + }) + }) +}) + +describe('signatureNormalize', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureNormalize(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.signatureNormalize(signature) + }) + }) + + it('parse fail (r equal N)', function () { + assert.throws(function () { + const signature = Buffer.concat([ + util.ec.curve.n.toArrayLike(Buffer, 'be', 32), + util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ]) + ethUtils.secp256k1.signatureNormalize(signature) + }) + }) + + it('normalize return same signature (s equal n/2)', function () { + const signature = Buffer.concat([ + util.BN_ONE.toArrayLike(Buffer, 'be', 32), + util.ec.nh.toArrayLike(Buffer, 'be', 32) + ]) + const result = ethUtils.secp256k1.signatureNormalize(signature) + assert.deepEqual(result, signature) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const message = util.getMessage() + + const sigObj = util.sign(message, privateKey) + const result = ethUtils.secp256k1.signatureNormalize(sigObj.signature) + assert.deepEqual(result, sigObj.signatureLowS) + }) + }) +}) + +describe('signatureExport', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureExport(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.signatureExport(signature) + }) + }) + + it('parse fail (r equal N)', function () { + assert.throws(function () { + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ]) + ethUtils.secp256k1.signatureExport(signature) + }) + }) +}) + +describe('signatureImport', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImport(null) + }) + }) + + it('parse fail', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImport(Buffer.alloc(1)) + }) + }) + + it('parse not bip66 signature', function () { + const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') + assert.throws(function () { + ethUtils.secp256k1.signatureImport(signature) + }) + }) +}) + +describe('signatureImportLax', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImportLax(null) + }) + }) + + it('parse fail', function () { + const buffers = [ + Buffer.alloc(0), + Buffer.alloc(1), + Buffer.from([0x30, 0x7b]), + Buffer.from([0x30, 0x87]), + Buffer.from([0x30, 0x80, 0x02, 0x80]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01]), + Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81, 0x01]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x21, 0x01, 0x00, 0x02, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x05, 0x01, 0x00, 0x02, 0x21, 0x02, 0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + ] + + buffers.forEach((buffer) => { + assert.throws(function () { + ethUtils.secp256k1.signatureImportLax(buffer) + }) + }) + }) + + it('parse not bip66 signature', function () { + const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') + assert.doesNotThrow(function () { + ethUtils.secp256k1.signatureImportLax(signature) + }) + }) +}) + +describe('signatureExport/signatureImport', function () { + it('signature should be a Buffer', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const message = util.getMessage() + + const signature = util.sign(message, privateKey).signatureLowS + + const der = ethUtils.secp256k1.signatureExport(signature) + assert.deepEqual(ethUtils.secp256k1.signatureImport(der), signature) + assert.deepEqual(ethUtils.secp256k1.signatureImportLax(der), signature) + }) + }) +}) + +describe('ecdh', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = null + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('invalid public key', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x00 + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key should be a Buffer', function () { + assert.throws(function () { + const privateKey = null + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key equal zero', function () { + assert.throws(function () { + const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key equal N', function () { + assert.throws(function () { + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey1, i) => { + const privateKey2 = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey1).compressed + const publicKey2 = util.getPublicKey(privateKey2).compressed + + const shared1 = ethUtils.secp256k1.ecdh(publicKey1, privateKey2) + const shared2 = ethUtils.secp256k1.ecdh(publicKey2, privateKey1) + assert.deepEqual(shared1, shared2) + }) + }) +}) + +describe('ecdhUnsafe', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = null + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('invalid public key', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x00 + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key should be a Buffer', function () { + assert.throws(function () { + const privateKey = null + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key equal zero', function () { + assert.throws(function () { + const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key equal N', function () { + assert.throws(function () { + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey1, i) => { + const privateKey2 = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey1).compressed + const publicKey2 = util.getPublicKey(privateKey2).compressed + + const shared1 = ethUtils.secp256k1.ecdhUnsafe(publicKey1, privateKey2) + const shared2 = ethUtils.secp256k1.ecdhUnsafe(publicKey2, privateKey1) + assert.deepEqual(shared1, shared2) + }) + }) +}) + +describe('sign', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(null, privateKey) + }) + }) + + it('message invalid length', function () { + assert.throws(function () { + const message = util.getMessage().slice(1) + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('private key should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + ethUtils.secp256k1.sign(message, null) + }) + }) + + it('private key invalid length', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('private key is invalid', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('options should be an Object', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, null) + }) + }) + + it('options.data should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, {data: null}) + }) + }) + + it('options.data length is invalid', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const data = getRandomBytes(31) + ethUtils.secp256k1.sign(message, privateKey, {data: data}) + }) + }) + + it('options.noncefn should be a Function', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, {noncefn: null}) + }) + }) + + it('noncefn return not a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const noncefn = function () { + return null + } + ethUtils.secp256k1.sign(message, privateKey, {noncefn: noncefn}) + }) + }) + + it('noncefn return Buffer with invalid length', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const noncefn = function () { + return getRandomBytes(31) + } + ethUtils.secp256k1.sign(message, privateKey, {noncefn: noncefn}) + }) + }) + + it('check options.noncefn arguments', function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const data = getRandomBytes(32) + const noncefn = function (message2, privateKey2, algo, data2, attempt) { + assert.deepEqual(message2, message) + assert.deepEqual(privateKey2, privateKey) + assert.deepEqual(algo, null) + assert.deepEqual(data2, data) + assert.deepEqual(attempt, 0) + return getRandomBytes(32) + } + ethUtils.secp256k1.sign(message, privateKey, {data: data, noncefn: noncefn}) + }) +}) + +describe('verify', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(null, signature, publicKey) + }) + }) + + it('message length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage().slice(1) + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('signature should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, null, publicKey) + }) + }) + + it('signature length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('signature is invalid (r equal N)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + getRandomBytes(32) + ]) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.verify(message, signature, null) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) +}) + +describe('recover', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(null, signature, 0) + }) + }) + + it('message length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage().slice(1) + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('signature should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + ethUtils.secp256k1.recover(message, null, 0) + }) + }) + + it('signature length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('signature is invalid (r equal N)', function () { + assert.throws(function () { + const message = util.getMessage() + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + getRandomBytes(32) + ]) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('recovery should be a Number', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, null) + }) + }) + + it('recovery is invalid (equal 4)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(privateKey, message) + ethUtils.secp256k1.recover(message, signature, 4) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, 0, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey, i) => { + const message = util.getMessage() + const publicKey = util.getPublicKey(privateKey) + const expected = util.sign(message, privateKey) + + const sigObj = ethUtils.secp256k1.sign(message, privateKey) + assert.deepEqual(sigObj.signature, expected.signatureLowS) + assert.deepEqual(sigObj.recovery, expected.recovery) + + const isValid = ethUtils.secp256k1.verify(message, sigObj.signature, publicKey.compressed) + assert.equal(isValid, true) + + const compressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, true) + assert.deepEqual(compressed, publicKey.compressed) + + const uncompressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, false) + assert.deepEqual(uncompressed, publicKey.uncompressed) + }) + }) +}) diff --git a/test/util.js b/test/util.js new file mode 100644 index 00000000..add11132 --- /dev/null +++ b/test/util.js @@ -0,0 +1,79 @@ +// This file is imported from secp256k1 v3 +// https://github.com/cryptocoinjs/secp256k1-node/blob/master/LICENSE + +const BN = require('bn.js') +const EC = require('elliptic').ec +const ec = new EC('secp256k1') +const getRandomBytes = require('crypto').randomBytes + +const getPrivateKeys = function (count) { + const privateKeys = [] + for (let i = 0; i < count; i++) { + privateKeys.push(getRandomBytes(32)) + } + + return privateKeys +} + +const getPrivateKey = function () { + return getRandomBytes(32) +} + +const getTweak = function () { + return getRandomBytes(32) +} + +const getMessage = function () { + return getRandomBytes(32) +} + +function getSignature (message, privateKey) { + return sign(message, privateKey).signatureLowS +} + +const getPublicKey = function (privateKey) { + const publicKey = ec.keyFromPrivate(privateKey).getPublic() + return { + compressed: Buffer.from(publicKey.encode(null, true)), + uncompressed: Buffer.from(publicKey.encode(null, false)) + } +} + +const sign = function (message, privateKey) { + const ecSig = ec.sign(message, privateKey, {canonical: false}) + + const signature = Buffer.concat([ + ecSig.r.toArrayLike(Buffer, 'be', 32), + ecSig.s.toArrayLike(Buffer, 'be', 32) + ]) + let recovery = ecSig.recoveryParam + if (ecSig.s.cmp(ec.nh) === 1) { + ecSig.s = ec.n.sub(ecSig.s) + recovery ^= 1 + } + const signatureLowS = Buffer.concat([ + ecSig.r.toArrayLike(Buffer, 'be', 32), + ecSig.s.toArrayLike(Buffer, 'be', 32) + ]) + + return { + signature: signature, + signatureLowS: signatureLowS, + recovery: recovery + } +} + +module.exports = { + ec: ec, + BN_ZERO: new BN(0), + BN_ONE: new BN(1), + + getPrivateKeys: getPrivateKeys, + getPrivateKey: getPrivateKey, + getPublicKey: getPublicKey, + getTweak: getTweak, + getMessage: getMessage, + getSignature: getSignature, + + sign: sign +} From d1e5abdc21b4a144bc66064090e14471f38baefb Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 00:24:11 +0200 Subject: [PATCH 2/8] Init xvfb as a service. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 65a3d916..4bf2815c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,14 +6,14 @@ node_js: - "5" env: - CXX=g++-4.8 +services: + - xvfb addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 -before_install: - - sh -e /etc/init.d/xvfb start env: global: - DISPLAY=:99.0 From cbcb0ecf86c9d9ceb45ea998888f70fbd56f89c0 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 00:45:36 +0200 Subject: [PATCH 3/8] Extract secp256k1 adapter tests. --- test/index.js | 1327 ------------------------------------ test/secp256k1-adapter.js | 1329 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1329 insertions(+), 1327 deletions(-) create mode 100644 test/secp256k1-adapter.js diff --git a/test/index.js b/test/index.js index bcda2328..b9811590 100644 --- a/test/index.js +++ b/test/index.js @@ -1,8 +1,6 @@ var assert = require('assert') var ethUtils = require('../index.js') var BN = require('bn.js') -const util = require('./util') -const getRandomBytes = require('crypto').randomBytes describe('zeros function', function () { it('should produce lots of 0s', function () { @@ -538,1328 +536,3 @@ describe('message sig', function () { }) }) }) - -describe('privateKeyVerify', function () { - it('should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyVerify(null) - }) - }) - - it('invalid length', function () { - assert.equal(ethUtils.secp256k1.privateKeyVerify(util.getPrivateKey().slice(1)), false) - }) - - it('zero key', function () { - const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) - assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) - }) - - it('equal to N', function () { - const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - privateKeys.forEach((privateKey) => { - assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), true) - }) - }) -}) - -describe('privateKeyExport', function () { - it('private key should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyExport(null) - }) - }) - - it('private key length is invalid', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyExport(util.getPrivateKey().slice(1)) - }) - }) - - it('private key is invalid', function () { - assert.throws(function () { - const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyExport(privateKey) - }) - }) -}) - -describe('privateKeyImport', function () { - it('should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyImport(null) - }) - }) - - it('invalid format', function () { - const buffers = [ - Buffer.from([0x00]), - Buffer.from([0x30, 0x7b]), - Buffer.from([0x30, 0x87]), - Buffer.from([0x30, 0x81]), - Buffer.from([0x30, 0x82, 0x00, 0xff]), - Buffer.from([0x30, 0x82, 0x00, 0x00]), - Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]) - ] - - buffers.forEach((buffer) => { - assert.throws(function () { - ethUtils.secp256k1.privateKeyImport(buffer) - }) - }) - }) -}) - -describe('privateKeyExport/privateKeyImport', function () { - it('export/import', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const der1 = ethUtils.secp256k1.privateKeyExport(privateKey, true) - const privateKey1 = ethUtils.secp256k1.privateKeyImport(der1) - assert.deepEqual(privateKey1, privateKey) - - const der2 = ethUtils.secp256k1.privateKeyExport(privateKey, false) - const privateKey2 = ethUtils.secp256k1.privateKeyImport(der2) - assert.deepEqual(privateKey2, privateKey) - - const der3 = ethUtils.secp256k1.privateKeyExport(privateKey) - const privateKey3 = ethUtils.secp256k1.privateKeyImport(der3) - assert.deepEqual(privateKey3, privateKey) - }) - }) -}) - -describe('privateKeyNegate', function () { - it('private key should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyNegate(null) - }) - }) - - it('private key length is invalid', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyNegate(util.getPrivateKey().slice(1)) - }) - }) - - it('private key is 0', function () { - const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) - - const expected = Buffer.alloc(32) - const result = ethUtils.secp256k1.privateKeyNegate(privateKey) - assert.deepEqual(result, expected) - }) - - it('private key equal to N', function () { - const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - - const expected = Buffer.alloc(32) - const result = ethUtils.secp256k1.privateKeyNegate(privateKey) - assert.deepEqual(result, expected) - }) - - it('private key overflow', function () { - const privateKey = util.ec.curve.n.addn(10).toArrayLike(Buffer, 'be', 32) - - const expected = util.ec.curve.n.subn(10).toArrayLike(Buffer, 'be', 32) - const result = ethUtils.secp256k1.privateKeyNegate(privateKey) - assert.deepEqual(result, expected) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - privateKeys.forEach((privateKey) => { - const expected = util.ec.curve.n.sub(new BN(privateKey)) - const result = ethUtils.secp256k1.privateKeyNegate(privateKey) - - assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) - }) - }) -}) - -describe('privateKeyModInverse', function () { - it('private key should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.privateKeyModInverse(null) - }) - }) - - it('private key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - ethUtils.secp256k1.privateKeyModInverse(privateKey) - }) - }) - - it('private key is 0', function () { - assert.throws(function () { - const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyModInverse(privateKey) - }) - }) - - it('private key equal to N', function () { - assert.throws(function () { - const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyModInverse(privateKey) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - privateKeys.forEach((privateKey) => { - const expected = new BN(privateKey).invm(new BN(util.ec.curve.n.toArrayLike(Buffer, 'be', 32))) - const result = ethUtils.secp256k1.privateKeyModInverse(privateKey) - - assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) - }) - }) -}) - -describe('privateKeyTweakAdd', function () { - it('private key should be a Buffer', function () { - assert.throws(function () { - const tweak = util.getTweak() - ethUtils.secp256k1.privateKeyTweakAdd(null, tweak) - }) - }) - - it('private key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - const tweak = util.getTweak() - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - }) - }) - - it('tweak should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, null) - }) - }) - - it('tweak length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const tweak = util.getTweak().slice(1) - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - }) - }) - - it('tweak overflow', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - }) - }) - - it('result is zero: (N - 1) + 1', function () { - assert.throws(function () { - const privateKey = util.ec.curve.n.sub(util.BN_ONE).toArrayLike(Buffer, 'be', 32) - const tweak = util.BN_ONE.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - const tweak = util.getTweak() - - privateKeys.forEach((privateKey) => { - const expected = new BN(privateKey).add(new BN(tweak)).mod(util.ec.curve.n) - if (expected.cmp(util.BN_ZERO) === 0) { - assert.throws(function () { - ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - }) - } else { - const result = ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) - assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) - } - }) - }) -}) - -describe('privateKeyTweakMul', function () { - it('private key should be a Buffer', function () { - assert.throws(function () { - const tweak = util.getPrivateKey() - ethUtils.secp256k1.privateKeyTweakMul(null, tweak) - }) - }) - - it('private key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - const tweak = util.getPrivateKey() - ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - }) - }) - - it('tweak should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.privateKeyTweakMul(privateKey, null) - }) - }) - - it('tweak length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const tweak = util.getTweak().slice(1) - ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - }) - }) - - it('tweak equal N', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - }) - }) - - it('tweak is 0', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const tweak = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - const tweak = util.getTweak() - - privateKeys.forEach((privateKey) => { - if (new BN(tweak).cmp(util.BN_ZERO) === 0) { - assert.throws(function () { - ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - }) - } else { - const expected = new BN(privateKey).mul(new BN(tweak)).mod(util.ec.curve.n) - const result = ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) - assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) - } - }) - }) -}) - -describe('publicKeyCreate', function () { - it('should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyCreate(null) - }) - }) - - it('invalid length', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - ethUtils.secp256k1.publicKeyCreate(privateKey) - }) - }) - - it('overflow', function () { - assert.throws(function () { - const privateKey = util.ec.curve.p.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyCreate(privateKey) - }) - }) - - it('equal zero', function () { - assert.throws(function () { - const privateKey = new BN(0).toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyCreate(privateKey) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.publicKeyCreate(privateKey, null) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const expected = util.getPublicKey(privateKey) - - const compressed = ethUtils.secp256k1.publicKeyCreate(privateKey, true) - assert.deepEqual(compressed, expected.compressed) - - const uncompressed = ethUtils.secp256k1.publicKeyCreate(privateKey, false) - assert.deepEqual(uncompressed, expected.uncompressed) - }) - }) -}) - -describe('publicKeyConvert', function () { - it('should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyConvert(null) - }) - }) - - it('length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - ethUtils.secp256k1.publicKeyConvert(publicKey) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.publicKeyConvert(publicKey, null) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const expected = util.getPublicKey(privateKey) - - const compressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, true) - assert.deepEqual(compressed, expected.compressed) - - const uncompressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, false) - assert.deepEqual(uncompressed, expected.uncompressed) - }) - }) -}) - -describe('publicKeyVerify', function () { - it('should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyVerify(null) - }) - }) - - it('invalid length', function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('invalid first byte', function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x01 - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('x overflow (first byte is 0x03)', function () { - const publicKey = Buffer.concat([ - Buffer.from([0x03]), - util.ec.curve.p.toArrayLike(Buffer, 'be', 32) - ]) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('x overflow', function () { - const publicKey = Buffer.concat([ - Buffer.from([0x04]), - util.ec.curve.p.toArrayLike(Buffer, 'be', 32) - ]) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('y overflow', function () { - const publicKey = Buffer.concat([ - Buffer.from([0x04]), - Buffer.alloc(32), - util.ec.curve.p.toArrayLike(Buffer, 'be', 32) - ]) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('y is even, first byte is 0x07', function () { - const publicKey = Buffer.concat([ - Buffer.from([0x07]), - Buffer.alloc(32), - util.ec.curve.p.subn(1).toArrayLike(Buffer, 'be', 32) - ]) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('y**2 !== x*x*x + 7', function () { - const publicKey = Buffer.concat([Buffer.from([0x04]), util.getTweak(), util.getTweak()]) - assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const expected = util.getPublicKey(privateKey) - - assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) - assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) - }) - }) -}) - -describe('publicKeyTweakAdd', function () { - it('public key should be a Buffer', function () { - assert.throws(function () { - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakAdd(null, tweak) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) - }) - }) - - it('public key is invalid (version is 0x01)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x01 - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) - }) - }) - - it('tweak should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, null) - }) - }) - - it('tweak length length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.getTweak().slice(1) - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) - }) - }) - - it('tweak overflow', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) - }) - }) - - it('tweak produce infinity point', function () { - // G * 1 - G = 0 - assert.throws(function () { - const publicKey = Buffer.from(util.ec.g.encode(null, true)) - publicKey[0] = publicKey[0] ^ 0x01 // change sign of G - const tweak = new BN(1).toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, null) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const tweak = util.getTweak() - const publicPoint = util.ec.g.mul(new BN(privateKey)) - const publicKey = Buffer.from(publicPoint.encode(null, true)) - const expected = util.ec.g.mul(new BN(tweak)).add(publicPoint) - - const compressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) - assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) - - const uncompressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, false) - assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) - }) - }) -}) - -describe('publicKeyTweakMul', function () { - it('public key should be a Buffer', function () { - assert.throws(function () { - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakMul(null, tweak) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - }) - - it('public key is invalid (version is 0x01)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x01 - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - }) - - it('tweak should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.publicKeyTweakMul(publicKey, null) - }) - }) - - it('tweak length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.getTweak().slice(1) - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - }) - - it('tweak is zero', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = new BN(0).toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - }) - - it('tweak overflow', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - const tweak = util.getTweak() - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, null) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const tweak = util.getTweak() - const publicPoint = util.ec.g.mul(new BN(privateKey)) - const publicKey = Buffer.from(publicPoint.encode(null, true)) - - if (new BN(tweak).cmp(util.BN_ZERO) === 0) { - assert.throws(function () { - ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) - }) - } else { - const expected = publicPoint.mul(tweak) - - const compressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, true) - assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) - - const uncompressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, false) - assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) - } - }) - }) -}) - -describe('publicKeyCombine', function () { - it('public keys should be an Array', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyCombine(null) - }) - }) - - it('public keys should have length greater that zero', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyCombine([]) - }) - }) - - it('public key should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.publicKeyCombine([null]) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - ethUtils.secp256k1.publicKeyCombine([publicKey]) - }) - }) - - it('public key is invalid (version is 0x01)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x01 - ethUtils.secp256k1.publicKeyCombine([publicKey]) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.publicKeyCombine([publicKey], null) - }) - }) - - it('P + (-P) = 0', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey1 = util.getPublicKey(privateKey).compressed - const publicKey2 = Buffer.from(publicKey1) - publicKey2[0] = publicKey2[0] ^ 0x01 - ethUtils.secp256k1.publicKeyCombine([publicKey1, publicKey2], true) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const cnt = 1 + Math.floor(Math.random() * 3) // 1 <= cnt <= 3 - const privateKeys = [] - while (privateKeys.length < cnt) privateKeys.push(util.getPrivateKey()) - const publicKeys = privateKeys.map(function (privateKey) { - return util.getPublicKey(privateKey).compressed - }) - - let expected = util.ec.g.mul(new BN(privateKeys[0])) - for (let i = 1; i < privateKeys.length; ++i) { - const publicPoint = util.ec.g.mul(new BN(privateKeys[i])) - expected = expected.add(publicPoint) - } - - const compressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, true) - assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) - - const uncompressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, false) - assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) - }) - }) -}) - -describe('signatureNormalize', function () { - it('signature should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.signatureNormalize(null) - }) - }) - - it('invalid length', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey).slice(1) - ethUtils.secp256k1.signatureNormalize(signature) - }) - }) - - it('parse fail (r equal N)', function () { - assert.throws(function () { - const signature = Buffer.concat([ - util.ec.curve.n.toArrayLike(Buffer, 'be', 32), - util.BN_ONE.toArrayLike(Buffer, 'be', 32) - ]) - ethUtils.secp256k1.signatureNormalize(signature) - }) - }) - - it('normalize return same signature (s equal n/2)', function () { - const signature = Buffer.concat([ - util.BN_ONE.toArrayLike(Buffer, 'be', 32), - util.ec.nh.toArrayLike(Buffer, 'be', 32) - ]) - const result = ethUtils.secp256k1.signatureNormalize(signature) - assert.deepEqual(result, signature) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const message = util.getMessage() - - const sigObj = util.sign(message, privateKey) - const result = ethUtils.secp256k1.signatureNormalize(sigObj.signature) - assert.deepEqual(result, sigObj.signatureLowS) - }) - }) -}) - -describe('signatureExport', function () { - it('signature should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.signatureExport(null) - }) - }) - - it('invalid length', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey).slice(1) - ethUtils.secp256k1.signatureExport(signature) - }) - }) - - it('parse fail (r equal N)', function () { - assert.throws(function () { - const signature = Buffer.concat([ - util.ec.n.toArrayLike(Buffer, 'be', 32), - util.BN_ONE.toArrayLike(Buffer, 'be', 32) - ]) - ethUtils.secp256k1.signatureExport(signature) - }) - }) -}) - -describe('signatureImport', function () { - it('signature should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.signatureImport(null) - }) - }) - - it('parse fail', function () { - assert.throws(function () { - ethUtils.secp256k1.signatureImport(Buffer.alloc(1)) - }) - }) - - it('parse not bip66 signature', function () { - const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') - assert.throws(function () { - ethUtils.secp256k1.signatureImport(signature) - }) - }) -}) - -describe('signatureImportLax', function () { - it('signature should be a Buffer', function () { - assert.throws(function () { - ethUtils.secp256k1.signatureImportLax(null) - }) - }) - - it('parse fail', function () { - const buffers = [ - Buffer.alloc(0), - Buffer.alloc(1), - Buffer.from([0x30, 0x7b]), - Buffer.from([0x30, 0x87]), - Buffer.from([0x30, 0x80, 0x02, 0x80]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01]), - Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81, 0x01]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x21, 0x01, 0x00, 0x02, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02]), - Buffer.from([0x30, 0x81, 0x00, 0x02, 0x05, 0x01, 0x00, 0x02, 0x21, 0x02, 0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - ] - - buffers.forEach((buffer) => { - assert.throws(function () { - ethUtils.secp256k1.signatureImportLax(buffer) - }) - }) - }) - - it('parse not bip66 signature', function () { - const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') - assert.doesNotThrow(function () { - ethUtils.secp256k1.signatureImportLax(signature) - }) - }) -}) - -describe('signatureExport/signatureImport', function () { - it('signature should be a Buffer', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey) => { - const message = util.getMessage() - - const signature = util.sign(message, privateKey).signatureLowS - - const der = ethUtils.secp256k1.signatureExport(signature) - assert.deepEqual(ethUtils.secp256k1.signatureImport(der), signature) - assert.deepEqual(ethUtils.secp256k1.signatureImportLax(der), signature) - }) - }) -}) - -describe('ecdh', function () { - it('public key should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = null - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('invalid public key', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x00 - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('secret key should be a Buffer', function () { - assert.throws(function () { - const privateKey = null - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('secret key invalid length', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('secret key equal zero', function () { - assert.throws(function () { - const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('secret key equal N', function () { - assert.throws(function () { - const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdh(publicKey, privateKey) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey1, i) => { - const privateKey2 = util.getPrivateKey() - const publicKey1 = util.getPublicKey(privateKey1).compressed - const publicKey2 = util.getPublicKey(privateKey2).compressed - - const shared1 = ethUtils.secp256k1.ecdh(publicKey1, privateKey2) - const shared2 = ethUtils.secp256k1.ecdh(publicKey2, privateKey1) - assert.deepEqual(shared1, shared2) - }) - }) -}) - -describe('ecdhUnsafe', function () { - it('public key should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = null - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('invalid public key', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x00 - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('secret key should be a Buffer', function () { - assert.throws(function () { - const privateKey = null - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('secret key invalid length', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey().slice(1) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('secret key equal zero', function () { - assert.throws(function () { - const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('secret key equal N', function () { - assert.throws(function () { - const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) - const publicKey = util.getPublicKey(util.getPrivateKey()).compressed - ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey1, i) => { - const privateKey2 = util.getPrivateKey() - const publicKey1 = util.getPublicKey(privateKey1).compressed - const publicKey2 = util.getPublicKey(privateKey2).compressed - - const shared1 = ethUtils.secp256k1.ecdhUnsafe(publicKey1, privateKey2) - const shared2 = ethUtils.secp256k1.ecdhUnsafe(publicKey2, privateKey1) - assert.deepEqual(shared1, shared2) - }) - }) -}) - -describe('sign', function () { - it('message should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.sign(null, privateKey) - }) - }) - - it('message invalid length', function () { - assert.throws(function () { - const message = util.getMessage().slice(1) - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.sign(message, privateKey) - }) - }) - - it('private key should be a Buffer', function () { - assert.throws(function () { - const message = util.getMessage() - ethUtils.secp256k1.sign(message, null) - }) - }) - - it('private key invalid length', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey().slice(1) - ethUtils.secp256k1.sign(message, privateKey) - }) - }) - - it('private key is invalid', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) - ethUtils.secp256k1.sign(message, privateKey) - }) - }) - - it('options should be an Object', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.sign(message, privateKey, null) - }) - }) - - it('options.data should be a Buffer', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.sign(message, privateKey, {data: null}) - }) - }) - - it('options.data length is invalid', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - const data = getRandomBytes(31) - ethUtils.secp256k1.sign(message, privateKey, {data: data}) - }) - }) - - it('options.noncefn should be a Function', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - ethUtils.secp256k1.sign(message, privateKey, {noncefn: null}) - }) - }) - - it('noncefn return not a Buffer', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - const noncefn = function () { - return null - } - ethUtils.secp256k1.sign(message, privateKey, {noncefn: noncefn}) - }) - }) - - it('noncefn return Buffer with invalid length', function () { - assert.throws(function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - const noncefn = function () { - return getRandomBytes(31) - } - ethUtils.secp256k1.sign(message, privateKey, {noncefn: noncefn}) - }) - }) - - it('check options.noncefn arguments', function () { - const message = util.getMessage() - const privateKey = util.getPrivateKey() - const data = getRandomBytes(32) - const noncefn = function (message2, privateKey2, algo, data2, attempt) { - assert.deepEqual(message2, message) - assert.deepEqual(privateKey2, privateKey) - assert.deepEqual(algo, null) - assert.deepEqual(data2, data) - assert.deepEqual(attempt, 0) - return getRandomBytes(32) - } - ethUtils.secp256k1.sign(message, privateKey, {data: data, noncefn: noncefn}) - }) -}) - -describe('verify', function () { - it('message should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.verify(null, signature, publicKey) - }) - }) - - it('message length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage().slice(1) - const signature = util.getSignature(message, privateKey) - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.verify(message, signature, publicKey) - }) - }) - - it('signature should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.verify(message, null, publicKey) - }) - }) - - it('signature length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey).slice(1) - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.verify(message, signature, publicKey) - }) - }) - - it('signature is invalid (r equal N)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = Buffer.concat([ - util.ec.n.toArrayLike(Buffer, 'be', 32), - getRandomBytes(32) - ]) - const publicKey = util.getPublicKey(privateKey).compressed - ethUtils.secp256k1.verify(message, signature, publicKey) - }) - }) - - it('public key should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - ethUtils.secp256k1.verify(message, signature, null) - }) - }) - - it('public key length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - const publicKey = util.getPublicKey(privateKey).compressed.slice(1) - ethUtils.secp256k1.verify(message, signature, publicKey) - }) - }) - - it('public key is invalid (version is 0x01)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - const publicKey = util.getPublicKey(privateKey).compressed - publicKey[0] = 0x01 - ethUtils.secp256k1.verify(message, signature, publicKey) - }) - }) -}) - -describe('recover', function () { - it('message should be a Buffer', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - ethUtils.secp256k1.recover(null, signature, 0) - }) - }) - - it('message length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage().slice(1) - const signature = util.getSignature(message, privateKey) - ethUtils.secp256k1.recover(message, signature, 0) - }) - }) - - it('signature should be a Buffer', function () { - assert.throws(function () { - const message = util.getMessage() - ethUtils.secp256k1.recover(message, null, 0) - }) - }) - - it('signature length is invalid', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey).slice(1) - ethUtils.secp256k1.recover(message, signature, 0) - }) - }) - - it('signature is invalid (r equal N)', function () { - assert.throws(function () { - const message = util.getMessage() - const signature = Buffer.concat([ - util.ec.n.toArrayLike(Buffer, 'be', 32), - getRandomBytes(32) - ]) - ethUtils.secp256k1.recover(message, signature, 0) - }) - }) - - it('recovery should be a Number', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - ethUtils.secp256k1.recover(message, signature, null) - }) - }) - - it('recovery is invalid (equal 4)', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(privateKey, message) - ethUtils.secp256k1.recover(message, signature, 4) - }) - }) - - it('compressed should be a boolean', function () { - assert.throws(function () { - const privateKey = util.getPrivateKey() - const message = util.getMessage() - const signature = util.getSignature(message, privateKey) - ethUtils.secp256k1.recover(message, signature, 0, null) - }) - }) - - it('random tests', function () { - const privateKeys = util.getPrivateKeys(10) - - privateKeys.forEach((privateKey, i) => { - const message = util.getMessage() - const publicKey = util.getPublicKey(privateKey) - const expected = util.sign(message, privateKey) - - const sigObj = ethUtils.secp256k1.sign(message, privateKey) - assert.deepEqual(sigObj.signature, expected.signatureLowS) - assert.deepEqual(sigObj.recovery, expected.recovery) - - const isValid = ethUtils.secp256k1.verify(message, sigObj.signature, publicKey.compressed) - assert.equal(isValid, true) - - const compressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, true) - assert.deepEqual(compressed, publicKey.compressed) - - const uncompressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, false) - assert.deepEqual(uncompressed, publicKey.uncompressed) - }) - }) -}) diff --git a/test/secp256k1-adapter.js b/test/secp256k1-adapter.js new file mode 100644 index 00000000..438b1035 --- /dev/null +++ b/test/secp256k1-adapter.js @@ -0,0 +1,1329 @@ +// This file is imported from secp256k1 v3 +// https://github.com/cryptocoinjs/secp256k1-node/blob/master/LICENSE + +const assert = require('assert') +const ethUtils = require('../dist/index.js') +const BN = require('bn.js') +const util = require('./util') +const getRandomBytes = require('crypto').randomBytes + +describe('privateKeyVerify', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyVerify(null) + }) + }) + + it('invalid length', function () { + assert.equal(ethUtils.secp256k1.privateKeyVerify(util.getPrivateKey().slice(1)), false) + }) + + it('zero key', function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) + }) + + it('equal to N', function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), false) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + assert.equal(ethUtils.secp256k1.privateKeyVerify(privateKey), true) + }) + }) +}) + +describe('privateKeyExport', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyExport(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyExport(util.getPrivateKey().slice(1)) + }) + }) + + it('private key is invalid', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyExport(privateKey) + }) + }) +}) + +describe('privateKeyImport', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyImport(null) + }) + }) + + it('invalid format', function () { + const buffers = [ + Buffer.from([0x00]), + Buffer.from([0x30, 0x7b]), + Buffer.from([0x30, 0x87]), + Buffer.from([0x30, 0x81]), + Buffer.from([0x30, 0x82, 0x00, 0xff]), + Buffer.from([0x30, 0x82, 0x00, 0x00]), + Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]) + ] + + buffers.forEach((buffer) => { + assert.throws(function () { + ethUtils.secp256k1.privateKeyImport(buffer) + }) + }) + }) +}) + +describe('privateKeyExport/privateKeyImport', function () { + it('export/import', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const der1 = ethUtils.secp256k1.privateKeyExport(privateKey, true) + const privateKey1 = ethUtils.secp256k1.privateKeyImport(der1) + assert.deepEqual(privateKey1, privateKey) + + const der2 = ethUtils.secp256k1.privateKeyExport(privateKey, false) + const privateKey2 = ethUtils.secp256k1.privateKeyImport(der2) + assert.deepEqual(privateKey2, privateKey) + + const der3 = ethUtils.secp256k1.privateKeyExport(privateKey) + const privateKey3 = ethUtils.secp256k1.privateKeyImport(der3) + assert.deepEqual(privateKey3, privateKey) + }) + }) +}) + +describe('privateKeyNegate', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyNegate(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyNegate(util.getPrivateKey().slice(1)) + }) + }) + + it('private key is 0', function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + + const expected = Buffer.alloc(32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('private key equal to N', function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + + const expected = Buffer.alloc(32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('private key overflow', function () { + const privateKey = util.ec.curve.n.addn(10).toArrayLike(Buffer, 'be', 32) + + const expected = util.ec.curve.n.subn(10).toArrayLike(Buffer, 'be', 32) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + assert.deepEqual(result, expected) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + const expected = util.ec.curve.n.sub(new BN(privateKey)) + const result = ethUtils.secp256k1.privateKeyNegate(privateKey) + + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + }) + }) +}) + +describe('privateKeyModInverse', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.privateKeyModInverse(null) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('private key is 0', function () { + assert.throws(function () { + const privateKey = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('private key equal to N', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyModInverse(privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + privateKeys.forEach((privateKey) => { + const expected = new BN(privateKey).invm(new BN(util.ec.curve.n.toArrayLike(Buffer, 'be', 32))) + const result = ethUtils.secp256k1.privateKeyModInverse(privateKey) + + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + }) + }) +}) + +describe('privateKeyTweakAdd', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.privateKeyTweakAdd(null, tweak) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('result is zero: (N - 1) + 1', function () { + assert.throws(function () { + const privateKey = util.ec.curve.n.sub(util.BN_ONE).toArrayLike(Buffer, 'be', 32) + const tweak = util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + const tweak = util.getTweak() + + privateKeys.forEach((privateKey) => { + const expected = new BN(privateKey).add(new BN(tweak)).mod(util.ec.curve.n) + if (expected.cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + }) + } else { + const result = ethUtils.secp256k1.privateKeyTweakAdd(privateKey, tweak) + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + } + }) + }) +}) + +describe('privateKeyTweakMul', function () { + it('private key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(null, tweak) + }) + }) + + it('private key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const tweak = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.privateKeyTweakMul(privateKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak equal N', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('tweak is 0', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const tweak = util.BN_ZERO.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + const tweak = util.getTweak() + + privateKeys.forEach((privateKey) => { + if (new BN(tweak).cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + }) + } else { + const expected = new BN(privateKey).mul(new BN(tweak)).mod(util.ec.curve.n) + const result = ethUtils.secp256k1.privateKeyTweakMul(privateKey, tweak) + assert.deepEqual(result.toString('hex'), expected.toString(16, 64)) + } + }) + }) +}) + +describe('publicKeyCreate', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCreate(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('overflow', function () { + assert.throws(function () { + const privateKey = util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('equal zero', function () { + assert.throws(function () { + const privateKey = new BN(0).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyCreate(privateKey) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.publicKeyCreate(privateKey, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + const compressed = ethUtils.secp256k1.publicKeyCreate(privateKey, true) + assert.deepEqual(compressed, expected.compressed) + + const uncompressed = ethUtils.secp256k1.publicKeyCreate(privateKey, false) + assert.deepEqual(uncompressed, expected.uncompressed) + }) + }) +}) + +describe('publicKeyConvert', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyConvert(null) + }) + }) + + it('length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.publicKeyConvert(publicKey) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyConvert(publicKey, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + const compressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, true) + assert.deepEqual(compressed, expected.compressed) + + const uncompressed = ethUtils.secp256k1.publicKeyConvert(expected.uncompressed, false) + assert.deepEqual(uncompressed, expected.uncompressed) + }) + }) +}) + +describe('publicKeyVerify', function () { + it('should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyVerify(null) + }) + }) + + it('invalid length', function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('invalid first byte', function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('x overflow (first byte is 0x03)', function () { + const publicKey = Buffer.concat([ + Buffer.from([ 0x03 ]), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('x overflow', function () { + const publicKey = Buffer.concat([ + Buffer.from([ 0x04 ]), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y overflow', function () { + const publicKey = Buffer.concat([ + Buffer.from([ 0x04 ]), + Buffer.alloc(32), + util.ec.curve.p.toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y is even, first byte is 0x07', function () { + const publicKey = Buffer.concat([ + Buffer.from([ 0x07 ]), + Buffer.alloc(32), + util.ec.curve.p.subn(1).toArrayLike(Buffer, 'be', 32) + ]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('y**2 !== x*x*x + 7', function () { + const publicKey = Buffer.concat([Buffer.from([0x04]), util.getTweak(), util.getTweak()]) + assert.equal(ethUtils.secp256k1.publicKeyVerify(publicKey), false) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const expected = util.getPublicKey(privateKey) + + assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) + assert.equal(ethUtils.secp256k1.publicKeyVerify(expected.uncompressed), true) + }) + }) +}) + +describe('publicKeyTweakAdd', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(null, tweak) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, null) + }) + }) + + it('tweak length length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak) + }) + }) + + it('tweak produce infinity point', function () { + // G * 1 - G = 0 + assert.throws(function () { + const publicKey = Buffer.from(util.ec.g.encode(null, true)) + publicKey[0] = publicKey[0] ^ 0x01 // change sign of G + const tweak = new BN(1).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const tweak = util.getTweak() + const publicPoint = util.ec.g.mul(new BN(privateKey)) + const publicKey = Buffer.from(publicPoint.encode(null, true)) + const expected = util.ec.g.mul(new BN(tweak)).add(publicPoint) + + const compressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyTweakAdd(publicKey, tweak, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + }) + }) +}) + +describe('publicKeyTweakMul', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(null, tweak) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyTweakMul(publicKey, null) + }) + }) + + it('tweak length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak().slice(1) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak is zero', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = new BN(0).toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('tweak overflow', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.ec.curve.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + const tweak = util.getTweak() + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const tweak = util.getTweak() + const publicPoint = util.ec.g.mul(new BN(privateKey)) + const publicKey = Buffer.from(publicPoint.encode(null, true)) + + if (new BN(tweak).cmp(util.BN_ZERO) === 0) { + assert.throws(function () { + ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak) + }) + } else { + const expected = publicPoint.mul(tweak) + + const compressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyTweakMul(publicKey, tweak, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + } + }) + }) +}) + +describe('publicKeyCombine', function () { + it('public keys should be an Array', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine(null) + }) + }) + + it('public keys should have length greater that zero', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine([]) + }) + }) + + it('public key should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.publicKeyCombine([null]) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.publicKeyCombine([publicKey]) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + ethUtils.secp256k1.publicKeyCombine([publicKey]) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.publicKeyCombine([publicKey], null) + }) + }) + + it('P + (-P) = 0', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey).compressed + const publicKey2 = Buffer.from(publicKey1) + publicKey2[0] = publicKey2[0] ^ 0x01 + ethUtils.secp256k1.publicKeyCombine([publicKey1, publicKey2], true) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const cnt = 1 + Math.floor(Math.random() * 3) // 1 <= cnt <= 3 + const privateKeys = [] + while (privateKeys.length < cnt) privateKeys.push(util.getPrivateKey()) + const publicKeys = privateKeys.map(function (privateKey) { + return util.getPublicKey(privateKey).compressed + }) + + let expected = util.ec.g.mul(new BN(privateKeys[0])) + for (let i = 1; i < privateKeys.length; ++i) { + const publicPoint = util.ec.g.mul(new BN(privateKeys[i])) + expected = expected.add(publicPoint) + } + + const compressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, true) + assert.deepEqual(compressed.toString('hex'), expected.encode('hex', true)) + + const uncompressed = ethUtils.secp256k1.publicKeyCombine(publicKeys, false) + assert.deepEqual(uncompressed.toString('hex'), expected.encode('hex', false)) + }) + }) +}) + +describe('signatureNormalize', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureNormalize(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.signatureNormalize(signature) + }) + }) + + it('parse fail (r equal N)', function () { + assert.throws(function () { + const signature = Buffer.concat([ + util.ec.curve.n.toArrayLike(Buffer, 'be', 32), + util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ]) + ethUtils.secp256k1.signatureNormalize(signature) + }) + }) + + it('normalize return same signature (s equal n/2)', function () { + const signature = Buffer.concat([ + util.BN_ONE.toArrayLike(Buffer, 'be', 32), + util.ec.nh.toArrayLike(Buffer, 'be', 32) + ]) + const result = ethUtils.secp256k1.signatureNormalize(signature) + assert.deepEqual(result, signature) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const message = util.getMessage() + + const sigObj = util.sign(message, privateKey) + const result = ethUtils.secp256k1.signatureNormalize(sigObj.signature) + assert.deepEqual(result, sigObj.signatureLowS) + }) + }) +}) + +describe('signatureExport', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureExport(null) + }) + }) + + it('invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.signatureExport(signature) + }) + }) + + it('parse fail (r equal N)', function () { + assert.throws(function () { + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + util.BN_ONE.toArrayLike(Buffer, 'be', 32) + ]) + ethUtils.secp256k1.signatureExport(signature) + }) + }) +}) + +describe('signatureImport', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImport(null) + }) + }) + + it('parse fail', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImport(Buffer.alloc(1)) + }) + }) + + it('parse not bip66 signature', function () { + const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') + assert.throws(function () { + ethUtils.secp256k1.signatureImport(signature) + }) + }) +}) + +describe('signatureImportLax', function () { + it('signature should be a Buffer', function () { + assert.throws(function () { + ethUtils.secp256k1.signatureImportLax(null) + }) + }) + + it('parse fail', function () { + const buffers = [ + Buffer.alloc(0), + Buffer.alloc(1), + Buffer.from([0x30, 0x7b]), + Buffer.from([0x30, 0x87]), + Buffer.from([0x30, 0x80, 0x02, 0x80]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01]), + Buffer.from([0x30, 0x82, 0x00, 0x00, 0x02, 0x01, 0x01]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x81, 0x01, 0x00, 0x02, 0x81, 0x01]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x21, 0x01, 0x00, 0x02, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02]), + Buffer.from([0x30, 0x81, 0x00, 0x02, 0x05, 0x01, 0x00, 0x02, 0x21, 0x02, 0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + ] + + buffers.forEach((buffer) => { + assert.throws(function () { + ethUtils.secp256k1.signatureImportLax(buffer) + }) + }) + }) + + it('parse not bip66 signature', function () { + const signature = Buffer.from('308002204171936738571ff75ec0c56c010f339f1f6d510ba45ad936b0762b1b2162d8020220152670567fa3cc92a5ea1a6ead11741832f8aede5ca176f559e8a46bb858e3f6', 'hex') + assert.doesNotThrow(function () { + ethUtils.secp256k1.signatureImportLax(signature) + }) + }) +}) + +describe('signatureExport/signatureImport', function () { + it('signature should be a Buffer', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey) => { + const message = util.getMessage() + + const signature = util.sign(message, privateKey).signatureLowS + + const der = ethUtils.secp256k1.signatureExport(signature) + assert.deepEqual(ethUtils.secp256k1.signatureImport(der), signature) + assert.deepEqual(ethUtils.secp256k1.signatureImportLax(der), signature) + }) + }) +}) + +describe('ecdh', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = null + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('invalid public key', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x00 + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key should be a Buffer', function () { + assert.throws(function () { + const privateKey = null + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key equal zero', function () { + assert.throws(function () { + const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('secret key equal N', function () { + assert.throws(function () { + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdh(publicKey, privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey1, i) => { + const privateKey2 = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey1).compressed + const publicKey2 = util.getPublicKey(privateKey2).compressed + + const shared1 = ethUtils.secp256k1.ecdh(publicKey1, privateKey2) + const shared2 = ethUtils.secp256k1.ecdh(publicKey2, privateKey1) + assert.deepEqual(shared1, shared2) + }) + }) +}) + +describe('ecdhUnsafe', function () { + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = null + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('invalid public key', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x00 + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key should be a Buffer', function () { + assert.throws(function () { + const privateKey = null + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key invalid length', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey().slice(1) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key equal zero', function () { + assert.throws(function () { + const privateKey = util.ec.curve.zero.fromRed().toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('secret key equal N', function () { + assert.throws(function () { + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + const publicKey = util.getPublicKey(util.getPrivateKey()).compressed + ethUtils.secp256k1.ecdhUnsafe(publicKey, privateKey) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey1, i) => { + const privateKey2 = util.getPrivateKey() + const publicKey1 = util.getPublicKey(privateKey1).compressed + const publicKey2 = util.getPublicKey(privateKey2).compressed + + const shared1 = ethUtils.secp256k1.ecdhUnsafe(publicKey1, privateKey2) + const shared2 = ethUtils.secp256k1.ecdhUnsafe(publicKey2, privateKey1) + assert.deepEqual(shared1, shared2) + }) + }) +}) + +describe('sign', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(null, privateKey) + }) + }) + + it('message invalid length', function () { + assert.throws(function () { + const message = util.getMessage().slice(1) + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('private key should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + ethUtils.secp256k1.sign(message, null) + }) + }) + + it('private key invalid length', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey().slice(1) + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('private key is invalid', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.ec.n.toArrayLike(Buffer, 'be', 32) + ethUtils.secp256k1.sign(message, privateKey) + }) + }) + + it('options should be an Object', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, null) + }) + }) + + it('options.data should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, { data: null }) + }) + }) + + it('options.data length is invalid', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const data = getRandomBytes(31) + ethUtils.secp256k1.sign(message, privateKey, { data: data }) + }) + }) + + it('options.noncefn should be a Function', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + ethUtils.secp256k1.sign(message, privateKey, { noncefn: null }) + }) + }) + + it('noncefn return not a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const noncefn = function () { return null } + ethUtils.secp256k1.sign(message, privateKey, { noncefn: noncefn }) + }) + }) + + it('noncefn return Buffer with invalid length', function () { + assert.throws(function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const noncefn = function () { return getRandomBytes(31) } + ethUtils.secp256k1.sign(message, privateKey, { noncefn: noncefn }) + }) + }) + + it('check options.noncefn arguments', function () { + const message = util.getMessage() + const privateKey = util.getPrivateKey() + const data = getRandomBytes(32) + const noncefn = function (message2, privateKey2, algo, data2, attempt) { + assert.deepEqual(message2, message) + assert.deepEqual(privateKey2, privateKey) + assert.deepEqual(algo, null) + assert.deepEqual(data2, data) + assert.deepEqual(attempt, 0) + return getRandomBytes(32) + } + ethUtils.secp256k1.sign(message, privateKey, { data: data, noncefn: noncefn }) + }) +}) + +describe('verify', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(null, signature, publicKey) + }) + }) + + it('message length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage().slice(1) + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('signature should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, null, publicKey) + }) + }) + + it('signature length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('signature is invalid (r equal N)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + getRandomBytes(32) + ]) + const publicKey = util.getPublicKey(privateKey).compressed + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('public key should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.verify(message, signature, null) + }) + }) + + it('public key length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed.slice(1) + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) + + it('public key is invalid (version is 0x01)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + const publicKey = util.getPublicKey(privateKey).compressed + publicKey[0] = 0x01 + ethUtils.secp256k1.verify(message, signature, publicKey) + }) + }) +}) + +describe('recover', function () { + it('message should be a Buffer', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(null, signature, 0) + }) + }) + + it('message length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage().slice(1) + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('signature should be a Buffer', function () { + assert.throws(function () { + const message = util.getMessage() + ethUtils.secp256k1.recover(message, null, 0) + }) + }) + + it('signature length is invalid', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey).slice(1) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('signature is invalid (r equal N)', function () { + assert.throws(function () { + const message = util.getMessage() + const signature = Buffer.concat([ + util.ec.n.toArrayLike(Buffer, 'be', 32), + getRandomBytes(32) + ]) + ethUtils.secp256k1.recover(message, signature, 0) + }) + }) + + it('recovery should be a Number', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, null) + }) + }) + + it('recovery is invalid (equal 4)', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(privateKey, message) + ethUtils.secp256k1.recover(message, signature, 4) + }) + }) + + it('compressed should be a boolean', function () { + assert.throws(function () { + const privateKey = util.getPrivateKey() + const message = util.getMessage() + const signature = util.getSignature(message, privateKey) + ethUtils.secp256k1.recover(message, signature, 0, null) + }) + }) + + it('random tests', function () { + const privateKeys = util.getPrivateKeys(10) + + privateKeys.forEach((privateKey, i) => { + const message = util.getMessage() + const publicKey = util.getPublicKey(privateKey) + const expected = util.sign(message, privateKey) + + const sigObj = ethUtils.secp256k1.sign(message, privateKey) + assert.deepEqual(sigObj.signature, expected.signatureLowS) + assert.deepEqual(sigObj.recovery, expected.recovery) + + const isValid = ethUtils.secp256k1.verify(message, sigObj.signature, publicKey.compressed) + assert.equal(isValid, true) + + const compressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, true) + assert.deepEqual(compressed, publicKey.compressed) + + const uncompressed = ethUtils.secp256k1.recover(message, sigObj.signature, sigObj.recovery, false) + assert.deepEqual(uncompressed, publicKey.uncompressed) + }) + }) +}) From 9b0b11bc094cf069d096d29624dca1a8d7327eb6 Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 00:46:15 +0200 Subject: [PATCH 4/8] Bump node version in ci. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4bf2815c..3a0d3943 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: node_js node_js: - - "0.11" - - "0.12" - - "4" - - "5" + - "6" + - "8" + - "10" + - "12" env: - CXX=g++-4.8 services: From e5197df1baab25dd0a8dc5b5a52dfe39d2fa310c Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 01:06:42 +0200 Subject: [PATCH 5/8] Add babel-cli for building. --- .babelrc | 7 +++++++ .travis.yml | 4 ++-- package.json | 13 +++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .babelrc diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..65f62059 --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": [ + [ + "env" + ] + ] +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 3a0d3943..f8e0a815 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,9 @@ matrix: fast_finish: true include: - os: linux - node_js: "4" + node_js: "8" env: CXX=g++-4.8 TEST_SUITE=coveralls - os: linux - node_js: "4" + node_js: "8" env: CXX=g++-4.8 TEST_SUITE=lint script: npm run $TEST_SUITE diff --git a/package.json b/package.json index 9fd625c5..3c36a1b4 100644 --- a/package.json +++ b/package.json @@ -2,15 +2,19 @@ "name": "ethereumjs-util", "version": "4.5.0", "description": "a collection of utility functions for Ethereum", - "main": "index.js", + "main": "dist/index.js", + "files": [ + "dist" + ], "scripts": { "coverage": "istanbul cover _mocha", "coveralls": "npm run coverage && coveralls ./docs/index.md" }, "repository": { @@ -39,6 +43,8 @@ "elliptic": "^6.5.2" }, "devDependencies": { + "babel-cli": "^6.26.0", + "babel-preset-env": "^1.6.1", "browserify": "^13.0.0", "coveralls": "^2.11.4", "documentation": "^3.0.4", @@ -57,6 +63,9 @@ "globals": [ "describe", "it" + ], + "ignore": [ + "dist/**" ] } } From 3dd0e640322670dd730bc8fb436150a5b7538fea Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 3 Jul 2020 13:56:21 +0200 Subject: [PATCH 6/8] Fix test coverage. --- test/defineFields.js | 2 +- test/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/defineFields.js b/test/defineFields.js index 3045dc69..c181aeba 100644 --- a/test/defineFields.js +++ b/test/defineFields.js @@ -1,5 +1,5 @@ var assert = require('assert') -var ethUtil = require('../index.js') +var ethUtil = require('../dist/index.js') describe('define', function () { const fields = [{ diff --git a/test/index.js b/test/index.js index b9811590..e1128a9e 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,5 @@ var assert = require('assert') -var ethUtils = require('../index.js') +var ethUtils = require('../dist/index.js') var BN = require('bn.js') describe('zeros function', function () { From c935a48a722dec81b2536f4b8bd780cbde79730a Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 10 Jul 2020 11:33:43 +0200 Subject: [PATCH 7/8] Remove keccakjs dependency. --- index.js | 22 +++++++++++++++++----- package.json | 1 - 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index c621bee3..a90d3f55 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -const SHA3 = require('keccakjs') +const { keccak224, keccak384, keccak256: k256, keccak512 } = require('ethereum-cryptography/keccak') const secp256k1 = require('./secp256k1-adapter') const assert = require('assert') const rlp = require('rlp') @@ -250,11 +250,23 @@ exports.sha3 = function (a, bytes) { a = exports.toBuffer(a) if (!bytes) bytes = 256 - var h = new SHA3(bytes) - if (a) { - h.update(a) + switch (bytes) { + case 224: { + return keccak224(a) + } + case 256: { + return k256(a) + } + case 384: { + return keccak384(a) + } + case 512: { + return keccak512(a) + } + default: { + throw new Error(`Invalid algorithm: keccak${bytes}`) + } } - return new Buffer(h.digest('hex'), 'hex') } /** diff --git a/package.json b/package.json index 3c36a1b4..0063b6df 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "dependencies": { "bn.js": "^4.8.0", "create-hash": "^1.1.2", - "keccakjs": "^0.2.0", "rlp": "^2.0.0", "ethereum-cryptography": "^0.1.3", "elliptic": "^6.5.2" From c15759752c007ac3eb78065d1acacbbcbdb2dbdb Mon Sep 17 00:00:00 2001 From: "nebojsa.urosevic" Date: Fri, 10 Jul 2020 11:34:06 +0200 Subject: [PATCH 8/8] Add comments to secp256k1 wrapper. --- secp256k1-adapter.js | 173 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/secp256k1-adapter.js b/secp256k1-adapter.js index 62a772a3..80361291 100644 --- a/secp256k1-adapter.js +++ b/secp256k1-adapter.js @@ -3,7 +3,14 @@ const secp256k1 = require('ethereum-cryptography/secp256k1') const secp256k1v3 = require('./secp256k1-lib/index') const der = require('./secp256k1-lib/der') +/** + * Verify an ECDSA privateKey + * @method privateKeyVerify + * @param {Buffer} privateKey + * @return {boolean} + */ const privateKeyVerify = function (privateKey) { + // secp256k1 v4 version throws when privateKey length is not 32 if (privateKey.length !== 32) { return false } @@ -11,7 +18,16 @@ const privateKeyVerify = function (privateKey) { return secp256k1.privateKeyVerify(Uint8Array.from(privateKey)) } +/** + * Export a privateKey in DER format + * @method privateKeyExport + * @param {Buffer} privateKey + * @param {boolean} compressed + * @return {boolean} + */ const privateKeyExport = function (privateKey, compressed) { + // privateKeyExport method is not part of secp256k1 v4 package + // this implementation is based on v3 if (privateKey.length !== 32) { throw new RangeError('private key length is invalid') } @@ -21,7 +37,16 @@ const privateKeyExport = function (privateKey, compressed) { return der.privateKeyExport(privateKey, publicKey, compressed) } +/** + * Import a privateKey in DER format + * @method privateKeyImport + * @param {Buffer} privateKey + * @return {Buffer} + */ + const privateKeyImport = function (privateKey) { + // privateKeyImport method is not part of secp256k1 v4 package + // this implementation is based on v3 privateKey = der.privateKeyImport(privateKey) if (privateKey !== null && privateKey.length === 32 && privateKeyVerify(privateKey)) { return privateKey @@ -30,10 +55,22 @@ const privateKeyImport = function (privateKey) { throw new Error("couldn't import from DER format") } +/** + * Negate a privateKey by subtracting it from the order of the curve's base point + * @method privateKeyNegate + * @param {Buffer} privateKey + * @return {Buffer} + */ const privateKeyNegate = function (privateKey) { return Buffer.from(secp256k1.privateKeyNegate(Uint8Array.from(privateKey))) } +/** + * Compute the inverse of a privateKey (modulo the order of the curve's base point). + * @method privateKeyModInverse + * @param {Buffer} privateKey + * @return {Buffer} + */ const privateKeyModInverse = function (privateKey) { if (privateKey.length !== 32) { throw new Error('private key length is invalid') @@ -42,25 +79,60 @@ const privateKeyModInverse = function (privateKey) { return Buffer.from(secp256k1v3.privateKeyModInverse(Uint8Array.from(privateKey))) } +/** + * Tweak a privateKey by adding tweak to it. + * @method privateKeyTweakAdd + * @param {Buffer} privateKey + * @param {Buffer} tweak + * @return {Buffer} + */ const privateKeyTweakAdd = function (privateKey, tweak) { return Buffer.from(secp256k1.privateKeyTweakAdd(Uint8Array.from(privateKey), tweak)) } +/** + * Tweak a privateKey by multiplying it by a tweak. + * @method privateKeyTweakMul + * @param {Buffer} privateKey + * @param {Buffer} tweak + * @return {Buffer} + */ const privateKeyTweakMul = function (privateKey, tweak) { return Buffer.from( secp256k1.privateKeyTweakMul(Uint8Array.from(privateKey), Uint8Array.from(tweak)) ) } +/** + * Compute the public key for a privateKey. + * @method publicKeyCreate + * @param {Buffer} privateKey + * @param {boolean} compressed + * @return {Buffer} + */ const publicKeyCreate = function (privateKey, compressed) { return Buffer.from(secp256k1.publicKeyCreate(Uint8Array.from(privateKey), compressed)) } +/** + * Convert a publicKey to compressed or uncompressed form. + * @method publicKeyConvert + * @param {Buffer} publicKey + * @param {boolean} compressed + * @return {Buffer} + */ const publicKeyConvert = function (publicKey, compressed) { return Buffer.from(secp256k1.publicKeyConvert(Uint8Array.from(publicKey), compressed)) } +/** + * Verify an ECDSA publicKey. + * @method publicKeyVerify + * @param {Buffer} publicKey + * @return {boolean} + */ const publicKeyVerify = function (publicKey) { + // secp256k1 v4 version throws when publicKey length is not 33 or 65 if (publicKey.length !== 33 && publicKey.length !== 65) { return false } @@ -68,18 +140,41 @@ const publicKeyVerify = function (publicKey) { return secp256k1.publicKeyVerify(Uint8Array.from(publicKey)) } +/** + * Tweak a publicKey by adding tweak times the generator to it. + * @method publicKeyTweakAdd + * @param {Buffer} publicKey + * @param {Buffer} tweak + * @param {boolean} compressed + * @return {Buffer} + */ const publicKeyTweakAdd = function (publicKey, tweak, compressed) { return Buffer.from( secp256k1.publicKeyTweakAdd(Uint8Array.from(publicKey), Uint8Array.from(tweak), compressed) ) } +/** + * Tweak a publicKey by multiplying it by a tweak value + * @method publicKeyTweakMul + * @param {Buffer} publicKey + * @param {Buffer} tweak + * @param {boolean} compressed + * @return {Buffer} + */ const publicKeyTweakMul = function (publicKey, tweak, compressed) { return Buffer.from( secp256k1.publicKeyTweakMul(Uint8Array.from(publicKey), Uint8Array.from(tweak), compressed) ) } +/** + * Add a given publicKeys together. + * @method publicKeyCombine + * @param {Array} publicKeys + * @param {boolean} compressed + * @return {Buffer} + */ const publicKeyCombine = function (publicKeys, compressed) { const keys = [] publicKeys.forEach((publicKey) => { @@ -89,19 +184,46 @@ const publicKeyCombine = function (publicKeys, compressed) { return Buffer.from(secp256k1.publicKeyCombine(keys, compressed)) } +/** + * Convert a signature to a normalized lower-S form. + * @method signatureNormalize + * @param {Buffer} signature + * @return {Buffer} + */ const signatureNormalize = function (signature) { return Buffer.from(secp256k1.signatureNormalize(Uint8Array.from(signature))) } +/** + * Serialize an ECDSA signature in DER format. + * @method signatureExport + * @param {Buffer} signature + * @return {Buffer} + */ const signatureExport = function (signature) { return Buffer.from(secp256k1.signatureExport(Uint8Array.from(signature))) } +/** + * Parse a DER ECDSA signature (follow by [BIP66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)). + * @method signatureImport + * @param {Buffer} signature + * @return {Buffer} + */ const signatureImport = function (signature) { return Buffer.from(secp256k1.signatureImport(Uint8Array.from(signature))) } +/** + * Parse a DER ECDSA signature (not follow by [BIP66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)). + * @method signatureImportLax + * @param {Buffer} signature + * @return {Buffer} + */ const signatureImportLax = function (signature) { + // signatureImportLax method is not part of secp256k1 v4 package + // this implementation is based on v3 + // ensure that signature is greater than 0 if (signature.length === 0) { throw new RangeError('signature length is invalid') } @@ -114,6 +236,14 @@ const signatureImportLax = function (signature) { return secp256k1v3.signatureImport(sigObj) } +/** + * Create an ECDSA signature. Always return low-S signature. + * @method sign + * @param {Buffer} message + * @param {Buffer} privateKey + * @param {Object} options + * @return {Buffer} + */ const sign = function (message, privateKey, options) { if (options === null) { throw new TypeError('options should be an Object') @@ -129,6 +259,7 @@ const sign = function (message, privateKey, options) { } if (options.data) { + // validate option.data length if (options.data.length !== 32) { throw new RangeError('options.data length is invalid') } @@ -141,6 +272,7 @@ const sign = function (message, privateKey, options) { } if (options.noncefn) { + // convert option.noncefn function signature signOptions.noncefn = (message, privateKey, algo, data, attempt) => { const bufferAlgo = algo != null ? Buffer.from(algo) : null const bufferData = data != null ? Buffer.from(data) : null @@ -156,7 +288,7 @@ const sign = function (message, privateKey, options) { ) } - return new Uint8Array(buffer) + return Uint8Array.from(buffer) } } } @@ -173,25 +305,64 @@ const sign = function (message, privateKey, options) { } } +/** + * Verify an ECDSA signature. + * @method verify + * @param {Buffer} message + * @param {Buffer} signature + * @param {Buffer} publicKey + * @return {boolean} + */ const verify = function (message, signature, publicKey) { + // note: secp256k1 v4 verify method has a different argument order return secp256k1.ecdsaVerify(Uint8Array.from(signature), Uint8Array.from(message), publicKey) } +/** + * Recover an ECDSA public key from a signature. + * @method recover + * @param {Buffer} message + * @param {Buffer} signature + * @param {Number} recid + * @param {boolean} compressed + * @return {Buffer} + */ const recover = function (message, signature, recid, compressed) { + // note: secp256k1 v4 recover method has a different argument order return Buffer.from( secp256k1.ecdsaRecover(Uint8Array.from(signature), recid, Uint8Array.from(message), compressed) ) } +/** + * Compute an EC Diffie-Hellman secret and applied sha256 to compressed public key. + * @method ecdh + * @param {Buffer} publicKey + * @param {Buffer} privateKey + * @return {Buffer} + */ const ecdh = function (publicKey, privateKey) { + // note: secp256k1 v3 doesn't allow optional parameter return Buffer.from(secp256k1.ecdh(Uint8Array.from(publicKey), Uint8Array.from(privateKey), {})) } +/** + * Compute an EC Diffie-Hellman secret and return public key as result + * @method ecdhUnsafe + * @param {Buffer} publicKey + * @param {Buffer} privateKey + * @param {boolean} compressed + * @return {Buffer} + */ const ecdhUnsafe = function (publicKey, privateKey, compressed) { + // ecdhUnsafe method is not part of secp256k1 v4 package + // this implementation is based on v3 + // ensure valid publicKey length if (publicKey.length !== 33 && publicKey.length !== 65) { throw new RangeError('public key length is invalid') } + // ensure valid privateKey length if (privateKey.length !== 32) { throw new RangeError('private key length is invalid') }