Remove sjcl-custom in favor of sjcl-extended module in separate repo

This commit is contained in:
Nicholas Dudfield
2015-06-04 16:19:31 +07:00
parent 22dc39b920
commit 35ae968d9f
15 changed files with 12 additions and 1212 deletions

8
npm-shrinkwrap.json generated
View File

@@ -66,9 +66,17 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/simple-asyncify/-/simple-asyncify-0.1.0.tgz"
},
"sjcl-extended": {
"version": "1.0.3",
"from": "ripple/sjcl-extended#1.0.3",
"resolved": "git://github.com/ripple/sjcl-extended.git#908218e0db4fccd08c4e3f1f7269b72f8d93c270",
"dependencies": {
"sjcl": {
"version": "1.0.3",
"from": "sjcl@>=1.0.3 <2.0.0",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.3.tgz"
}
}
},
"ws": {
"version": "0.7.2",

View File

@@ -25,7 +25,7 @@
"ripple-lib-transactionparser": "^0.3.2",
"ripple-wallet-generator": "^1.0.3",
"simple-asyncify": "^0.1.0",
"sjcl": "^1.0.3",
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
"ws": "~0.7.1"
},
"devDependencies": {
@@ -59,7 +59,6 @@
"compile": "babel -i runtime -d dist/npm/ src/",
"compile-with-source-maps": "babel -i runtime -s -t -d dist/npm/ src/",
"prepublish": "npm run compile",
"postinstall": "cd node_modules/sjcl; ./configure --with-all --compress=none; make",
"test": "istanbul test _mocha",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; fi; eslint --reset -c eslintrc src/*.js",

View File

@@ -1,8 +0,0 @@
'use strict';
require('./sjcl-ecc-pointextras.js');
require('./sjcl-secp256k1.js');
require('./sjcl-extramath.js');
require('./sjcl-validecc.js');
require('./sjcl-ecdsa-canonical.js');
require('./sjcl-ecdsa-der.js');
require('./sjcl-ecdsa-recoverablepublickey.js');

View File

@@ -1,86 +0,0 @@
/* eslint new-cap: [2, {newIsCapExceptions: ["bn"]}] */
'use strict';
var sjcl = require('sjcl');
/**
* Check that the point is valid based on the method described in
* SEC 1: Elliptic Curve Cryptography, section 3.2.2.1:
* Elliptic Curve Public Key Validation Primitive
* http://www.secg.org/download/aid-780/sec1-v2.pdf
*
* @returns {Boolean} true if point is valid
*/
sjcl.ecc.point.prototype.isValidPoint = function() {
var self = this;
var field_modulus = self.curve.field.modulus;
if (self.isIdentity) {
return false;
}
// Check that coordinatres are in bounds
// Return false if x < 1 or x > (field_modulus - 1)
if (((new sjcl.bn(1).greaterEquals(self.x)) &&
!self.x.equals(1)) ||
(self.x.greaterEquals(field_modulus.sub(1))) &&
!self.x.equals(1)) {
return false;
}
// Return false if y < 1 or y > (field_modulus - 1)
if (((new sjcl.bn(1).greaterEquals(self.y)) &&
!self.y.equals(1)) ||
(self.y.greaterEquals(field_modulus.sub(1))) &&
!self.y.equals(1)) {
return false;
}
if (!self.isOnCurve()) {
return false;
}
// TODO check to make sure point is a scalar multiple of base_point
return true;
};
/**
* Check that the point is on the curve
*
* @returns {Boolean} true if point is on the curve
*/
sjcl.ecc.point.prototype.isOnCurve = function() {
var self = this;
var component_a = self.curve.a;
var component_b = self.curve.b;
var field_modulus = self.curve.field.modulus;
var left_hand_side = self.y.mul(self.y).mod(field_modulus);
var right_hand_side = self.x.mul(self.x).mul(self.x).add(
component_a.mul(self.x)).add(component_b).mod(field_modulus);
return left_hand_side.equals(right_hand_side);
};
sjcl.ecc.point.prototype.toString = function() {
return '(' +
this.x.toString() + ', ' +
this.y.toString() +
')';
};
sjcl.ecc.pointJac.prototype.toString = function() {
return '(' +
this.x.toString() + ', ' +
this.y.toString() + ', ' +
this.z.toString() +
')';
};

View File

@@ -1,20 +0,0 @@
'use strict';
var sjcl = require('sjcl');
sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature = function(rs) {
var w = sjcl.bitArray,
R = this._curve.r,
l = R.bitLength();
var r = sjcl.bn.fromBits(w.bitSlice(rs, 0, l)),
s = sjcl.bn.fromBits(w.bitSlice(rs, l, 2 * l));
// For a canonical signature we want the lower of two possible values for s
// 0 < s <= n/2
if (!R.copy().halveM().greaterEquals(s)) {
s = R.sub(s);
}
return w.concat(r.toBits(l), s.toBits(l));
};

View File

@@ -1,45 +0,0 @@
'use strict';
var sjcl = require('sjcl');
sjcl.ecc.ecdsa.secretKey.prototype.signDER = function(hash, paranoia) {
return this.encodeDER(this.sign(hash, paranoia));
};
sjcl.ecc.ecdsa.secretKey.prototype.encodeDER = function(rs) {
var w = sjcl.bitArray,
R = this._curve.r,
l = R.bitLength();
var rb = sjcl.codec.bytes.fromBits(w.bitSlice(rs, 0, l)),
sb = sjcl.codec.bytes.fromBits(w.bitSlice(rs, l, 2 * l));
// Drop empty leading bytes
while (!rb[0] && rb.length) {
rb.shift();
}
while (!sb[0] && sb.length) {
sb.shift();
}
// If high bit is set, prepend an extra zero byte (DER signed integer)
if (rb[0] & 0x80) {
rb.unshift(0);
}
if (sb[0] & 0x80) {
sb.unshift(0);
}
var buffer = [].concat(
0x30,
4 + rb.length + sb.length,
0x02,
rb.length,
rb,
0x02,
sb.length,
sb
);
return sjcl.codec.bytes.toBits(buffer);
};

View File

@@ -1,325 +0,0 @@
/* eslint-disable valid-jsdoc */
/* eslint new-cap: [2, {newIsCapExceptions: [
"bn", "invalid", "point", "corrupt", "bug", "publicKey"]}] */
'use strict';
const sjcl = require('sjcl');
/**
* This module uses the public key recovery method
* described in SEC 1: Elliptic Curve Cryptography,
* section 4.1.6, "Public Key Recovery Operation".
* http://www.secg.org/download/aid-780/sec1-v2.pdf
*
* Implementation based on:
* https://github.com/bitcoinjs/bitcoinjs-lib/blob/
* 89cf731ac7309b4f98994e3b4b67b7226020181f/src/ecdsa.js
*/
// Defined here so that this value only needs to be calculated once
let FIELD_MODULUS_PLUS_ONE_DIVIDED_BY_FOUR;
/**
* Retrieve the r and s components of a signature
*
* @param {sjcl.ecc.curve} curve - curve
* @param {bitArray} signature - signature
* @returns {Object} Object with 'r' and 's' fields each as an sjcl.bn
*/
function getRandSFromSignature(curve, signature) {
const r_length = curve.r.bitLength();
return {
r: sjcl.bn.fromBits(sjcl.bitArray.bitSlice(signature, 0, r_length)),
s: sjcl.bn.fromBits(sjcl.bitArray.bitSlice(
signature, r_length, sjcl.bitArray.bitLength(signature)))
};
}
/**
* Verify a signature given the raw components
* using method defined in section 4.1.5:
* "Alternative Verifying Operation"
*
* @param {sjcl.ecc.curve} curve
* @param {sjcl.bn} e
* @param {sjcl.bn} r
* @param {sjcl.bn} s
* @param {sjcl.ecc.point} public_key_point
* @returns {Boolean}
*/
function verify_raw(curve, e, r, s, public_key_point) {
const field_order = curve.r;
// Return false if r is out of bounds
if ((new sjcl.bn(1)).greaterEquals(r)
|| r.greaterEquals(new sjcl.bn(field_order))) {
return false;
}
// Return false if s is out of bounds
if ((new sjcl.bn(1)).greaterEquals(s)
|| s.greaterEquals(new sjcl.bn(field_order))) {
return false;
}
// Check that r = (u1 + u2)G
// u1 = e x s^-1 (mod field_order)
// u2 = r x s^-1 (mod field_order)
const s_mod_inverse_field_order = s.inverseMod(field_order);
const u1 = e.mul(s_mod_inverse_field_order).mod(field_order);
const u2 = r.mul(s_mod_inverse_field_order).mod(field_order);
const point_computed = curve.G.mult2(u1, u2, public_key_point);
return r.equals(point_computed.x.mod(field_order));
}
/**
* Recover the public key from the signature.
*
* @param {sjcl.ecc.curve} curve
* @param {sjcl.bn} r
* @param {sjcl.bn} s
* @param {bitArray} hash_bits
* @param {Number, 0-3} recovery_factor
* @returns {sjcl.point} Public key corresponding to signature
*/
function recoverPublicKeyPointFromSignature(curve, signature_r, signature_s,
hash_bits, recovery_factor) {
const field_order = curve.r;
const field_modulus = curve.field.modulus;
// The less significant bit specifies whether the y coordinate
// of the compressed point is even or not.
const compressed_point_y_coord_is_even = recovery_factor & 1;
// The more significant bit specifies whether we should use the
// first or second candidate key.
const use_second_candidate_key = (recovery_factor & 2) >> 1;
// Calculate (field_order + 1) / 4
if (!FIELD_MODULUS_PLUS_ONE_DIVIDED_BY_FOUR) {
FIELD_MODULUS_PLUS_ONE_DIVIDED_BY_FOUR = field_modulus.add(1).div(4);
}
// In the paper they write "1. For j from 0 to h do the following..."
// That is not necessary here because we are given the recovery_factor
// step 1.1 Let x = r + jn
// Here "j" is either 0 or 1
let x;
if (use_second_candidate_key) {
x = signature_r.add(field_order);
} else {
x = signature_r;
}
// step 1.2 and 1.3 convert x to an elliptic curve point
// Following formula in section 2.3.4 Octet-String-to-Elliptic-Curve-Point
// Conversion
const alpha = x.mul(x).mul(x).add(curve.a.mul(x)).add(curve.b).mod(
field_modulus);
const beta = alpha.powermod(FIELD_MODULUS_PLUS_ONE_DIVIDED_BY_FOUR,
field_modulus);
// If beta is even but y isn't or
// if beta is odd and y is even
// then subtract beta from the field_modulus
let y;
const beta_is_even = beta.mod(2).equals(0);
if (beta_is_even && !compressed_point_y_coord_is_even ||
!beta_is_even && compressed_point_y_coord_is_even) {
y = beta;
} else {
y = field_modulus.sub(beta);
}
// generated_point_R is the point generated from x and y
const generated_point_R = new sjcl.ecc.point(curve, x, y);
// step 1.4 check that R is valid and R x field_order !== infinity
// TODO: add check for R x field_order === infinity
if (!generated_point_R.isValidPoint()) {
throw new sjcl.exception.corrupt(
'point R. Not a valid point on the curve. Cannot recover public key');
}
// step 1.5 Compute e from M
const message_e = sjcl.bn.fromBits(hash_bits);
const message_e_neg = new sjcl.bn(0).sub(message_e).mod(field_order);
// step 1.6 Compute Q = r^-1 (sR - eG)
// console.log('r: ', signature_r);
const signature_r_inv = signature_r.inverseMod(field_order);
const public_key_point = generated_point_R.mult2(signature_s, message_e_neg,
curve.G).mult(signature_r_inv);
// Validate public key point
if (!public_key_point.isValidPoint()) {
throw new sjcl.exception.corrupt('public_key_point. Not a valid point'
+ ' on the curve. Cannot recover public key');
}
// Verify that this public key matches the signature
if (!verify_raw(curve, message_e, signature_r, signature_s,
public_key_point)) {
throw new sjcl.exception.corrupt('cannot recover public key');
}
return public_key_point;
}
/**
* Determine the recovery factor by trying all four
* possibilities and figuring out which results in the
* correct public key
*
* @param {sjcl.ecc.curve} curve
* @param {sjcl.bn} r
* @param {sjcl.bn} s
* @param {bitArray} hash_bits
* @param {sjcl.ecc.point} original_public_key_point
* @returns {Number, 0-3} Recovery factor
*/
function calculateRecoveryFactor(curve, r, s, hash_bits,
original_public_key_point) {
const original_public_key_point_bits = original_public_key_point.toBits();
// TODO: verify that it is possible for the recovery_factor to be 2 or 3,
// we may only need 1 bit because the canonical signature might remove the
// possibility of us needing to "use the second candidate key"
for (let possible_factor = 0; possible_factor < 4; possible_factor++) {
let resulting_public_key_point;
try {
resulting_public_key_point = recoverPublicKeyPointFromSignature(
curve, r, s, hash_bits, possible_factor);
} catch (err) {
// console.log(err, err.stack);
continue;
}
if (sjcl.bitArray.equal(resulting_public_key_point.toBits(),
original_public_key_point_bits)) {
return possible_factor;
}
}
throw new sjcl.exception.bug(
'unable to calculate recovery factor from signature');
}
/**
* Sign the given hash such that the public key, prepending an extra byte
* so that the public key will be recoverable from the signature
*
* @param {bitArray} hash - hash to sign
* @param {Number} paranoia - minimum entropy required
* @param {Number} k_for_testing - fixed k value to bypass PRNG
* @returns {bitArray} Signature formatted as bitArray
*/
sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(
hash, paranoia, k_for_testing) {
const self = this;
// Convert hash to bits and determine encoding for output
let hash_bits;
if (typeof hash === 'object' && hash.length > 0
&& typeof hash[0] === 'number') {
hash_bits = hash;
} else {
throw new sjcl.exception.invalid('hash. Must be a bitArray');
}
// Sign hash with standard, canonicalized method
const standard_signature = self.sign(hash_bits, paranoia, k_for_testing);
const canonical_signature = self.canonicalizeSignature(standard_signature);
// Extract r and s signature components from canonical signature
const r_and_s = getRandSFromSignature(self._curve, canonical_signature);
// Rederive public key
const public_key = self._curve.G.mult(sjcl.bn.fromBits(self.get()));
// Determine recovery factor based on which possible value
// returns the correct public key
const recovery_factor = calculateRecoveryFactor(self._curve, r_and_s.r,
r_and_s.s, hash_bits, public_key);
// Prepend recovery_factor to signature and encode in DER
// The value_to_prepend should be 4 bytes total
const value_to_prepend = recovery_factor + 27;
const final_signature_bits = sjcl.bitArray.concat([value_to_prepend],
canonical_signature);
// Return value in bits
return final_signature_bits;
};
/**
* Recover the public key from a signature created with the
* signWithRecoverablePublicKey method in this module
*
* @static
*
* @param {bitArray} hash
* @param {bitArray} signature
* @param {sjcl.ecc.curve} [curve=sjcl.ecc.curves['k256']]
* @returns {sjcl.ecc.ecdsa.publicKey} Public key
*/
sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(
hash, signature, curve=sjcl.ecc.curves.k256) {
if (!signature || signature instanceof sjcl.ecc.curve) {
throw new sjcl.exception.invalid(
'must supply hash and signature to recover public key');
}
// Convert hash to bits and determine encoding for output
let hash_bits;
if (typeof hash === 'object' && hash.length > 0
&& typeof hash[0] === 'number') {
hash_bits = hash;
} else {
throw new sjcl.exception.invalid('hash. Must be a bitArray');
}
let signature_bits;
if (typeof signature === 'object' && signature.length > 0
&& typeof signature[0] === 'number') {
signature_bits = signature;
} else {
throw new sjcl.exception.invalid('signature. Must be a bitArray');
}
// Extract recovery_factor from first 4 bytes
const recovery_factor = signature_bits[0] - 27;
if (recovery_factor < 0 || recovery_factor > 3) {
throw new sjcl.exception.invalid(
'signature. Signature must be generated with algorithm ' +
'that prepends the recovery factor in order to recover the public key');
}
// Separate r and s values
const r_and_s = getRandSFromSignature(curve, signature_bits.slice(1));
const signature_r = r_and_s.r;
const signature_s = r_and_s.s;
// Recover public key using recovery_factor
const recovered_public_key_point = recoverPublicKeyPointFromSignature(
curve, signature_r, signature_s, hash_bits, recovery_factor);
const recovered_public_key = new sjcl.ecc.ecdsa.publicKey(
curve, recovered_public_key_point);
return recovered_public_key;
};

View File

@@ -1,156 +0,0 @@
/* eslint new-cap: [2, {newIsCapExceptions: ["bn"]}] */
'use strict';
var sjcl = require('sjcl');
sjcl.bn.ZERO = new sjcl.bn(0);
/* [ this / that , this % that ] */
sjcl.bn.prototype.divRem = function(that) {
if (typeof that !== 'object') {
that = new this._class(that);
}
var thisa = this.abs(), thata = that.abs(), quot = new this._class(0),
ci = 0;
if (!thisa.greaterEquals(thata)) {
return [new sjcl.bn(0), this.copy()];
} else if (thisa.equals(thata)) {
return [new sjcl.bn(1), new sjcl.bn(0)];
}
for (; thisa.greaterEquals(thata); ci++) {
thata.doubleM();
}
for (; ci > 0; ci--) {
quot.doubleM();
thata.halveM();
if (thisa.greaterEquals(thata)) {
quot.addM(1);
thisa.subM(that).normalize();
}
}
return [quot, thisa];
};
/* this /= that (rounded to nearest int) */
sjcl.bn.prototype.divRound = function(that) {
var dr = this.divRem(that), quot = dr[0], rem = dr[1];
if (rem.doubleM().greaterEquals(that)) {
quot.addM(1);
}
return quot;
};
/* this /= that (rounded down) */
sjcl.bn.prototype.div = function(that) {
var dr = this.divRem(that);
return dr[0];
};
sjcl.bn.prototype.sign = function() {
return this.greaterEquals(sjcl.bn.ZERO) ? 1 : -1;
};
/* -this */
sjcl.bn.prototype.neg = function() {
return sjcl.bn.ZERO.sub(this);
};
/* |this| */
sjcl.bn.prototype.abs = function() {
if (this.sign() === -1) {
return this.neg();
}
return this;
};
/* this >> that */
sjcl.bn.prototype.shiftRight = function(that) {
if (typeof that !== 'number') {
throw new Error('shiftRight expects a number');
}
that = +that;
if (that < 0) {
return this.shiftLeft(that);
}
var a = new sjcl.bn(this);
while (that >= this.radix) {
a.limbs.shift();
that -= this.radix;
}
while (that--) {
a.halveM();
}
return a;
};
/* this >> that */
sjcl.bn.prototype.shiftLeft = function(that) {
if (typeof that !== 'number') {
throw new Error('shiftLeft expects a number');
}
that = +that;
if (that < 0) {
return this.shiftRight(that);
}
var a = new sjcl.bn(this);
while (that >= this.radix) {
a.limbs.unshift(0);
that -= this.radix;
}
while (that--) {
a.doubleM();
}
return a;
};
/* (int)this */
// NOTE Truncates to 32-bit integer
sjcl.bn.prototype.toNumber = function() {
return this.limbs[0] | 0;
};
/* find n-th bit, 0 = LSB */
sjcl.bn.prototype.testBit = function(bitIndex) {
var limbIndex = Math.floor(bitIndex / this.radix);
var bitIndexInLimb = bitIndex % this.radix;
if (limbIndex >= this.limbs.length) {
return 0;
}
return (this.limbs[limbIndex] >>> bitIndexInLimb) & 1;
};
/* set n-th bit, 0 = LSB */
sjcl.bn.prototype.setBitM = function(bitIndex) {
var limbIndex = Math.floor(bitIndex / this.radix);
var bitIndexInLimb = bitIndex % this.radix;
while (limbIndex >= this.limbs.length) {
this.limbs.push(0);
}
this.limbs[limbIndex] |= 1 << bitIndexInLimb;
this.cnormalize();
return this;
};
sjcl.bn.prototype.modInt = function(n) {
return this.toNumber() % n;
};

View File

@@ -1,81 +0,0 @@
/* eslint new-cap: [2, {newIsCapExceptions: ["pointJac"]}] */
'use strict';
var sjcl = require('sjcl');
// ----- for secp256k1 ------
sjcl.ecc.point.prototype.toBytesCompressed = function() {
var header = this.y.mod(2).toString() === '0x0' ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()));
};
// Replace point addition and doubling algorithms
// NIST-P256 is a=-3, we need algorithms for a=0
//
// This is a custom point addition formula that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.add = function(T) {
var self = this;
if (self.curve !== T.curve) {
throw ('sjcl.ecc.add(): Points must be on the same curve to add them!');
}
if (self.isIdentity) {
return T.toJac();
} else if (T.isIdentity) {
return self;
}
var z1z1 = self.z.square();
var h = T.x.mul(z1z1).subM(self.x);
var s2 = T.y.mul(self.z).mul(z1z1);
if (h.equals(0)) {
if (self.y.equals(T.y.mul(z1z1.mul(self.z)))) {
// same point
return self.doubl();
}
// inverses
return new sjcl.ecc.pointJac(self.curve);
}
var hh = h.square();
var i = hh.copy().doubleM().doubleM();
var j = h.mul(i);
var r = s2.sub(self.y).doubleM();
var v = self.x.mul(i);
var x = r.square().subM(j).subM(v.copy().doubleM());
var y = r.mul(v.sub(x)).subM(self.y.mul(j).doubleM());
var z = self.z.add(h).square().subM(z1z1).subM(hh);
return new sjcl.ecc.pointJac(this.curve, x, y, z);
};
// This is a custom doubling algorithm that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.doubl = function() {
if (this.isIdentity) {
return this;
}
var a = this.x.square();
var b = this.y.square();
var c = b.square();
var d = this.x.add(b).square().subM(a).subM(c).doubleM();
var e = a.mul(3);
var f = e.square();
var x = f.sub(d.copy().doubleM());
var y = e.mul(d.sub(x)).subM(c.doubleM().doubleM().doubleM());
var z = this.z.mul(this.y).doubleM();
return new sjcl.ecc.pointJac(this.curve, x, y, z);
};
// DEPRECATED:
// previously the c256 curve was overridden with the secp256k1 curve
// since then, sjcl has been updated to support k256
// this override exist to keep supporting the old c256 with k256 behavior
// this will be removed in future release
sjcl.ecc.curves.c256 = sjcl.ecc.curves.k256;

View File

@@ -1,47 +0,0 @@
/* eslint new-cap: [2, {newIsCapExceptions: ["corrupt"]}] */
'use strict';
var sjcl = require('sjcl');
sjcl.ecc.ecdsa.secretKey.prototype.sign = function(hash, paranoia,
k_for_testing) {
var R = this._curve.r,
l = R.bitLength();
// k_for_testing should ONLY BE SPECIFIED FOR TESTING
// specifying it will make the signature INSECURE
var k;
if (typeof k_for_testing === 'object' && k_for_testing.length > 0
&& typeof k_for_testing[0] === 'number') {
k = k_for_testing;
} else if (typeof k_for_testing === 'string'
&& /^[0-9a-fA-F]+$/.test(k_for_testing)) {
k = sjcl.bn.fromBits(sjcl.codec.hex.toBits(k_for_testing));
} else {
// This is the only option that should be used in production
k = sjcl.bn.random(R.sub(1), paranoia).add(1);
}
var r = this._curve.G.mult(k).x.mod(R);
var s = sjcl.bn.fromBits(hash).add(r.mul(this._exponent))
.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
};
sjcl.ecc.ecdsa.publicKey.prototype.verify = function(hash, rs) {
var w = sjcl.bitArray;
var R = this._curve.r;
var l = R.bitLength();
var r = sjcl.bn.fromBits(w.bitSlice(rs, 0, l));
var s = sjcl.bn.fromBits(w.bitSlice(rs, l, 2 * l));
var sInv = s.inverseMod(R);
var hG = sjcl.bn.fromBits(hash).mul(sInv).mod(R);
var hA = r.mul(sInv).mod(R);
var r2 = this._curve.G.mult2(hG, hA, this._point).x;
if (r.equals(0) || s.equals(0) || r.greaterEquals(R) || s.greaterEquals(R)
|| !r2.equals(r)) {
throw (new sjcl.exception.corrupt('signature didn\'t check out'));
}
return true;
};

View File

@@ -157,7 +157,6 @@ exports.toTimestamp = toTimestamp;
exports.fromTimestamp = fromTimestamp;
exports.getMantissaDecimalString = getMantissaDecimalString;
exports.sjcl = require('sjcl');
require('./sjcl-custom');
exports.sjcl = require('sjcl-extended');
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,27 +0,0 @@
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
var Seed = require('ripple-lib').Seed;
describe('SJCL ECDSA Canonicalization', function() {
describe('canonicalizeSignature', function() {
it('should canonicalize non-canonical signatures', function () {
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
var key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
var rs = sjcl.codec.hex.toBits("27ce1b914045ba7e8c11a2f2882cb6e07a19d4017513f12e3e363d71dc3fff0fb0a0747ecc7b4ca46e45b3b32b6b2a066aa0249c027ef11e5bce93dab756549c");
rs = sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature.call(key._secret, rs);
assert.strictEqual(sjcl.codec.hex.fromBits(rs), "27ce1b914045ba7e8c11a2f2882cb6e07a19d4017513f12e3e363d71dc3fff0f4f5f8b813384b35b91ba4c4cd494d5f8500eb84aacc9af1d6403cab218dfeca5");
});
it('should not touch canonical signatures', function () {
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
var key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
var rs = sjcl.codec.hex.toBits("5c32bc2b4d34e27af9fb66eeea0f47f6afb3d433658af0f649ebae7b872471ab7d23860688aaf9d8131f84cfffa6c56bf9c32fd8b315b2ef9d6bcb243f7a686c");
rs = sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature.call(key._secret, rs);
assert.strictEqual(sjcl.codec.hex.fromBits(rs), "5c32bc2b4d34e27af9fb66eeea0f47f6afb3d433658af0f649ebae7b872471ab7d23860688aaf9d8131f84cfffa6c56bf9c32fd8b315b2ef9d6bcb243f7a686c");
});
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,260 +0,0 @@
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
describe('ECDSA signing with recoverable public key', function(){
describe('Sign and recover public key from signature', function(){
it('should recover public keys from signatures it generates', function(){
var messages = [{
message: 'Hello world!',
secret_hex: '9931c08f61f127d5735fa3c60e702212ce7ed9a2ac90d5dbade99c689728cd9b',
random_value: '5473a3dbdc13ec9efbad7f7f929fbbea404af556a48041dd9d41d29fdbc989ad',
hash_function: sjcl.hash.sha512.hash
// signature: 'AAAAGzFa1pYjhssCpDFZgFSnYQ8qCnMkLaZrg0mXZyNQ2NxgMQ8z9U3ngYerxSZCEt3Q4raMIpt03db7jDNGbfmHy8I='
}, {
// Correct recovery value for this one is 0
message: 'ua5pdcG0I1JuhSr9Fwai2UoZ9ll5leUtHE5NzSSNnPkw8nSPH5mT1gE1fe0sn',
secret_hex: '84814318ffe6e612694ad59b9084b7b66d68b6979567c619171a67b05e2b654b',
random_value: '14261d30b319709c10ab13cabe595313b99dd2d5c76b8b38d7eb445f0b81cc9a',
hash_function: sjcl.hash.sha512.hash
// signature: 'AAAAHGjpBM7wnTHbPGo0TXsxKbr+d7KvACuJ/eGQsp3ZJfOOQHszaciRo3ClenwKixcquFcBlaVfHlOc3JWOZq1RjpQ='
}, {
// Correct recovery value for this one is 1
message: 'rxc76UnmVTp',
secret_hex: '37eac47c212be8ea8372f506b11673c281cd9ea29a035c2c9e90d027c3dbecc6',
random_value: '61b53ca6de0543f911765ae216a3a4d851918a0733fba9ac80cf29de5bec8032',
hash_function: sjcl.hash.sha256.hash
// signature: 'AAAAG8L/yOA3nNqK4aOiQWJmOaWvkvr3NoTk6wCdX97U3qowdgFd98UK3evWV16qO3RHgFMEnUW/Vt4+kcidqW6hMo0='
}];
var curve = sjcl.ecc.curves['k256'];
for (var m = 0; m < messages.length; m++) {
var message = messages[m].message;
var secret_hex = messages[m].secret_hex;
var random_value = messages[m].random_value;
var hash_function = messages[m].hash_function;
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var pub_val_point = secret_key._curve.G.mult(secret_key._exponent);
var public_key = new sjcl.ecc.ecdsa.publicKey(curve, pub_val_point);
var hash = hash_function(message);
var recoverable_signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var recovered_public_key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, recoverable_signature);
assert.deepEqual(public_key.get().x, recovered_public_key.get().x, 'The x value for the recovered public key did not match for message: ' + message + '. Expected: ' + public_key.get().x.toString() + '. Actual: ' + recovered_public_key.get().x.toString());
assert.deepEqual(public_key.get().y, recovered_public_key.get().y, 'The y value for the recovered public key did not match for message: ' + message + '. Expected: ' + public_key.get().y.toString() + '. Actual: ' + recovered_public_key.get().y.toString());
}
});
});
describe('signWithRecoverablePublicKey', function(){
// it('should produce the same values as bitcoinjs-lib\'s implementation', function(){
// // TODO: figure out why bitcoinjs-lib and this produce different signature values
// var curve = sjcl.ecc.curves['k256'];
// var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
// var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
// var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
// // var public_key = '0217b9f5b3ba8d550f19fdfb5233818cd27d19aaea029b667f547f5918c307ed3b';
// var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
// var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
// var bitcoin_signature_base64 = 'IJPzXewhO1CORRx14FROzZC8ne4v0Me94UZoBKH15e4pcSgeYiYeKZ4PJOBI/D5yqUOhemO+rKKHhE0HL66kAcM=';
// var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
// var signature_base64 = sjcl.codec.base64.fromBits(signature);
// assert.equal(signature_base64, bitcoin_signature_base64);
// });
it('should produce an error if the hash is not given as a bitArray', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){
secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
}, /(?=.*hash)(?=.*bitArray).+/);
});
it('should return a bitArray', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
assert(typeof signature === 'object' && signature.length > 0 && typeof signature[0] === 'number');
});
it('should return a bitArray where the first word contains the recovery factor', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var recovery_factor = signature[0] - 27;
assert(recovery_factor >= 0 && recovery_factor < 4);
});
});
describe('recoverFromSignature', function(){
// it('should be able to recover public keys from bitcoinjs-lib\'s implementation', function(){
// // TODO: figure out why bitcoinjs-lib and this produce different signature values
// var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
// var signature = sjcl.codec.base64.toBits('IJPzXewhO1CORRx14FROzZC8ne4v0Me94UZoBKH15e4pcSgeYiYeKZ4PJOBI/D5yqUOhemO+rKKHhE0HL66kAcM=');
// var public_key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
// });
it('should produce an error if the signature given does not have the recovery factor prefix', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.sign(hash, 0, random_value);
assert.throws(function(){
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
}, /(?=.*signature)(?=.*recovery factor)(?=.*public key).*/);
});
it('should produce an error if it is not given both the hash and the signature', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
assert.throws(function(){
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash);
}, /(?=.*hash\ and\ signature)(?=.*recover\ public\ key).*/);
assert.throws(function(){
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(signature);
}, /(?=.*hash\ and\ signature)(?=.*recover\ public\ key).*/);
});
it('should produce an error if it cannot generate a valid public key from the the signature', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = sjcl.codec.base64.toBits('IJPzXewhO1CORRx14FROzZC8ne4v0Me94UZoBKH15e4pcSgeYiYeKZ4PJOBI/D5yqUOhemO+rKKHhE0HL66kAcM=');
signature[0] = 27;
signature[3] = 0 - signature[3];
assert.throws(function(){
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
}, /(?=.*Cannot\ recover\ public\ key).*/);
});
it('should return a publicKey object', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
assert(key instanceof sjcl.ecc.ecdsa.publicKey);
});
it('tampering with the signature should produce a different public key, if it produces a valid one at all', function(){
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
signature[3]++;
var original_public_key = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(secret_key._exponent));
var recovered_public_key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
assert.notDeepEqual(original_public_key.get().x, recovered_public_key.get().x);
assert.notDeepEqual(original_public_key.get().y, recovered_public_key.get().y);
});
it('should return a publicKey object, while using the overridden c256 curve', function(){
var curve = sjcl.ecc.curves['c256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
assert(key instanceof sjcl.ecc.ecdsa.publicKey);
});
});
});

View File

@@ -1,51 +0,0 @@
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
describe('SJCL Extramath', function() {
describe('setBitM', function() {
it('0x0f set bit 4 => 0x1f', function () {
var val = new sjcl.bn("0f");
val.setBitM(4);
assert.strictEqual(val.toString(), '0x1f');
});
it('0x0f set bit 23 => 0x80000f', function () {
var val = new sjcl.bn("0f");
val.setBitM(23);
assert.strictEqual(val.toString(), '0x80000f');
});
it('0x0f set bit 24 => 0x100000f', function () {
var val = new sjcl.bn("0f");
val.setBitM(24);
assert.strictEqual(val.toString(), '0x100000f');
});
});
describe('testBit', function() {
it('0x03', function () {
var val = new sjcl.bn("03");
assert.strictEqual(val.testBit(0), 1);
assert.strictEqual(val.testBit(1), 1);
assert.strictEqual(val.testBit(2), 0);
});
it('0x1000000', function () {
var val = new sjcl.bn("1000000");
assert.strictEqual(val.testBit(25), 0);
assert.strictEqual(val.testBit(24), 1);
assert.strictEqual(val.testBit(23), 0);
assert.strictEqual(val.testBit( 1), 0);
assert.strictEqual(val.testBit( 0), 0);
});
it('0xff7fffffff', function () {
var val = new sjcl.bn("ff7fffffff");
assert.strictEqual(val.testBit(32), 1);
assert.strictEqual(val.testBit(31), 0);
assert.strictEqual(val.testBit(30), 1);
assert.strictEqual(val.testBit(24), 1);
assert.strictEqual(val.testBit(23), 1);
assert.strictEqual(val.testBit(22), 1);
assert.strictEqual(val.testBit( 1), 1);
assert.strictEqual(val.testBit( 0), 1);
});
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,100 +0,0 @@
/* eslint-disable max-len */
/* eslint new-cap: [2, {newIsCapExceptions: ["bn"]}] */
'use strict';
const assert = require('assert');
const sjcl = require('ripple-lib').sjcl;
// fix normalize bug in scjl:
// https://github.com/bitwiseshiftleft/sjcl/pull/221
function normalizeFix(bn) {
while (bn.limbs.length > 0 && bn.limbs[bn.limbs.length - 1] === 0) {
bn.limbs.pop();
}
return bn;
}
function testExp(vec) {
const actual = normalizeFix(new sjcl.bn(vec.g).powermod(
new sjcl.bn(vec.e), new sjcl.bn(vec.m)));
const expected = new sjcl.bn(vec.r);
assert.strictEqual(actual.toString(), expected.toString());
}
describe('SJCL Montgomery Exponentiation', function() {
describe('powermod', function() {
it('test 1', function() {
testExp({
g: 2,
e: 3,
m: 3,
r: 2
});
});
it('test 2', function() {
testExp({
g: 2,
e: '10000000000000000000000000',
m: 1337,
r: 1206
});
});
it('test 3', function() {
testExp({
g: 17,
e: 90,
m: 34717861147,
r: 28445204336
});
});
it('test 4', function() {
testExp({
g: 2,
e: '0x844A000000000000000000000',
m: 13,
r: 9
});
});
it('test 5', function() {
testExp({
g: 2,
e: 0x1010,
m: 131,
r: 59
});
});
it('test 6', function() {
testExp({
g: 2,
e: '43207437777777877617151',
m: 13,
r: 2
});
});
it('test 7', function() {
testExp({
g: 2,
e: '389274238947216444871600001871964319565192765874149',
m: 117,
r: 44
});
});
it('test 8', function() {
testExp({
g: 2,
e: '89457115510016156219817846189181057618965150496979174671534084187',
m: '1897166415676096761',
r: '16840615e646a4c5c8d'
});
});
it('test 9', function() {
testExp({
g: '4c3399bebab284bc7f9056efe17ea39db324ffa1d52dc1542eb16a749570789359f192535b7bcb514b36be9cdb4fb2a6ba3ad6b4248034e9a1d2a8612cd9d885242b4f679524121b74f79d7db14859fccde1c0dfe6ef002dcbc777ab5fcf4432',
e: '000322e6b6dafe138ccd6a991977d19',
m: 'a5091daa41997943e3c98469e93377f668d05d8059bc53f72aaacdac3729a3070dc7439a5171160bf9ec2826b7191b03b0e84b28e14dd376de35d29a96f686666e053ab62a41ebc2b5f52e8cf06254100fd153a1cda4485f170c39c54689e52d',
r: '554336ea044782d29f091117cfeaeee2334b4242bd7428d0bba3ce5325781dc219e891e54698cb0193ffe7c6fc07f1808f6685e64b6a082815f6afd2e16c7a61316b5e3e59cd8b3984d5a76c8e173f8615f7dceac0c99e27e4abfb1278dfa67f'
});
});
});
});
// vim:sw=2:sts=2:ts=8:et