Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"dependencies": {
"bigi": "1.1.0",
"crypto-js": "3.1.2-3",
"ecurve": "0.7.0",
"ecurve": "0.9.0",
"secure-random": "0.2.1"
}
}
119 changes: 10 additions & 109 deletions src/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var assert = require('assert')
var crypto = require('./crypto')

var BigInteger = require('bigi')
var ECSignature = require('./ecsignature')
var Point = require('ecurve').Point

function deterministicGenerateK(curve, hash, d) {
Expand All @@ -22,7 +23,7 @@ function deterministicGenerateK(curve, hash, d) {
v = crypto.HmacSHA256(v, k)
v = crypto.HmacSHA256(v, k)

var n = curve.params.n
var n = curve.n
var kB = BigInteger.fromBuffer(v).mod(n)
assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value')
assert(kB.compareTo(n) < 0, 'Invalid k value')
Expand All @@ -33,8 +34,8 @@ function deterministicGenerateK(curve, hash, d) {
function sign(curve, hash, d) {
var k = deterministicGenerateK(curve, hash, d)

var n = curve.params.n
var G = curve.params.G
var n = curve.n
var G = curve.G
var Q = G.multiply(k)
var e = BigInteger.fromBuffer(hash)

Expand All @@ -51,7 +52,7 @@ function sign(curve, hash, d) {
s = n.subtract(s)
}

return {r: r, s: s}
return new ECSignature(r, s)
}

function verify(curve, hash, signature, Q) {
Expand All @@ -61,8 +62,8 @@ function verify(curve, hash, signature, Q) {
}

function verifyRaw(curve, e, signature, Q) {
var n = curve.params.n
var G = curve.params.G
var n = curve.n
var G = curve.G

var r = signature.r
var s = signature.s
Expand All @@ -81,102 +82,6 @@ function verifyRaw(curve, e, signature, Q) {
return v.equals(r)
}

/**
* Serialize a signature into DER format.
*
* Takes two BigIntegers representing r and s and returns a byte array.
*/
function serializeSig(signature) {
var rBa = signature.r.toDERInteger()
var sBa = signature.s.toDERInteger()

var sequence = []
sequence.push(0x02) // INTEGER
sequence.push(rBa.length)
sequence = sequence.concat(rBa)

sequence.push(0x02) // INTEGER
sequence.push(sBa.length)
sequence = sequence.concat(sBa)

sequence.unshift(sequence.length)
sequence.unshift(0x30) // SEQUENCE

return new Buffer(sequence)
}

/**
* Parses a buffer containing a DER-encoded signature.
*
* This function will return an object of the form:
*
* {
* r: BigInteger,
* s: BigInteger
* }
*/
function parseSig(buffer) {
assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')

assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer')
var rLen = buffer.readUInt8(3)
var rB = buffer.slice(4, 4 + rLen)

var offset = 4 + rLen
assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)')
var sLen = buffer.readUInt8(1 + offset)
var sB = buffer.slice(2 + offset)
offset += 2 + sLen

assert.equal(offset, buffer.length, 'Invalid DER encoding')

return {
r: BigInteger.fromDERInteger(rB),
s: BigInteger.fromDERInteger(sB)
}
}

function serializeSigCompact(signature, i, compressed) {
if (compressed) {
i += 4
}

i += 27

var buffer = new Buffer(65)
buffer.writeUInt8(i, 0)

signature.r.toBuffer(32).copy(buffer, 1)
signature.s.toBuffer(32).copy(buffer, 33)

return buffer
}

function parseSigCompact(buffer) {
assert.equal(buffer.length, 65, 'Invalid signature length')
var i = buffer.readUInt8(0) - 27

// At most 3 bits
assert.equal(i, i & 7, 'Invalid signature parameter')
var compressed = !!(i & 4)

// Recovery param only
i = i & 3

var r = BigInteger.fromBuffer(buffer.slice(1, 33))
var s = BigInteger.fromBuffer(buffer.slice(33))

return {
signature: {
r: r,
s: s
},
i: i,
compressed: compressed
}
}

/**
* Recover a public key from a signature.
*
Expand All @@ -199,8 +104,8 @@ function recoverPubKey(curve, e, signature, i) {
// first or second candidate key.
var isSecondKey = i >> 1

var n = curve.params.n
var G = curve.params.G
var n = curve.n
var G = curve.G
var p = curve.p
var a = curve.a
var b = curve.b
Expand Down Expand Up @@ -269,9 +174,5 @@ module.exports = {
recoverPubKey: recoverPubKey,
sign: sign,
verify: verify,
verifyRaw: verifyRaw,
serializeSig: serializeSig,
parseSig: parseSig,
serializeSigCompact: serializeSigCompact,
parseSigCompact: parseSigCompact
verifyRaw: verifyRaw
}
6 changes: 3 additions & 3 deletions src/eckey.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ var curve = ecurve.getCurveByName('secp256k1')

function ECKey(d, compressed) {
assert(d.signum() > 0, 'Private key must be greater than 0')
assert(d.compareTo(curve.params.n) < 0, 'Private key must be less than the curve order')
assert(d.compareTo(curve.n) < 0, 'Private key must be less than the curve order')

var Q = curve.params.G.multiply(d)
var Q = curve.G.multiply(d)

this.d = d
this.pub = new ECPubKey(Q, compressed)
Expand Down Expand Up @@ -47,7 +47,7 @@ ECKey.makeRandom = function(compressed, rng) {

var buffer = new Buffer(rng(32))
var d = BigInteger.fromBuffer(buffer)
d = d.mod(curve.params.n)
d = d.mod(curve.n)

return new ECKey(d, compressed)
}
Expand Down
102 changes: 102 additions & 0 deletions src/ecsignature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
var assert = require('assert')

var BigInteger = require('bigi')

function ECSignature(r, s) {
assert(r instanceof BigInteger, 'Expected BigInteger, got ' + r)
assert(s instanceof BigInteger, 'Expected BigInteger, got ' + s)
this.r = r
this.s = s
}

// Import operations
ECSignature.parseCompact = function(buffer) {
assert.equal(buffer.length, 65, 'Invalid signature length')
var i = buffer.readUInt8(0) - 27

// At most 3 bits
assert.equal(i, i & 7, 'Invalid signature parameter')
var compressed = !!(i & 4)

// Recovery param only
i = i & 3

var r = BigInteger.fromBuffer(buffer.slice(1, 33))
var s = BigInteger.fromBuffer(buffer.slice(33))

return {
compressed: compressed,
i: i,
signature: new ECSignature(r, s)
}
}

ECSignature.fromDER = function(buffer) {
assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')

assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer')
var rLen = buffer.readUInt8(3)
var rB = buffer.slice(4, 4 + rLen)

var offset = 4 + rLen
assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)')
var sLen = buffer.readUInt8(1 + offset)
var sB = buffer.slice(2 + offset)
offset += 2 + sLen

assert.equal(offset, buffer.length, 'Invalid DER encoding')
var r = BigInteger.fromDERInteger(rB)
var s = BigInteger.fromDERInteger(sB)

return new ECSignature(r, s)
}

ECSignature.parseScriptSignature = function(buffer) {
return {
signature: ECSignature.fromDER(buffer.slice(0, -1)),
hashType: buffer.readUInt8(buffer.length - 1)
}
}

// Export operations
ECSignature.prototype.toCompact = function(i, compressed) {
if (compressed) i += 4
i += 27

var buffer = new Buffer(65)
buffer.writeUInt8(i, 0)

this.r.toBuffer(32).copy(buffer, 1)
this.s.toBuffer(32).copy(buffer, 33)

return buffer
}

ECSignature.prototype.toDER = function() {
var rBa = this.r.toDERInteger()
var sBa = this.s.toDERInteger()

var sequence = []
sequence.push(0x02) // INTEGER
sequence.push(rBa.length)
sequence = sequence.concat(rBa)

sequence.push(0x02) // INTEGER
sequence.push(sBa.length)
sequence = sequence.concat(sBa)

sequence.unshift(sequence.length)
sequence.unshift(0x30) // SEQUENCE

return new Buffer(sequence)
}

ECSignature.prototype.toScriptSignature = function(hashType) {
var hashTypeBuffer = new Buffer(1)
hashTypeBuffer.writeUInt8(hashType, 0)

return Buffer.concat([this.toDER(), hashTypeBuffer])
}

module.exports = ECSignature
6 changes: 3 additions & 3 deletions src/hdnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,15 @@ HDNode.prototype.derive = function(index) {
var pIL = BigInteger.fromBuffer(IL)

// In case parse256(IL) >= n, proceed with the next value for i
if (pIL.compareTo(curve.params.n) >= 0) {
if (pIL.compareTo(curve.n) >= 0) {
return this.derive(index + 1)
}

// Private parent key -> private child key
var hd
if (this.privKey) {
// ki = parse256(IL) + kpar (mod n)
var ki = pIL.add(this.privKey.d).mod(curve.params.n)
var ki = pIL.add(this.privKey.d).mod(curve.n)

// In case ki == 0, proceed with the next value for i
if (ki.signum() === 0) {
Expand All @@ -243,7 +243,7 @@ HDNode.prototype.derive = function(index) {
} else {
// Ki = point(parse256(IL)) + Kpar
// = G*IL + Kpar
var Ki = curve.params.G.multiply(pIL).add(this.pubKey.Q)
var Ki = curve.G.multiply(pIL).add(this.pubKey.Q)

// In case Ki is the point at infinity, proceed with the next value for i
if (curve.isInfinity(Ki)) {
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
ecdsa: require('./ecdsa'),
ECKey: require('./eckey'),
ECPubKey: require('./ecpubkey'),
ECSignature: require('./ecsignature'),
Message: require('./message'),
opcodes: require('./opcodes'),
HDNode: require('./hdnode'),
Expand Down
17 changes: 8 additions & 9 deletions src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var networks = require('./networks')

var Address = require('./address')
var ECPubKey = require('./ecpubkey')
var ECSignature = require('./ecsignature')

var ecurve = require('ecurve')
var ecparams = ecurve.getCurveByName('secp256k1')
Expand All @@ -18,32 +19,30 @@ function magicHash(message, network) {
var lengthBuffer = new Buffer(bufferutils.varIntSize(messageBuffer.length))
bufferutils.writeVarInt(lengthBuffer, messageBuffer.length, 0)

var buffer = Buffer.concat([
magicPrefix, lengthBuffer, messageBuffer
])
var buffer = Buffer.concat([magicPrefix, lengthBuffer, messageBuffer])
return crypto.hash256(buffer)
}

function sign(key, message, network) {
function sign(privKey, message, network) {
network = network || networks.bitcoin

var hash = magicHash(message, network)
var signature = key.sign(hash)
var signature = privKey.sign(hash)
var e = BigInteger.fromBuffer(hash)
var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, key.pub.Q)
var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, privKey.pub.Q)

return ecdsa.serializeSigCompact(signature, i, key.pub.compressed)
return signature.toCompact(i, privKey.pub.compressed)
}

// TODO: network could be implied from address
function verify(address, compactSig, message, network) {
function verify(address, signatureBuffer, message, network) {
if (address instanceof Address) {
address = address.toString()
}
network = network || networks.bitcoin

var hash = magicHash(message, network)
var parsed = ecdsa.parseSigCompact(compactSig)
var parsed = ECSignature.parseCompact(signatureBuffer)
var e = BigInteger.fromBuffer(hash)
var Q = ecdsa.recoverPubKey(ecparams, e, parsed.signature, parsed.i)

Expand Down
Loading