From 2114deba95b067e0c954d003bf5be43875ca7f02 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 22 May 2014 13:22:59 +1000 Subject: [PATCH 1/4] message: data-driven tests --- test/fixtures/message.json | 101 ++++++++++++++++++++++++++++++------- test/message.js | 77 +++++++++++----------------- 2 files changed, 113 insertions(+), 65 deletions(-) diff --git a/test/fixtures/message.json b/test/fixtures/message.json index 3db64c9..550609e 100644 --- a/test/fixtures/message.json +++ b/test/fixtures/message.json @@ -1,19 +1,86 @@ { - "magicHash": [ - { - "network": "bitcoin", - "message": "", - "magicHash": "80e795d4a4caadd7047af389d9f7f220562feb6196032e2131e10563352c4bcc" - }, - { - "network": "bitcoin", - "message": "Vires is Numeris", - "magicHash": "f8a5affbef4a3241b19067aa694562f64f513310817297089a8929a930f4f933" - }, - { - "network": "dogecoin", - "message": "Vires is Numeris", - "magicHash": "c0963d20d0accd0ea0df6c1020bf85a7e629a40e7b5363f2c3e9dcafd5638f12" - } - ] + "valid": { + "magicHash": [ + { + "network": "bitcoin", + "message": "", + "magicHash": "80e795d4a4caadd7047af389d9f7f220562feb6196032e2131e10563352c4bcc" + }, + { + "network": "bitcoin", + "message": "Vires is Numeris", + "magicHash": "f8a5affbef4a3241b19067aa694562f64f513310817297089a8929a930f4f933" + }, + { + "network": "dogecoin", + "message": "Vires is Numeris", + "magicHash": "c0963d20d0accd0ea0df6c1020bf85a7e629a40e7b5363f2c3e9dcafd5638f12" + } + ], + "verify": [ + { + "message": "vires is numeris", + "network": "bitcoin", + "address": "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", + "signature": "G8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=", + "compressed": { + "address": "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs", + "signature": "H8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=" + } + }, + { + "message": "vires is numeris", + "network": "dogecoin", + "address": "DFpN6QqFfUm3gKNaxN6tNcab1FArL9cZLE", + "signature": "H6k+dZwJ8oOei3PCSpdj603fDvhlhQ+sqaFNIDvo/bI+Xh6zyIKGzZpyud6YhZ1a5mcrwMVtTWL+VXq/hC5Zj7s=" + } + ], + "signing": [ + { + "description": "gives equal r, s values irrespective of point compression", + "message": "vires is numeris", + "network": "bitcoin", + "D": "1", + "signature": "HF8nHqFr3K2UKYahhX3soVeoW8W1ECNbr0wfck7lzyXjCS5Q16Ek45zyBuy1Fiy9sTPKVgsqqOuPvbycuVSSVl8=", + "compressed": { + "signature": "IF8nHqFr3K2UKYahhX3soVeoW8W1ECNbr0wfck7lzyXjCS5Q16Ek45zyBuy1Fiy9sTPKVgsqqOuPvbycuVSSVl8=" + } + }, + { + "description": "supports alternative networks", + "message": "vires is numeris", + "network": "dogecoin", + "D": "1", + "signature": "G6k+dZwJ8oOei3PCSpdj603fDvhlhQ+sqaFNIDvo/bI+Xh6zyIKGzZpyud6YhZ1a5mcrwMVtTWL+VXq/hC5Zj7s=" + } + ] + }, + "invalid": { + "verify": [ + { + "description": "will fail for the wrong message", + "message": "foobar", + "address": "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", + "signature": "G8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=" + }, + { + "description": "will fail for the wrong address", + "message": "vires is numeris", + "address": "1111111111111111111114oLvT2", + "signature": "H8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=" + }, + { + "description": "does not cross verify (uncompressed address, compressed signature)", + "message": "vires is numeris", + "address": "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", + "signature": "H8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=" + }, + { + "description": "does not cross verify (compressed address, uncompressed signature)", + "message": "vires is numeris", + "address": "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs", + "signature": "G8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=" + } + ] + } } diff --git a/test/message.js b/test/message.js index f819b8f..5ad63bf 100644 --- a/test/message.js +++ b/test/message.js @@ -8,15 +8,9 @@ var Message = require('../src/message') var fixtures = require('./fixtures/message.json') describe('Message', function() { - var message - - beforeEach(function() { - message = 'vires is numeris' - }) - describe('magicHash', function() { it('matches the test vectors', function() { - fixtures.magicHash.forEach(function(f) { + fixtures.valid.magicHash.forEach(function(f) { var network = networks[f.network] var actual = Message.magicHash(f.message, network) @@ -26,58 +20,45 @@ describe('Message', function() { }) describe('verify', function() { - var addr, sig, caddr, csig + it('verifies a valid signature', function() { + fixtures.valid.verify.forEach(function(f) { + var network = networks[f.network] - beforeEach(function() { - addr = '16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM' // uncompressed - caddr = '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs' // compressed + var s1 = new Buffer(f.signature, 'base64') + assert.ok(Message.verify(f.address, s1, f.message, network)) - sig = new Buffer('G8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=', 'base64') - csig = new Buffer('H8JawPtQOrybrSP1WHQnQPr67B9S3qrxBrl1mlzoTJOSHEpmnF7D3+t+LX0Xei9J20B5AIdPbeL3AaTBZ4N3bY0=', 'base64') + if (f.compressed) { + var s2 = new Buffer(f.compressed.signature, 'base64') + + assert.ok(Message.verify(f.compressed.address, s2, f.message, network)) + } + }) }) - it('can verify a signed message', function() { - assert.ok(Message.verify(addr, sig, message)) - }) - - it('will fail for the wrong message', function() { - assert.ok(!Message.verify(addr, sig, 'foobar')) - }) - - it('will fail for the wrong address', function() { - assert.ok(!Message.verify('1MsHWS1BnwMc3tLE8G35UXsS58fKipzB7a', sig, message)) - }) - - it('does not cross verify (compressed/uncompressed)', function() { - assert.ok(!Message.verify(addr, csig, message)) - assert.ok(!Message.verify(caddr, sig, message)) - }) - - it('supports alternate networks', function() { - var dogeaddr = 'DFpN6QqFfUm3gKNaxN6tNcab1FArL9cZLE' - var dogesig = new Buffer('H6k+dZwJ8oOei3PCSpdj603fDvhlhQ+sqaFNIDvo/bI+Xh6zyIKGzZpyud6YhZ1a5mcrwMVtTWL+VXq/hC5Zj7s=', 'base64') - - assert.ok(Message.verify(dogeaddr, dogesig, message, networks.dogecoin)) + fixtures.invalid.verify.forEach(function(f) { + it(f.description, function() { + var signature = new Buffer(f.signature, 'base64') + assert.ok(!Message.verify(f.address, signature, f.message)) + }) }) }) describe('signing', function() { - it('gives matching signatures irrespective of point compression', function() { - var privKey = new ECKey(BigInteger.ONE, false) - var compressedKey = new ECKey(privKey.D, true) + fixtures.valid.signing.forEach(function(f) { + it(f.description, function() { + var network = networks[f.network] - var sig = Message.sign(privKey, message) - var csig = Message.sign(compressedKey, message) + var k1 = new ECKey(new BigInteger(f.D), false) + var s1 = Message.sign(k1, f.message, network) + assert.equal(s1.toString('base64'), f.signature) - assert.notDeepEqual(sig.slice(0, 2), csig.slice(0, 2)) // unequal compression flags - assert.deepEqual(sig.slice(2), csig.slice(2)) // equal signatures - }) + if (f.compressed) { + var k2 = new ECKey(new BigInteger(f.D)) + var s2 = Message.sign(k2, f.message) - it('supports alternate networks', function() { - var privKey = new ECKey(BigInteger.ONE) - var signature = Message.sign(privKey, message, networks.dogecoin) - - assert.equal(signature.toString('base64'), 'H6k+dZwJ8oOei3PCSpdj603fDvhlhQ+sqaFNIDvo/bI+Xh6zyIKGzZpyud6YhZ1a5mcrwMVtTWL+VXq/hC5Zj7s=') + assert.equal(s2.toString('base64'), f.compressed.signature) + } + }) }) }) }) From 1d6b1fe58bd6d15ab18ff3398e1336f5afe784e4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 24 May 2014 13:54:46 +1000 Subject: [PATCH 2/4] message: better variable names --- test/message.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/message.js b/test/message.js index 5ad63bf..8a798ee 100644 --- a/test/message.js +++ b/test/message.js @@ -24,13 +24,13 @@ describe('Message', function() { fixtures.valid.verify.forEach(function(f) { var network = networks[f.network] - var s1 = new Buffer(f.signature, 'base64') - assert.ok(Message.verify(f.address, s1, f.message, network)) + var signature = new Buffer(f.signature, 'base64') + assert.ok(Message.verify(f.address, signature, f.message, network)) if (f.compressed) { - var s2 = new Buffer(f.compressed.signature, 'base64') + var compressedSignature = new Buffer(f.compressed.signature, 'base64') - assert.ok(Message.verify(f.compressed.address, s2, f.message, network)) + assert.ok(Message.verify(f.compressed.address, compressedSignature, f.message, network)) } }) }) @@ -48,15 +48,15 @@ describe('Message', function() { it(f.description, function() { var network = networks[f.network] - var k1 = new ECKey(new BigInteger(f.D), false) - var s1 = Message.sign(k1, f.message, network) - assert.equal(s1.toString('base64'), f.signature) + var privKey = new ECKey(new BigInteger(f.D), false) + var signature = Message.sign(privKey, f.message, network) + assert.equal(signature.toString('base64'), f.signature) if (f.compressed) { - var k2 = new ECKey(new BigInteger(f.D)) - var s2 = Message.sign(k2, f.message) + var compressedPrivKey = new ECKey(new BigInteger(f.D)) + var compressedSignature = Message.sign(compressedPrivKey, f.message) - assert.equal(s2.toString('base64'), f.compressed.signature) + assert.equal(compressedSignature.toString('base64'), f.compressed.signature) } }) }) From 37d5147cacbe30b3db6fb3591e5eb499f804d2cb Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 24 May 2014 13:40:20 +1000 Subject: [PATCH 3/4] ecdsa: add invalid tests for verifyRaw --- test/ecdsa.js | 14 +++++++++++- test/fixtures/ecdsa.json | 49 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/test/ecdsa.js b/test/ecdsa.js index 8e53881..cbe1fd3 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -62,7 +62,7 @@ describe('ecdsa', function() { }) describe('verifyRaw', function() { - it('matches the test vectors', function() { + it('verifies valid signatures', function() { fixtures.valid.forEach(function(f) { var D = BigInteger.fromHex(f.D) var Q = ecparams.getG().multiply(D) @@ -74,6 +74,18 @@ describe('ecdsa', function() { assert(ecdsa.verifyRaw(ecparams, e, r, s, Q)) }) }) + + fixtures.invalid.verifyRaw.forEach(function(f) { + it('fails to verify with ' + f.description, function() { + var D = BigInteger.fromHex(f.D) + var e = BigInteger.fromHex(f.e) + var r = new BigInteger(f.signature.r) + var s = new BigInteger(f.signature.s) + var Q = ecparams.getG().multiply(D) + + assert.equal(ecdsa.verifyRaw(ecparams, e, r, s, Q), false) + }) + }) }) describe('serializeSig', function() { diff --git a/test/fixtures/ecdsa.json b/test/fixtures/ecdsa.json index d8b7f60..f3ce488 100644 --- a/test/fixtures/ecdsa.json +++ b/test/fixtures/ecdsa.json @@ -1,7 +1,7 @@ { "valid": [ { - "D": "0000000000000000000000000000000000000000000000000000000000000001", + "D": "01", "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", "message": "Everything should be made as simple as possible, but not simpler.", "compact": "1f33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", @@ -132,6 +132,53 @@ "description": "Sequence too long", "hex": "30080304ffffffff0304ffffffffffffff" } + ], + "verifyRaw": [ + { + "description": "The wrong signature", + "D": "01", + "e": "06ef2b193b83b3d701f765f1db34672ab84897e1252343cc2197829af3a30456", + "signature": { + "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", + "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" + } + }, + { + "description": "Invalid r value (== 0)", + "D": "01", + "e": "01", + "signature": { + "r": "00", + "s": "02" + } + }, + { + "description": "Invalid r value (>= n)", + "D": "01", + "e": "01", + "signature": { + "r": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "s": "02" + } + }, + { + "description": "Invalid s value (== 0)", + "D": "01", + "e": "01", + "signature": { + "r": "02", + "s": "00" + } + }, + { + "description": "Invalid s value (>= n)", + "D": "01", + "e": "01", + "signature": { + "r": "02", + "s": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" + } + } ] } } From bd1a08bfa1925b5390359e16d62c1a7965706e7f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 24 May 2014 14:33:02 +1000 Subject: [PATCH 4/4] ecdsa: use signum() over compareTo --- src/ecdsa.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ecdsa.js b/src/ecdsa.js index 7d73a6d..4fd95b3 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -64,15 +64,16 @@ function verifyRaw(ecparams, e, r, s, Q) { var n = ecparams.getN() var G = ecparams.getG() - if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) { + if (r.signum() === 0 || r.compareTo(n) >= 0) { return false } - if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) { + if (s.signum() === 0 || s.compareTo(n) >= 0) { return false } var c = s.modInverse(n) + var u1 = e.multiply(c).mod(n) var u2 = r.multiply(c).mod(n)