Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fanatid committed Jan 6, 2020
1 parent 1766281 commit dd54901
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 24 deletions.
7 changes: 4 additions & 3 deletions lib/elliptic.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ function loadUncompressedPublicKey (first, xbuf, ybuf) {
}

function loadPublicKey (pubkey) {
// length should be validated in interface
const first = pubkey[0]
switch (first) {
case 0x02:
case 0x03:
if (pubkey.length !== 33) return null
// if (pubkey.length !== 33) return null
return loadCompressedPublicKey(first, pubkey.subarray(1, 33))
case 0x04:
case 0x06:
case 0x07:
if (pubkey.length !== 65) return null
// if (pubkey.length !== 65) return null
return loadUncompressedPublicKey(first, pubkey.subarray(1, 33), pubkey.subarray(33, 65))
default:
return null
Expand Down Expand Up @@ -101,7 +102,7 @@ module.exports = {
if (bn.cmp(ecparams.n) >= 0 || bn.isZero()) return 1

bn.imul(new BN(seckey))
if (bn.cmp(ecparams.n)) bn = bn.umod(ecparams.n)
if (bn.cmp(ecparams.n) >= 0) bn = bn.umod(ecparams.n)

const tweaked = bn.toArrayLike(Uint8Array, 'be', 32)
seckey.set(tweaked)
Expand Down
21 changes: 12 additions & 9 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const errors = {
COMPRESSED: 'Expected compressed to be a Boolean',
IMPOSSIBLE_CASE: 'Impossible case. Please create issue.',
TWEAK_ADD:
'The tweak was out of range or the resulted private key is invalid',
Expand All @@ -16,7 +15,7 @@ const errors = {
}

function assert (cond, msg) {
if (!cond) throw new Error(msg || 'Assertion failed')
if (!cond) throw new Error(msg)
}

function isUint8Array (name, value, length) {
Expand All @@ -34,6 +33,10 @@ function isUint8Array (name, value, length) {
}
}

function isCompressed (value) {
assert(toTypeString(value) === 'Boolean', 'Expected compressed to be a Boolean')
}

function getAssertedOutput (output = (len) => new Uint8Array(len), length) {
if (typeof output === 'function') output = output(length)
isUint8Array('output', output, length)
Expand Down Expand Up @@ -108,7 +111,7 @@ module.exports = (secp256k1) => {

publicKeyCreate (seckey, compressed = true, output) {
isUint8Array('private key', seckey, 32)
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyCreate(output, seckey)) {
Expand All @@ -123,7 +126,7 @@ module.exports = (secp256k1) => {

publicKeyConvert (pubkey, compressed = true, output) {
isUint8Array('public key', pubkey, [33, 65])
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyConvert(output, pubkey)) {
Expand All @@ -138,7 +141,7 @@ module.exports = (secp256k1) => {

publicKeyNegate (pubkey, compressed = true, output) {
isUint8Array('public key', pubkey, [33, 65])
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyNegate(output, pubkey)) {
Expand All @@ -159,7 +162,7 @@ module.exports = (secp256k1) => {
for (const pubkey of pubkeys) {
isUint8Array('public key', pubkey, [33, 65])
}
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyCombine(output, pubkeys)) {
Expand All @@ -177,7 +180,7 @@ module.exports = (secp256k1) => {
publicKeyTweakAdd (pubkey, tweak, compressed = true, output) {
isUint8Array('public key', pubkey, [33, 65])
isUint8Array('tweak', tweak, 32)
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyTweakAdd(output, pubkey, tweak)) {
Expand All @@ -193,7 +196,7 @@ module.exports = (secp256k1) => {
publicKeyTweakMul (pubkey, tweak, compressed = true, output) {
isUint8Array('public key', pubkey, [33, 65])
isUint8Array('tweak', tweak, 32)
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.publicKeyTweakMul(output, pubkey, tweak)) {
Expand Down Expand Up @@ -291,7 +294,7 @@ module.exports = (secp256k1) => {
'Expected recovery id to be a Number within interval [0, 3]'
)
isUint8Array('message', msg32, 32)
assert(toTypeString(compressed) === 'Boolean', errors.COMPRESSED)
isCompressed(compressed)
output = getAssertedOutput(output, compressed ? 33 : 65)

switch (secp256k1.ecdsaRecover(output, sig, recid, msg32)) {
Expand Down
62 changes: 51 additions & 11 deletions test/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('return true/false', (t) => {
const message = util.getMessage()
const privateKey = util.getPrivateKey()
const publicKey = util.getPublicKey(privateKey)
const sigObj = util.sign(message, privateKey)

t.true(secp256k1.ecdsaVerify(sigObj.signatureLowS, message, publicKey.compressed), 'true for valid data')

const newMessage = Buffer.from([message[0] ^ 0x01, ...message.slice(1)])
t.false(secp256k1.ecdsaVerify(sigObj.signatureLowS, newMessage, publicKey.compressed), 'false for new message')

const newSignatureR = Buffer.concat([Buffer.alloc(32, 0), sigObj.signatureLowS.slice(32, 64)])
t.false(secp256k1.ecdsaVerify(newSignatureR, message, publicKey.compressed), 'false for invalid signature (zero r)')

const newSignatureS = Buffer.concat([sigObj.signatureLowS.slice(0, 32), Buffer.alloc(32, 0)])
t.false(secp256k1.ecdsaVerify(newSignatureS, message, publicKey.compressed), 'false for invalid signature (zero s)')

t.end()
})

t.end()
})

Expand Down Expand Up @@ -233,19 +253,17 @@ module.exports = (t, secp256k1) => {
})

t.test('arg: invalid recovery', (t) => {
const privateKey = util.getPrivateKey()
const message = util.getMessage()
const sigObj = util.sign(message, privateKey)

t.throws(() => {
const privateKey = util.getPrivateKey()
const message = util.getMessage()
const signature = util.getSignature(message, privateKey)
secp256k1.ecdsaRecover(signature, null, message)
secp256k1.ecdsaRecover(sigObj.signature, null, message)
}, /^Error: Expected recovery id to be a Number within interval \[0, 3]$/, 'should be a Number')

t.throws(() => {
const privateKey = util.getPrivateKey()
const message = util.getMessage()
const signature = util.getSignature(privateKey, message)
secp256k1.ecdsaRecover(signature, 4, message)
}, /^Error: Expected recovery id to be a Number within interval \[0, 3]$/, 'should throw for invalid value')
secp256k1.ecdsaRecover(sigObj.signature, 4, message)
}, /^Error: Expected recovery id to be a Number within interval \[0, 3]$/, 'should throw for recovery outside interval')

t.end()
})
Expand Down Expand Up @@ -309,6 +327,28 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('can not be recovered', (t) => {
const privateKey = util.getPrivateKey()
const message = util.getMessage()
const sigObj = util.sign(message, privateKey)

t.throws(() => {
const newSignatureR = Buffer.concat([Buffer.alloc(32, 0), sigObj.signatureLowS.slice(32, 64)])
secp256k1.ecdsaRecover(newSignatureR, sigObj.recid, message)
}, /^Error: Public key could not be recover$/, 'invalid signature (zero r)')

t.throws(() => {
const newSignatureS = Buffer.concat([sigObj.signatureLowS.slice(0, 32), Buffer.alloc(32, 0)])
secp256k1.ecdsaRecover(newSignatureS, sigObj.recid, message)
}, /^Error: Public key could not be recover$/, 'invalid signature (zero s)')

t.throws(() => {
secp256k1.ecdsaRecover(sigObj.signature, sigObj.recid ^ 0x02, message)
}, /^Error: Public key could not be recover$/, 'invalid recovery id')

t.end()
})

t.end()
})

Expand All @@ -320,8 +360,8 @@ module.exports = (t, secp256k1) => {
const expected = util.sign(message, privateKey)

const sigObj = secp256k1.ecdsaSign(message, privateKey, {}, Buffer.alloc)
t.same(sigObj.ecdsaSignature, expected.ecdsaSignatureLowS)
t.same(sigObj.ecdsaRecovery, expected.ecdsaRecovery)
t.same(sigObj.signature, expected.signatureLowS)
t.same(sigObj.recid, expected.recid)

const isValid = secp256k1.ecdsaVerify(sigObj.signature, message, publicKey.compressed)
t.true(isValid)
Expand Down
16 changes: 16 additions & 0 deletions test/privatekey.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('should be OK without overflow', (t) => {
const privateKey = util.BN_ONE.toArrayLike(Buffer, 'be', 32)
const tweak = util.BN_ONE.toArrayLike(Buffer, 'be', 32)
const result = secp256k1.privateKeyTweakAdd(privateKey, tweak, Buffer.alloc)
t.same(result, new util.BN(2).toArrayLike(Buffer, 'be', 32))
t.end()
})

util.repeat(t, 'random tests', util.env.repeat, (t) => {
const privateKey = util.getPrivateKey()
const tweak = util.getTweak()
Expand Down Expand Up @@ -195,6 +203,14 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('should be OK without overflow', (t) => {
const privateKey = util.BN_ONE.toArrayLike(Buffer, 'be', 32)
const tweak = util.BN_ONE.toArrayLike(Buffer, 'be', 32)
const result = secp256k1.privateKeyTweakMul(privateKey, tweak, Buffer.alloc)
t.same(result, util.BN_ONE.toArrayLike(Buffer, 'be', 32))
t.end()
})

util.repeat(t, 'random tests', util.env.repeat, (t) => {
const privateKey = util.getPrivateKey()
const tweak = util.getTweak()
Expand Down
34 changes: 33 additions & 1 deletion test/publickey.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,38 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('special tests for cover loadPublicKey', (t) => {
const p = util.ec.curve.p.toArray('be', 32)
const one = util.BN_ONE.toArray('be', 32)

t.throws(() => {
const publicKey = Buffer.from([0x02, ...p])
secp256k1.publicKeyConvert(publicKey)
}, /^Error: Public Key could not be parsed$/, 'overflowed compressed key')

t.throws(() => {
const publicKey = Buffer.from([0x04, ...p, ...one])
secp256k1.publicKeyConvert(publicKey)
}, /^Error: Public Key could not be parsed$/, 'overflowed uncompressed key (x part)')

t.throws(() => {
const publicKey = Buffer.from([0x04, ...one, ...p])
secp256k1.publicKeyConvert(publicKey)
}, /^Error: Public Key could not be parsed$/, 'overflowed uncompressed key (y part)')

t.throws(() => {
const privateKey = util.getPrivateKey()
const keys = util.getPublicKey(privateKey)

const publicKey = keys.uncompressed
publicKey[0] = keys.point.y.isEven() ? 0x07 : 0x06

secp256k1.publicKeyConvert(publicKey)
}, /^Error: Public Key could not be parsed$/, 'odd flag for 0x06/0x07')

t.end()
})

util.repeat(t, 'random tests', util.env.repeat, (t) => {
const privateKey = util.getPrivateKey()
const expected = util.getPublicKey(privateKey)
Expand Down Expand Up @@ -205,7 +237,7 @@ module.exports = (t, secp256k1) => {

t.throws(() => {
const publicKey = new Uint8Array(33)
secp256k1.publicKeyCombine([publicKey])
secp256k1.publicKeyNegate(publicKey)
}, /^Error: Public Key could not be parsed$/, 'should throw for invalid public key')

t.end()
Expand Down
11 changes: 11 additions & 0 deletions test/signature.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ module.exports = (t, secp256k1) => {
t.end()
})

t.test('normalize signature (s equal to N/2 + 1)', (t) => {
const signature = Buffer.concat([
util.BN_ONE.toArrayLike(Buffer, 'be', 32),
util.ec.nh.toArrayLike(Buffer, 'be', 32)
])
const signature1 = new util.BN(signature).iaddn(1).toArrayLike(Buffer, 'be', 64)
const result = secp256k1.signatureNormalize(signature1)
t.same(result, signature)
t.end()
})

util.repeat(t, 'random tests', util.env.repeat, (t) => {
const message = util.getMessage()
const privateKey = util.getPrivateKey()
Expand Down

0 comments on commit dd54901

Please sign in to comment.