mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
Remove sjcl-custom in favor of sjcl-extended module in separate repo
This commit is contained in:
8
npm-shrinkwrap.json
generated
8
npm-shrinkwrap.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
@@ -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() +
|
||||
')';
|
||||
};
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user