-
Notifications
You must be signed in to change notification settings - Fork 397
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d86cd2a
commit c0690b3
Showing
13 changed files
with
5,368 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
'use strict'; | ||
|
||
var hash = require('hash.js'); | ||
var elliptic = require('../../elliptic'); | ||
var utils = elliptic.utils; | ||
var assert = utils.assert; | ||
var parseBytes = utils.parseBytes; | ||
var KeyPair = require('./key'); | ||
var Signature = require('./signature'); | ||
|
||
function EDDSA(curve) { | ||
assert(curve === 'ed25519', 'only tested with ed25519 so far'); | ||
|
||
if (!(this instanceof EDDSA)) | ||
return new EDDSA(curve); | ||
|
||
var curve = elliptic.curves[curve].curve; | ||
this.curve = curve; | ||
this.g = curve.g; | ||
this.g.precompute(curve.n.bitLength() + 1); | ||
|
||
this.pointClass = curve.point().constructor; | ||
this.encodingLength = Math.ceil(curve.n.bitLength() / 8); | ||
this.hash = hash.sha512; | ||
} | ||
|
||
module.exports = EDDSA; | ||
|
||
/** | ||
* @param {Array|String} message - message bytes | ||
* @param {Array|String|KeyPair} secret - secret bytes or a keypair | ||
* @returns {Signature} - signature | ||
*/ | ||
EDDSA.prototype.sign = function sign(message, secret) { | ||
message = parseBytes(message); | ||
var key = this.keyFromSecret(secret); | ||
var r = this.hashInt(key.messagePrefix(), message); | ||
var R = this.g.mul(r); | ||
var Rencoded = this.encodePoint(R); | ||
var s_ = this.hashInt(Rencoded, key.pubBytes(), message) | ||
.mul(key.priv()); | ||
var S = r.add(s_).mod(this.curve.n); | ||
return this.makeSignature({ R: R, S: S, Rencoded: Rencoded }); | ||
}; | ||
|
||
/** | ||
* @param {Array} message - message bytes | ||
* @param {Array|String|Signature} sig - sig bytes | ||
* @param {Array|String|Point|KeyPair} pub - public key | ||
* @returns {Boolean} - true if public key matches sig of message | ||
*/ | ||
EDDSA.prototype.verify = function verify(message, sig, pub) { | ||
message = parseBytes(message); | ||
sig = this.makeSignature(sig); | ||
var key = this.keyFromPublic(pub); | ||
var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message); | ||
var SG = this.g.mul(sig.S()); | ||
var RplusAh = sig.R().add(key.pub().mul(h)); | ||
return RplusAh.eq(SG); | ||
}; | ||
|
||
EDDSA.prototype.hashInt = function hashInt() { | ||
var hash = this.hash(); | ||
for (var i = 0; i < arguments.length; i++) { | ||
hash.update(arguments[i]); | ||
} | ||
return utils.intFromLE(hash.digest()).mod(this.curve.n); | ||
}; | ||
|
||
EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) { | ||
return KeyPair.fromPublic(this, pub); | ||
}; | ||
|
||
EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) { | ||
return KeyPair.fromSecret(this, secret); | ||
}; | ||
|
||
EDDSA.prototype.makeSignature = function makeSignature(sig) { | ||
if (sig instanceof Signature) | ||
return sig; | ||
return new Signature(this, sig); | ||
}; | ||
|
||
/** | ||
* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2 | ||
* | ||
* EDDSA defines methods for encoding and decoding points and integers. These are | ||
* helper convenience methods, that pass along to utility functions implied | ||
* parameters. | ||
* | ||
*/ | ||
EDDSA.prototype.encodePoint = function encodePoint(point) { | ||
return utils.pointToLEYoddX(point, this.encodingLength); | ||
}; | ||
|
||
EDDSA.prototype.decodePoint = function decodePoint(bytes) { | ||
return utils.pointFromLEYoddX(this.curve, bytes, bytes.length); | ||
}; | ||
|
||
EDDSA.prototype.encodeInt = function encodeInt(num) { | ||
return utils.intToLE(num, this.encodingLength); | ||
}; | ||
|
||
EDDSA.prototype.decodeInt = function decodeInt(bytes) { | ||
return utils.intFromLE(bytes); | ||
}; | ||
|
||
EDDSA.prototype.isPoint = function isPoint(val) { | ||
return val instanceof this.pointClass; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
'use strict'; | ||
|
||
var elliptic = require('../../elliptic'); | ||
var utils = elliptic.utils; | ||
var assert = utils.assert; | ||
var parseBytes = utils.parseBytes; | ||
var lazyComputed = utils.lazyComputed; | ||
|
||
/** | ||
* @param {EDDSA} eddsa - instance | ||
* @param {Object} params - public/private key parameters | ||
* | ||
* @param {Array<Byte>} [params.secret] - secret seed bytes | ||
* @param {Point} [params.pub] - public key point (aka `A` in eddsa terms) | ||
* @param {Array<Byte>} [params.pub] - public key point encoded as bytes | ||
* | ||
*/ | ||
function KeyPair(eddsa, params) { | ||
this.eddsa = eddsa; | ||
this._secret = parseBytes(params.secret); | ||
if (eddsa.isPoint(params.pub)) | ||
this._pub = params.pub; | ||
else | ||
this._pubBytes = parseBytes(params.pub); | ||
} | ||
|
||
KeyPair.fromPublic = function fromPublic(eddsa, pub) { | ||
if (pub instanceof KeyPair) | ||
return pub; | ||
return new KeyPair(eddsa, { pub: pub }); | ||
}; | ||
|
||
KeyPair.fromSecret = function fromSecret(eddsa, secret) { | ||
if (secret instanceof KeyPair) | ||
return secret; | ||
return new KeyPair(eddsa, { secret: secret }); | ||
}; | ||
|
||
KeyPair.prototype.secret = function secret() { | ||
return this._secret; | ||
}; | ||
|
||
lazyComputed(KeyPair, 'pubBytes', function pubBytes() { | ||
return this.eddsa.encodePoint(this.pub()); | ||
}); | ||
|
||
lazyComputed(KeyPair, 'pub', function pub() { | ||
if (this._pubBytes) | ||
return this.eddsa.decodePoint(this._pubBytes); | ||
return this.eddsa.g.mul(this.priv()); | ||
}); | ||
|
||
lazyComputed(KeyPair, 'privBytes', function privBytes() { | ||
var eddsa = this.eddsa; | ||
var hash = this.hash(); | ||
var lastIx = eddsa.encodingLength - 1; | ||
|
||
var a = hash.slice(0, eddsa.encodingLength); | ||
a[0] &= 248; | ||
a[lastIx] &= 127; | ||
a[lastIx] |= 64; | ||
|
||
return a; | ||
}); | ||
|
||
lazyComputed(KeyPair, 'priv', function priv() { | ||
return this.eddsa.decodeInt(this.privBytes()); | ||
}); | ||
|
||
lazyComputed(KeyPair, 'hash', function hash() { | ||
return this.eddsa.hash().update(this.secret()).digest(); | ||
}); | ||
|
||
lazyComputed(KeyPair, 'messagePrefix', function messagePrefix() { | ||
return this.hash().slice(this.eddsa.encodingLength); | ||
}); | ||
|
||
KeyPair.prototype.sign = function sign(message) { | ||
assert(this._secret, 'KeyPair can only verify'); | ||
return this.eddsa.sign(message, this); | ||
}; | ||
|
||
KeyPair.prototype.verify = function verify(message, sig) { | ||
return this.eddsa.verify(message, sig, this); | ||
}; | ||
|
||
KeyPair.prototype.getSecret = function getSecret(enc) { | ||
assert(this._secret, 'KeyPair is public only'); | ||
return utils.encode(this.secret(), enc); | ||
}; | ||
|
||
KeyPair.prototype.getPublic = function getPublic(enc) { | ||
return utils.encode(this.pubBytes(), enc); | ||
}; | ||
|
||
module.exports = KeyPair; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
'use strict'; | ||
|
||
var bn = require('bn.js'); | ||
var elliptic = require('../../elliptic'); | ||
var utils = elliptic.utils; | ||
var assert = utils.assert; | ||
var lazyComputed = utils.lazyComputed; | ||
var parseBytes = utils.parseBytes; | ||
|
||
/** | ||
* @param {EDDSA} eddsa - eddsa instance | ||
* @param {Array<Bytes>|Object} sig - | ||
* @param {Array<Bytes>|Point} [sig.R] - R point as Point or bytes | ||
* @param {Array<Bytes>|bn} [sig.S] - S scalar as bn or bytes | ||
* @param {Array<Bytes>} [sig.Rencoded] - R point encoded | ||
* @param {Array<Bytes>} [sig.Sencoded] - S scalar encoded | ||
*/ | ||
function Signature(eddsa, sig) { | ||
this.eddsa = eddsa; | ||
|
||
if (typeof sig !== 'object') | ||
sig = parseBytes(sig); | ||
|
||
if (Array.isArray(sig)) { | ||
sig = { | ||
R: sig.slice(0, eddsa.encodingLength), | ||
S: sig.slice(eddsa.encodingLength) | ||
}; | ||
} | ||
|
||
assert(sig.R && sig.S, 'Signature without R or S'); | ||
|
||
if (eddsa.isPoint(sig.R)) | ||
this._R = sig.R; | ||
if (sig.S instanceof bn) | ||
this._S = sig.S; | ||
|
||
this._Rencoded = Array.isArray(sig.R) ? sig.R : sig.Rencoded; | ||
this._Sencoded = Array.isArray(sig.S) ? sig.S : sig.Sencoded; | ||
} | ||
|
||
lazyComputed(Signature, 'S', function S() { | ||
return this.eddsa.decodeInt(this.Sencoded()); | ||
}); | ||
|
||
lazyComputed(Signature, 'R', function S() { | ||
return this.eddsa.decodePoint(this.Rencoded()); | ||
}); | ||
|
||
lazyComputed(Signature, 'Rencoded', function S() { | ||
return this.eddsa.encodePoint(this.R()); | ||
}); | ||
|
||
lazyComputed(Signature, 'Sencoded', function S() { | ||
return this.eddsa.encodeInt(this.S()); | ||
}); | ||
|
||
Signature.prototype.toBytes = function toBytes() { | ||
return this.Rencoded().concat(this.Sencoded()); | ||
}; | ||
|
||
Signature.prototype.toHex = function toHex() { | ||
return utils.encode(this.toBytes(), 'hex').toUpperCase(); | ||
}; | ||
|
||
module.exports = Signature; |
Oops, something went wrong.