ecdsa: moved all signature encoding to ECSignature
This commit is contained in:
parent
c5252fc509
commit
eb3d9a25f7
9 changed files with 350 additions and 297 deletions
105
src/ecdsa.js
105
src/ecdsa.js
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -269,9 +174,5 @@ module.exports = {
|
|||
recoverPubKey: recoverPubKey,
|
||||
sign: sign,
|
||||
verify: verify,
|
||||
verifyRaw: verifyRaw,
|
||||
serializeSig: serializeSig,
|
||||
parseSig: parseSig,
|
||||
serializeSigCompact: serializeSigCompact,
|
||||
parseSigCompact: parseSigCompact
|
||||
verifyRaw: verifyRaw
|
||||
}
|
||||
|
|
102
src/ecsignature.js
Normal file
102
src/ecsignature.js
Normal file
|
@ -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.fromCompact = 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.fromScriptSignature = 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
|
|
@ -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'),
|
||||
|
|
|
@ -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')
|
||||
|
@ -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.fromCompact(signatureBuffer)
|
||||
var e = BigInteger.fromBuffer(hash)
|
||||
var Q = ecdsa.recoverPubKey(ecparams, e, parsed.signature, parsed.i)
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
var assert = require('assert')
|
||||
var bufferutils = require('./bufferutils')
|
||||
var crypto = require('./crypto')
|
||||
var ecdsa = require('./ecdsa')
|
||||
var opcodes = require('./opcodes')
|
||||
var scripts = require('./scripts')
|
||||
|
||||
var Address = require('./address')
|
||||
var ECKey = require('./eckey')
|
||||
var ECSignature = require('./ecsignature')
|
||||
var Script = require('./script')
|
||||
|
||||
var DEFAULT_SEQUENCE = 0xffffffff
|
||||
|
@ -301,16 +301,11 @@ Transaction.prototype.sign = function(index, privKey, hashType) {
|
|||
|
||||
Transaction.prototype.signInput = function(index, prevOutScript, privKey, hashType) {
|
||||
hashType = hashType || SIGHASH_ALL
|
||||
assert(privKey instanceof ECKey, 'Expected ECKey, got ' + privKey)
|
||||
|
||||
var hash = this.hashForSignature(prevOutScript, index, hashType)
|
||||
var signature = privKey.sign(hash)
|
||||
var DERencoded = ecdsa.serializeSig(signature)
|
||||
|
||||
return Buffer.concat([
|
||||
new Buffer(DERencoded),
|
||||
new Buffer([hashType])
|
||||
])
|
||||
return signature.toScriptSignature(hashType)
|
||||
}
|
||||
|
||||
Transaction.prototype.setInputScript = function(index, script) {
|
||||
|
@ -318,14 +313,11 @@ Transaction.prototype.setInputScript = function(index, script) {
|
|||
}
|
||||
|
||||
// FIXME: could be validateInput(index, prevTxOut, pub)
|
||||
Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, DERsig) {
|
||||
var type = DERsig.readUInt8(DERsig.length - 1)
|
||||
DERsig = DERsig.slice(0, -1)
|
||||
Transaction.prototype.validateInput = function(index, prevOutScript, pubKey, buffer) {
|
||||
var parsed = ECSignature.fromScriptSignature(buffer)
|
||||
var hash = this.hashForSignature(prevOutScript, index, parsed.hashType)
|
||||
|
||||
var hash = this.hashForSignature(prevOutScript, index, type)
|
||||
var signature = ecdsa.parseSig(DERsig)
|
||||
|
||||
return pubKey.verify(hash, signature)
|
||||
return pubKey.verify(hash, parsed.signature)
|
||||
}
|
||||
|
||||
module.exports = Transaction
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue