rm ecdsa, add new ECPair using secp256k1
This commit is contained in:
parent
49422672cf
commit
fba0699dd3
18 changed files with 227 additions and 576 deletions
163
src/ecdsa.js
163
src/ecdsa.js
|
@ -1,163 +0,0 @@
|
|||
var Buffer = require('safe-buffer').Buffer
|
||||
var createHmac = require('create-hmac')
|
||||
var typeforce = require('typeforce')
|
||||
var types = require('./types')
|
||||
|
||||
var BigInteger = require('bigi')
|
||||
|
||||
var ZERO = Buffer.alloc(1, 0)
|
||||
var ONE = Buffer.alloc(1, 1)
|
||||
|
||||
var ecurve = require('ecurve')
|
||||
var secp256k1 = ecurve.getCurveByName('secp256k1')
|
||||
|
||||
// https://tools.ietf.org/html/rfc6979#section-3.2
|
||||
function deterministicGenerateK (hash, x, checkSig) {
|
||||
typeforce(types.tuple(
|
||||
types.Hash256bit,
|
||||
types.Buffer256bit,
|
||||
types.Function
|
||||
), arguments)
|
||||
|
||||
// Step A, ignored as hash already provided
|
||||
// Step B
|
||||
// Step C
|
||||
var k = Buffer.alloc(32, 0)
|
||||
var v = Buffer.alloc(32, 1)
|
||||
|
||||
// Step D
|
||||
k = createHmac('sha256', k)
|
||||
.update(v)
|
||||
.update(ZERO)
|
||||
.update(x)
|
||||
.update(hash)
|
||||
.digest()
|
||||
|
||||
// Step E
|
||||
v = createHmac('sha256', k).update(v).digest()
|
||||
|
||||
// Step F
|
||||
k = createHmac('sha256', k)
|
||||
.update(v)
|
||||
.update(ONE)
|
||||
.update(x)
|
||||
.update(hash)
|
||||
.digest()
|
||||
|
||||
// Step G
|
||||
v = createHmac('sha256', k).update(v).digest()
|
||||
|
||||
// Step H1/H2a, ignored as tlen === qlen (256 bit)
|
||||
// Step H2b
|
||||
v = createHmac('sha256', k).update(v).digest()
|
||||
|
||||
var T = BigInteger.fromBuffer(v)
|
||||
|
||||
// Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA
|
||||
while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) {
|
||||
k = createHmac('sha256', k)
|
||||
.update(v)
|
||||
.update(ZERO)
|
||||
.digest()
|
||||
|
||||
v = createHmac('sha256', k).update(v).digest()
|
||||
|
||||
// Step H1/H2a, again, ignored as tlen === qlen (256 bit)
|
||||
// Step H2b again
|
||||
v = createHmac('sha256', k).update(v).digest()
|
||||
T = BigInteger.fromBuffer(v)
|
||||
}
|
||||
|
||||
return T
|
||||
}
|
||||
|
||||
var N_OVER_TWO = secp256k1.n.shiftRight(1)
|
||||
|
||||
function sign (hash, d) {
|
||||
typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments)
|
||||
|
||||
var x = d.toBuffer(32)
|
||||
var e = BigInteger.fromBuffer(hash)
|
||||
var n = secp256k1.n
|
||||
var G = secp256k1.G
|
||||
|
||||
var r, s
|
||||
deterministicGenerateK(hash, x, function (k) {
|
||||
var Q = G.multiply(k)
|
||||
|
||||
if (secp256k1.isInfinity(Q)) return false
|
||||
|
||||
r = Q.affineX.mod(n)
|
||||
if (r.signum() === 0) return false
|
||||
|
||||
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
|
||||
if (s.signum() === 0) return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
// enforce low S values, see bip62: 'low s values in signatures'
|
||||
if (s.compareTo(N_OVER_TWO) > 0) {
|
||||
s = n.subtract(s)
|
||||
}
|
||||
|
||||
return {
|
||||
r: r,
|
||||
s: s
|
||||
}
|
||||
}
|
||||
|
||||
function verify (hash, signature, Q) {
|
||||
typeforce(types.tuple(
|
||||
types.Hash256bit,
|
||||
types.ECSignature,
|
||||
types.ECPoint
|
||||
), arguments)
|
||||
|
||||
var n = secp256k1.n
|
||||
var G = secp256k1.G
|
||||
|
||||
var r = signature.r
|
||||
var s = signature.s
|
||||
|
||||
// 1.4.1 Enforce r and s are both integers in the interval [1, n − 1]
|
||||
if (r.signum() <= 0 || r.compareTo(n) >= 0) return false
|
||||
if (s.signum() <= 0 || s.compareTo(n) >= 0) return false
|
||||
|
||||
// 1.4.2 H = Hash(M), already done by the user
|
||||
// 1.4.3 e = H
|
||||
var e = BigInteger.fromBuffer(hash)
|
||||
|
||||
// Compute s^-1
|
||||
var sInv = s.modInverse(n)
|
||||
|
||||
// 1.4.4 Compute u1 = es^−1 mod n
|
||||
// u2 = rs^−1 mod n
|
||||
var u1 = e.multiply(sInv).mod(n)
|
||||
var u2 = r.multiply(sInv).mod(n)
|
||||
|
||||
// 1.4.5 Compute R = (xR, yR)
|
||||
// R = u1G + u2Q
|
||||
var R = G.multiplyTwo(u1, Q, u2)
|
||||
|
||||
// 1.4.5 (cont.) Enforce R is not at infinity
|
||||
if (secp256k1.isInfinity(R)) return false
|
||||
|
||||
// 1.4.6 Convert the field element R.x to an integer
|
||||
var xR = R.affineX
|
||||
|
||||
// 1.4.7 Set v = xR mod n
|
||||
var v = xR.mod(n)
|
||||
|
||||
// 1.4.8 If v = r, output "valid", and if v != r, output "invalid"
|
||||
return v.equals(r)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
deterministicGenerateK: deterministicGenerateK,
|
||||
sign: sign,
|
||||
verify: verify,
|
||||
|
||||
// TODO: remove
|
||||
__curve: secp256k1
|
||||
}
|
153
src/ecpair.js
153
src/ecpair.js
|
@ -1,63 +1,73 @@
|
|||
let ecdsa = require('./ecdsa')
|
||||
let ecc = require('tiny-secp256k1')
|
||||
let randomBytes = require('randombytes')
|
||||
let typeforce = require('typeforce')
|
||||
let types = require('./types')
|
||||
let wif = require('wif')
|
||||
|
||||
let NETWORKS = require('./networks')
|
||||
let BigInteger = require('bigi')
|
||||
|
||||
let ecurve = require('ecurve')
|
||||
let secp256k1 = ecdsa.__curve
|
||||
// TODO: why is the function name toJSON weird?
|
||||
function isPoint (x) { return ecc.isPoint(x) }
|
||||
let isOptions = typeforce.maybe(typeforce.compile({
|
||||
compressed: types.maybe(types.Boolean),
|
||||
network: types.maybe(types.Network)
|
||||
}))
|
||||
|
||||
function ECPair (d, Q, options) {
|
||||
if (options) {
|
||||
typeforce({
|
||||
compressed: types.maybe(types.Boolean),
|
||||
network: types.maybe(types.Network)
|
||||
}, options)
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
|
||||
if (d) {
|
||||
if (d.signum() <= 0) throw new Error('Private key must be greater than 0')
|
||||
if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order')
|
||||
if (Q) throw new TypeError('Unexpected publicKey parameter')
|
||||
|
||||
this.d = d
|
||||
} else {
|
||||
typeforce(types.ECPoint, Q)
|
||||
|
||||
this.__Q = Q
|
||||
}
|
||||
|
||||
this.compressed = options.compressed === undefined ? true : options.compressed
|
||||
this.network = options.network || NETWORKS.bitcoin
|
||||
|
||||
this.__d = d || null
|
||||
this.__Q = null
|
||||
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed)
|
||||
}
|
||||
|
||||
Object.defineProperty(ECPair.prototype, 'Q', {
|
||||
get: function () {
|
||||
if (!this.__Q && this.d) {
|
||||
this.__Q = secp256k1.G.multiply(this.d)
|
||||
}
|
||||
|
||||
return this.__Q
|
||||
}
|
||||
})
|
||||
|
||||
ECPair.fromPublicKeyBuffer = function (buffer, network) {
|
||||
var Q = ecurve.Point.decodeFrom(secp256k1, buffer)
|
||||
|
||||
return new ECPair(null, Q, {
|
||||
compressed: Q.compressed,
|
||||
network: network
|
||||
})
|
||||
ECPair.prototype.getNetwork = function () {
|
||||
return this.network
|
||||
}
|
||||
|
||||
ECPair.fromWIF = function (string, network) {
|
||||
var decoded = wif.decode(string)
|
||||
var version = decoded.version
|
||||
ECPair.prototype.getPrivateKey = function () {
|
||||
return this.__d
|
||||
}
|
||||
|
||||
ECPair.prototype.getPublicKey = function () {
|
||||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed)
|
||||
return this.__Q
|
||||
}
|
||||
|
||||
ECPair.prototype.toWIF = function () {
|
||||
if (!this.__d) throw new Error('Missing private key')
|
||||
return wif.encode(this.network.wif, this.__d, this.compressed)
|
||||
}
|
||||
|
||||
ECPair.prototype.sign = function (hash) {
|
||||
if (!this.__d) throw new Error('Missing private key')
|
||||
return ecc.sign(hash, this.__d)
|
||||
}
|
||||
|
||||
ECPair.prototype.verify = function (hash, signature) {
|
||||
return ecc.verify(hash, this.getPublicKey(), signature)
|
||||
}
|
||||
|
||||
function fromPrivateKey (buffer, options) {
|
||||
typeforce(types.Buffer256bit, buffer)
|
||||
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)')
|
||||
typeforce(isOptions, options)
|
||||
|
||||
return new ECPair(buffer, null, options)
|
||||
}
|
||||
|
||||
function fromPublicKey (buffer, options) {
|
||||
typeforce(isPoint, buffer)
|
||||
typeforce(isOptions, options)
|
||||
return new ECPair(null, buffer, options)
|
||||
}
|
||||
|
||||
function fromWIF (string, network) {
|
||||
let decoded = wif.decode(string)
|
||||
let version = decoded.version
|
||||
|
||||
// list of networks?
|
||||
if (types.Array(network)) {
|
||||
|
@ -74,58 +84,29 @@ ECPair.fromWIF = function (string, network) {
|
|||
if (version !== network.wif) throw new Error('Invalid network version')
|
||||
}
|
||||
|
||||
var d = BigInteger.fromBuffer(decoded.privateKey)
|
||||
|
||||
return new ECPair(d, null, {
|
||||
return fromPrivateKey(decoded.privateKey, {
|
||||
compressed: decoded.compressed,
|
||||
network: network
|
||||
})
|
||||
}
|
||||
|
||||
ECPair.makeRandom = function (options) {
|
||||
function makeRandom (options) {
|
||||
typeforce(isOptions, options)
|
||||
options = options || {}
|
||||
let rng = options.rng || randomBytes
|
||||
|
||||
var rng = options.rng || randomBytes
|
||||
|
||||
var d
|
||||
let d
|
||||
do {
|
||||
var buffer = rng(32)
|
||||
typeforce(types.Buffer256bit, buffer)
|
||||
d = rng(32)
|
||||
typeforce(types.Buffer256bit, d)
|
||||
} while (!ecc.isPrivate(d))
|
||||
|
||||
d = BigInteger.fromBuffer(buffer)
|
||||
} while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0)
|
||||
|
||||
return new ECPair(d, null, options)
|
||||
return fromPrivateKey(d, options)
|
||||
}
|
||||
|
||||
ECPair.prototype.getNetwork = function () {
|
||||
return this.network
|
||||
module.exports = {
|
||||
makeRandom,
|
||||
fromPrivateKey,
|
||||
fromPublicKey,
|
||||
fromWIF
|
||||
}
|
||||
|
||||
ECPair.prototype.getPublicKeyBuffer = function () {
|
||||
return this.Q.getEncoded(this.compressed)
|
||||
}
|
||||
|
||||
ECPair.prototype.sign = function (hash) {
|
||||
if (!this.d) throw new Error('Missing private key')
|
||||
|
||||
let signature = ecdsa.sign(hash, this.d)
|
||||
return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64)
|
||||
}
|
||||
|
||||
ECPair.prototype.toWIF = function () {
|
||||
if (!this.d) throw new Error('Missing private key')
|
||||
|
||||
return wif.encode(this.network.wif, this.d.toBuffer(32), this.compressed)
|
||||
}
|
||||
|
||||
ECPair.prototype.verify = function (hash, signature) {
|
||||
signature = {
|
||||
r: BigInteger.fromBuffer(signature.slice(0, 32)),
|
||||
s: BigInteger.fromBuffer(signature.slice(32, 64))
|
||||
}
|
||||
|
||||
return ecdsa.verify(hash, signature, this.Q)
|
||||
}
|
||||
|
||||
module.exports = ECPair
|
||||
|
|
|
@ -180,7 +180,7 @@ function fixMultisigOrder (input, transaction, vin) {
|
|||
var unmatched = input.signatures.concat()
|
||||
|
||||
input.signatures = input.pubKeys.map(function (pubKey) {
|
||||
var keyPair = ECPair.fromPublicKeyBuffer(pubKey)
|
||||
var keyPair = ECPair.fromPublicKey(pubKey)
|
||||
var match
|
||||
|
||||
// check for a signature
|
||||
|
@ -686,7 +686,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
|
|||
throw new Error('Inconsistent redeemScript')
|
||||
}
|
||||
|
||||
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer()
|
||||
var kpPubKey = keyPair.publicKey || keyPair.getPublicKey()
|
||||
if (!canSign(input)) {
|
||||
if (witnessValue !== undefined) {
|
||||
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
|
||||
|
|
|
@ -16,11 +16,9 @@ function Satoshi (value) {
|
|||
}
|
||||
|
||||
// external dependent types
|
||||
var BigInt = typeforce.quacksLike('BigInteger')
|
||||
var ECPoint = typeforce.quacksLike('Point')
|
||||
|
||||
// exposed, external API
|
||||
var ECSignature = typeforce.compile({ r: BigInt, s: BigInt })
|
||||
var Network = typeforce.compile({
|
||||
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
|
||||
bip32: {
|
||||
|
@ -34,11 +32,9 @@ var Network = typeforce.compile({
|
|||
|
||||
// extend typeforce types with ours
|
||||
var types = {
|
||||
BigInt: BigInt,
|
||||
BIP32Path: BIP32Path,
|
||||
Buffer256bit: typeforce.BufferN(32),
|
||||
ECPoint: ECPoint,
|
||||
ECSignature: ECSignature,
|
||||
Hash160bit: typeforce.BufferN(20),
|
||||
Hash256bit: typeforce.BufferN(32),
|
||||
Network: Network,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue