Merge pull request #151 from dcousens/rfc6979testvectors
RFC6979 Test Vectors
This commit is contained in:
commit
355bc7e227
4 changed files with 140 additions and 92 deletions
83
src/ecdsa.js
83
src/ecdsa.js
|
@ -1,13 +1,11 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var BigInteger = require('./bigi')
|
var crypto = require('crypto')
|
||||||
var ECPointFp = require('./ec').ECPointFp
|
|
||||||
|
|
||||||
var convert = require('./convert')
|
|
||||||
var HmacSHA256 = require('crypto-js/hmac-sha256')
|
|
||||||
|
|
||||||
var sec = require('./sec')
|
var sec = require('./sec')
|
||||||
var ecparams = sec("secp256k1")
|
var ecparams = sec("secp256k1")
|
||||||
|
|
||||||
|
var BigInteger = require('./bigi')
|
||||||
|
var ECPointFp = require('./ec').ECPointFp
|
||||||
|
|
||||||
var P_OVER_FOUR = null
|
var P_OVER_FOUR = null
|
||||||
|
|
||||||
function implShamirsTrick(P, k, Q, l) {
|
function implShamirsTrick(P, k, Q, l) {
|
||||||
|
@ -36,47 +34,56 @@ function implShamirsTrick(P, k, Q, l) {
|
||||||
return R
|
return R
|
||||||
}
|
}
|
||||||
|
|
||||||
function deterministicGenerateK(hash, secret) {
|
|
||||||
assert(Array.isArray(hash), 'hash must be array')
|
|
||||||
assert(Array.isArray(secret), 'secret must be array')
|
|
||||||
|
|
||||||
var vArr = []
|
|
||||||
var kArr = []
|
|
||||||
for (var i = 0;i < 32;i++) vArr.push(1)
|
|
||||||
for (var i = 0;i < 32;i++) kArr.push(0)
|
|
||||||
var v = convert.bytesToWordArray(vArr)
|
|
||||||
var k = convert.bytesToWordArray(kArr)
|
|
||||||
|
|
||||||
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([0]).concat(secret).concat(hash)), k)
|
|
||||||
v = HmacSHA256(v, k)
|
|
||||||
vArr = convert.wordArrayToBytes(v)
|
|
||||||
k = HmacSHA256(convert.bytesToWordArray(vArr.concat([1]).concat(secret).concat(hash)), k)
|
|
||||||
v = HmacSHA256(v,k)
|
|
||||||
v = HmacSHA256(v,k)
|
|
||||||
vArr = convert.wordArrayToBytes(v)
|
|
||||||
return BigInteger.fromBuffer(vArr)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ecdsa = {
|
var ecdsa = {
|
||||||
deterministicGenerateK: deterministicGenerateK,
|
deterministicGenerateK: function(hash, D) {
|
||||||
sign: function (hash, priv) {
|
function HmacSHA256(buffer, secret) {
|
||||||
if (Buffer.isBuffer(hash)) hash = Array.prototype.slice.call(hash)
|
return crypto.createHmac('sha256', secret).update(buffer).digest()
|
||||||
if (Buffer.isBuffer(priv)) priv = Array.prototype.slice.call(priv)
|
}
|
||||||
|
|
||||||
|
assert(Buffer.isBuffer(hash), 'Hash must be a Buffer')
|
||||||
|
assert.equal(hash.length, 32, 'Hash must be 256 bit')
|
||||||
|
assert(D instanceof BigInteger, 'Private key must be a BigInteger')
|
||||||
|
|
||||||
|
var x = D.toBuffer(32)
|
||||||
|
var k = new Buffer(32)
|
||||||
|
var v = new Buffer(32)
|
||||||
|
k.fill(0)
|
||||||
|
v.fill(1)
|
||||||
|
|
||||||
|
k = HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
|
||||||
|
v = HmacSHA256(v, k)
|
||||||
|
|
||||||
|
k = HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
|
||||||
|
v = HmacSHA256(v, k)
|
||||||
|
v = HmacSHA256(v, k)
|
||||||
|
|
||||||
var d = priv
|
|
||||||
var n = ecparams.getN()
|
var n = ecparams.getN()
|
||||||
var e = BigInteger.fromBuffer(hash)
|
var kB = BigInteger.fromBuffer(v).mod(n)
|
||||||
|
assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value')
|
||||||
|
assert(kB.compareTo(ecparams.getN()) < 0, 'Invalid k value')
|
||||||
|
|
||||||
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
|
return kB
|
||||||
|
},
|
||||||
|
|
||||||
|
sign: function (hash, D) {
|
||||||
|
var k = ecdsa.deterministicGenerateK(hash, D)
|
||||||
|
|
||||||
|
var n = ecparams.getN()
|
||||||
var G = ecparams.getG()
|
var G = ecparams.getG()
|
||||||
var Q = G.multiply(k)
|
var Q = G.multiply(k)
|
||||||
|
var e = BigInteger.fromBuffer(hash)
|
||||||
|
|
||||||
var r = Q.getX().toBigInteger().mod(n)
|
var r = Q.getX().toBigInteger().mod(n)
|
||||||
|
assert.notEqual(r.signum(), 0, 'Invalid R value')
|
||||||
|
|
||||||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
|
var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n)
|
||||||
|
assert.notEqual(s.signum(), 0, 'Invalid S value')
|
||||||
|
|
||||||
if (s.compareTo(n.divide(BigInteger.valueOf(2))) > 0) {
|
var N_OVER_TWO = n.divide(BigInteger.valueOf(2))
|
||||||
// Make 's' value 'low', as per https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
|
|
||||||
s = n.subtract(s);
|
// Make 's' value 'low' as per bip62
|
||||||
|
if (s.compareTo(N_OVER_TWO) > 0) {
|
||||||
|
s = n.subtract(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ecdsa.serializeSig(r, s)
|
return ecdsa.serializeSig(r, s)
|
||||||
|
|
|
@ -9,16 +9,18 @@ var ECKey = require('..').ECKey
|
||||||
var ECPubKey = require('..').ECPubKey
|
var ECPubKey = require('..').ECPubKey
|
||||||
var Message = require('..').Message
|
var Message = require('..').Message
|
||||||
|
|
||||||
|
var fixtures = require('./fixtures/ecdsa.js')
|
||||||
|
|
||||||
describe('ecdsa', function() {
|
describe('ecdsa', function() {
|
||||||
// FIXME: needs much better tests than this
|
|
||||||
describe('deterministicGenerateK', function() {
|
describe('deterministicGenerateK', function() {
|
||||||
it('produces deterministic K values', function() {
|
it('matches the test vectors', function() {
|
||||||
var secret = [4]
|
fixtures.forEach(function(f) {
|
||||||
|
var priv = BigInteger.fromHex(f.privateKey)
|
||||||
|
var h1 = crypto.sha256(f.message)
|
||||||
|
|
||||||
var k1 = ecdsa.deterministicGenerateK([1], secret)
|
var k = ecdsa.deterministicGenerateK(h1, priv)
|
||||||
var k2 = ecdsa.deterministicGenerateK([2], secret)
|
assert.deepEqual(k.toHex(), f.k)
|
||||||
|
})
|
||||||
assert.notDeepEqual(k1, k2)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -33,53 +35,40 @@ describe('ecdsa', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sign/verify', function() {
|
describe('sign', function() {
|
||||||
it('Signing and Verifying', function () {
|
it('matches the test vectors', function() {
|
||||||
var s1 = ECKey.makeRandom()
|
fixtures.forEach(function(f) {
|
||||||
var sig_a = s1.sign([0])
|
var priv = ECKey.fromHex(f.privateKey)
|
||||||
|
var hash = crypto.sha256(f.message)
|
||||||
|
var sig = ecdsa.parseSig(priv.sign(hash))
|
||||||
|
|
||||||
assert.ok(sig_a, 'Sign null')
|
assert.equal(sig.r.toHex(), f.signature.slice(0, 64))
|
||||||
assert.ok(s1.pub.verify([0], sig_a))
|
assert.equal(sig.s.toHex(), f.signature.slice(64))
|
||||||
|
})
|
||||||
var message = new Buffer(1024) // More or less random :P
|
|
||||||
var hash = crypto.sha256(message)
|
|
||||||
var sig_b = s1.sign(hash)
|
|
||||||
assert.ok(sig_b, 'Sign random string')
|
|
||||||
assert.ok(s1.pub.verify(hash, sig_b))
|
|
||||||
|
|
||||||
var message2 = new Buffer(
|
|
||||||
'12dce2c169986b3346827ffb2305cf393984627f5f9722a1b1368e933c8d' +
|
|
||||||
'd296653fbe5d7ac031c4962ad0eb1c4298c3b91d244e1116b4a76a130c13' +
|
|
||||||
'1e7aec7fa70184a71a2e66797052831511b93c6e8d72ae58a1980eaacb66' +
|
|
||||||
'8a33f50d7cefb96a5dab897b5efcb99cbafb0d777cb83fc9b2115b69c0fa' +
|
|
||||||
'3d82507b932b84e4', 'hex')
|
|
||||||
|
|
||||||
var hash2 = crypto.sha256(message2)
|
|
||||||
|
|
||||||
var sig_c = new Buffer(
|
|
||||||
'3044022038d9b8dd5c9fbf330565c1f51d72a59ba869aeb2c2001be959d3' +
|
|
||||||
'79e861ec71960220a73945f32cf90d03127d2c3410d16cee120fa1a4b4c3' +
|
|
||||||
'f273ab082801a95506c4', 'hex')
|
|
||||||
|
|
||||||
var s2 = new Buffer(
|
|
||||||
'045a1594316e433fb91f35ef4874610d22177c3f1a1060f6c1e70a609d51' +
|
|
||||||
'b20be5795cd2a5eae0d6b872ba42db95e9afaeea3fbb89e98099575b6828' +
|
|
||||||
'609a978528', 'hex')
|
|
||||||
|
|
||||||
assert.ok(ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should sign with low S value', function() {
|
it('should sign with low S value', function() {
|
||||||
var priv = ECKey.fromHex('ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458')
|
var priv = ECKey.fromHex('ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458')
|
||||||
var message = new Buffer('Vires in numeris')
|
var hash = crypto.sha256('Vires in numeris')
|
||||||
var signature = priv.sign(message)
|
var signature = priv.sign(hash)
|
||||||
var parsed = ecdsa.parseSig(signature)
|
var psig = ecdsa.parseSig(signature)
|
||||||
|
|
||||||
// Check that the 's' value is 'low', to prevent possible transaction malleability as per
|
// See BIP62 for more information
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
|
assert(psig.s.compareTo(ecparams.getN().divide(BigInteger.valueOf(2))) <= 0)
|
||||||
assert.ok(parsed.s.compareTo(ecparams.getN().divide(BigInteger.valueOf(2))) <= 0)
|
})
|
||||||
|
})
|
||||||
|
|
||||||
assert.ok(priv.pub.verify(message, signature))
|
describe('verifyRaw', function() {
|
||||||
|
it('matches the test vectors', function() {
|
||||||
|
fixtures.forEach(function(f) {
|
||||||
|
var priv = ECKey.fromHex(f.privateKey)
|
||||||
|
|
||||||
|
var r = BigInteger.fromHex(f.signature.slice(0, 64))
|
||||||
|
var s = BigInteger.fromHex(f.signature.slice(64))
|
||||||
|
var e = BigInteger.fromBuffer(crypto.sha256(f.message))
|
||||||
|
|
||||||
|
assert(ecdsa.verifyRaw(e, r, s, priv.pub.Q))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
|
|
||||||
|
var crypto = require('..').crypto
|
||||||
var ECKey = require('../src/eckey.js').ECKey
|
var ECKey = require('../src/eckey.js').ECKey
|
||||||
var ECPubKey = require('../src/eckey.js').ECPubKey
|
var ECPubKey = require('../src/eckey.js').ECPubKey
|
||||||
|
|
||||||
|
@ -124,30 +125,30 @@ describe('ECKey', function() {
|
||||||
describe('signing', function() {
|
describe('signing', function() {
|
||||||
var hpriv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458'
|
var hpriv = 'ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458'
|
||||||
var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992'
|
var hcpub = '024b12d9d7c77db68388b6ff7c89046174c871546436806bcd80d07c28ea811992'
|
||||||
var message = new Buffer('Vires in numeris')
|
var hash = crypto.sha256('Vires in numeris')
|
||||||
|
|
||||||
it('should verify against the private key', function() {
|
it('should verify against the private key', function() {
|
||||||
var priv = ECKey.fromHex(hpriv)
|
var priv = ECKey.fromHex(hpriv)
|
||||||
var signature = priv.sign(message)
|
var signature = priv.sign(hash)
|
||||||
|
|
||||||
assert(priv.pub.verify(message, signature))
|
assert(priv.pub.verify(hash, signature))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should verify against the public key', function() {
|
it('should verify against the public key', function() {
|
||||||
var priv = ECKey.fromHex(hpriv)
|
var priv = ECKey.fromHex(hpriv)
|
||||||
var pub = ECPubKey.fromHex(hcpub, true)
|
var pub = ECPubKey.fromHex(hcpub, true)
|
||||||
var signature = priv.sign(message)
|
var signature = priv.sign(hash)
|
||||||
|
|
||||||
assert(pub.verify(message, signature))
|
assert(pub.verify(hash, signature))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not verify against the wrong private key', function() {
|
it('should not verify against the wrong private key', function() {
|
||||||
var priv1 = ECKey.fromHex(hpriv)
|
var priv1 = ECKey.fromHex(hpriv)
|
||||||
var priv2 = ECKey.fromHex('1111111111111111111111111111111111111111111111111111111111111111')
|
var priv2 = ECKey.fromHex('1111111111111111111111111111111111111111111111111111111111111111')
|
||||||
|
|
||||||
var signature = priv1.sign(message)
|
var signature = priv1.sign(hash)
|
||||||
|
|
||||||
assert(!priv2.pub.verify(message, signature))
|
assert(!priv2.pub.verify(hash, signature))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
51
test/fixtures/ecdsa.js
vendored
Normal file
51
test/fixtures/ecdsa.js
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Test Vectors for RFC 6979 ECDSA, secp256k1, SHA-256
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
"privateKey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"k": "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15",
|
||||||
|
"message": "Satoshi Nakamoto",
|
||||||
|
"signature": "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"k": "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3",
|
||||||
|
"message": "All those moments will be lost in time, like tears in rain. Time to die...",
|
||||||
|
"signature": "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140",
|
||||||
|
"k": "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90",
|
||||||
|
"message": "Satoshi Nakamoto",
|
||||||
|
"signature": "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
|
||||||
|
"k": "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1",
|
||||||
|
"message": "Alan Turing",
|
||||||
|
"signature": "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
|
||||||
|
"k": "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d",
|
||||||
|
"message": "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
|
||||||
|
"signature": "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
|
||||||
|
"message": "Everything should be made as simple as possible, but not simpler.",
|
||||||
|
"signature": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||||
|
"k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4",
|
||||||
|
"message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
|
||||||
|
"signature": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privateKey": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||||
|
"k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5",
|
||||||
|
"message": "Not only is the Universe stranger than we think, it is stranger than we can think.",
|
||||||
|
"signature": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in a new issue