Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Jan 6, 2016
1 parent d47ed2c commit 0030854
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 125 deletions.
56 changes: 54 additions & 2 deletions src/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,65 @@ function calcPubKeyRecoveryParam (e, signature, Q) {
throw new Error('Unable to find valid recovery factor')
}

function intAdd (a, b) {
typeforce(types.tuple(types.Buffer, types.Buffer), arguments)

var A = BigInteger.fromBuffer(a)
var B = BigInteger.fromBuffer(b)

return A.add(B).toBuffer(32)

This comment has been minimized.

Copy link
@afk11

afk11 Oct 13, 2016

Contributor

a + b mod n ?

This comment has been minimized.

Copy link
@dcousens

dcousens Oct 13, 2016

Author Contributor

@afk11 its a untested concept WIP , but probably yeah mod(n)

}

function intCheck (a) {
typeforce(types.tuple(types.Buffer), arguments)

var A = BigInteger.fromBuffer(a)

return A.signum() > 0 && A.compareTo(secp256k1.n) < 0
}

function intSign (a) {
typeforce(types.tuple(types.Buffer), arguments)

return BigInteger.fromBuffer(a).signum()
}

function pointAdd (p, q) {
typeforce(types.tuple(types.Buffer, types.Buffer), arguments)

var P = ecurve.Point.decodeFrom(p)
var Q = ecurve.Point.decodeFrom(q)
var R = P.add(Q)

if (secp256k1.isInfinity(R)) return null
return R.getEncoded(P.compressed)
}

function pointDerive (d, compressed) {
typeforce(types.tuple(types.Buffer, types.Boolean), arguments)

d = BigInteger.fromBuffer(d)
return secp256k1.G.multiply(d).getEncoded(compressed)
}

function pointVerify (q) {
typeforce(types.tuple(types.Buffer), arguments)
var Q = ecurve.Point.decodeFrom(q)

return secp256k1.validate(Q)
}

module.exports = {
calcPubKeyRecoveryParam: calcPubKeyRecoveryParam,
deterministicGenerateK: deterministicGenerateK,
recoverPubKey: recoverPubKey,
sign: sign,
verify: verify,

// TODO: remove
__curve: secp256k1
intAdd: intAdd,
intCheck: intCheck,
intSign: intSign,
pointAdd: pointAdd,
pointDerive: pointDerive,
pointVerify: pointVerify
}
110 changes: 53 additions & 57 deletions src/ecpair.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,56 @@
var baddress = require('./address')
var bcrypto = require('./crypto')
var bs58check = require('bs58check')
var ecdsa = require('./ecdsa')
var randomBytes = require('randombytes')
var secp256k1 = require('./ecdsa')
var typeforce = require('typeforce')
var types = require('./types')
var wif = require('wif')

var NETWORKS = require('./networks')
var BigInteger = require('bigi')

var ecurve = require('ecurve')
var secp256k1 = ecdsa.__curve

function ECPair (d, Q, options) {
if (options) {
typeforce({
compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network)
network: types.maybe(types.Network),
validate: types.maybe(types.Boolean)
}, options)
} else {
options = {}
}

options = options || {}
options.network = options.network || NETWORKS.bitcoin

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')
typeforce(types.Buffer256bit, d)

this.d = d
} else {
typeforce(types.ECPoint, Q)
if (Q) throw new TypeError('Unexpected public key parameter')
if (!secp256k1.intCheck(d)) throw new TypeError('Private key must be within the interval [1, n - 1]')

this.__Q = Q
}
this.__d = d
this.__compressed = options.compressed === undefined ? true : options.compressed
} else if (Q) {
typeforce(types.Buffer, Q)

this.compressed = options.compressed === undefined ? true : options.compressed
this.network = options.network || NETWORKS.bitcoin
}
if (options.validate) {
secp256k1.pointVerify(Q)
}

Object.defineProperty(ECPair.prototype, 'Q', {
get: function () {
if (!this.__Q && this.d) {
this.__Q = secp256k1.G.multiply(this.d)
if (options.compressed && Q.length !== 65) {
throw new TypeError('Expected compressed public key')
} else if (Q.length !== 33) {
throw new TypeError('Expected uncompressed public key')
}

return this.__Q
}
})
this.__Q = Q

ECPair.fromPublicKeyBuffer = function (buffer, network) {
var Q = ecurve.Point.decodeFrom(secp256k1, buffer)
// TODO: remove
this.__compressed = (Q.length === 33)
}

return new ECPair(null, Q, {
compressed: Q.compressed,
network: network
})
typeforce(types.Network, options.network)
this.__network = options.network
}

ECPair.fromWIF = function (string, network) {
Expand All @@ -69,10 +65,9 @@ ECPair.fromWIF = function (string, network) {
}).pop() || {}
}

var decoded = wif.decodeRaw(network.wif, buffer)
var d = BigInteger.fromBuffer(decoded.d)
var decoded = wif.decode(network.wif, string)

return new ECPair(d, null, {
return new ECPair(decoded.d, null, {
compressed: decoded.compressed,
network: network
})
Expand All @@ -82,51 +77,52 @@ ECPair.makeRandom = function (options) {
options = options || {}

var rng = options.rng || randomBytes

var d
do {
var buffer = rng(32)
typeforce(types.Buffer256bit, buffer)

d = BigInteger.fromBuffer(buffer)
} while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0)
d = rng(32)
typeforce(types.Buffer256bit, d)
} while (!secp256k1.intCheck(d))

return new ECPair(d, null, options)
}

ECPair.prototype.getAddress = function () {
var pubKey = this.getPublicKeyBuffer()
var pubKeyHash = bcrypto.hash160(pubKey)
return baddress.toBase58Check(bcrypto.hash160(this.getPublic()), this.getNetwork().pubKeyHash)
}

var payload = new Buffer(21)
payload.writeUInt8(this.network.pubKeyHash, 0)
pubKeyHash.copy(payload, 1)
ECPair.prototype.getNetwork = function () {
return this.__network
}

return bs58check.encode(payload)
ECPair.prototype.getPrivate = function () {
if (!this.__d) throw new Error('Missing private key')
return this.__d
}

ECPair.prototype.getNetwork = function () {
return this.network
ECPair.prototype.getPublic = function () {
if (!this.__Q) {
this.__Q = secp256k1.pointDerive(this.getPrivate(), this.isCompressed())
}

return this.__Q
}

ECPair.prototype.getPublicKeyBuffer = function () {
return this.Q.getEncoded(this.compressed)
ECPair.prototype.isCompressed = function () {
return this.__compressed
// return this.getPublic().length === 33
}

ECPair.prototype.sign = function (hash) {
if (!this.d) throw new Error('Missing private key')

return ecdsa.sign(hash, this.d)
return secp256k1.sign(hash, this.getPrivate())
}

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)
if (!this.__d) throw new Error('Missing private key')
return wif.encode(this.getNetwork().wif, this.getPrivate(), this.isCompressed())
}

ECPair.prototype.verify = function (hash, signature) {
return ecdsa.verify(hash, signature, this.Q)
return secp256k1.verify(hash, signature, this.getPublic())
}

module.exports = ECPair
Loading

3 comments on commit 0030854

@dcousens
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jprichardson I had hoped to merge something like this.
To save you reading it to find out what it is, it changes d and Q to their Buffer equivalents as part of the ECDSA break out.

@jprichardson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I see where you're going with this. I like it!

@dcousens
Copy link
Contributor Author

@dcousens dcousens commented on 0030854 Oct 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still aiming for this @jprichardson ... but I don't see a sane/performant API for 3.0.0, so again, 4.0.0.

c9f0c43 was something I tried recently, but again the issues come up with a sane interface while still allowing for advanced cryptography.

I think perhaps splitting BIP32 out to its own module will mean we manage the "key pair" ourselves there, and that will avoid that use case.

But what about people who want to stealth addresses etc?
I don't want to force serialization... but duck types SUCK!
Hmph.

Please sign in to comment.