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));
};
  function bits2int(bits) {

    var blen = sjcl.bitArray.bitLength(bits);

    if (blen > qlen) {
      return sjcl.bn.fromBits(sjcl.bitArray.clamp(bits, qlen));
    }
    return sjcl.bn.fromBits(bits);
  }
/**
 *  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) {

  var 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)))
  };
}
 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);
 });
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;
};
Exemple #6
0
    /**
     * Get a random number based on the curve
     *
     * @param {sjcl.ecc.curve} curve
     * @param {number} paranoia
     * @returns {sjcl.bn}
     */
    getRandomNumber(curve, paranoia) {
        if (!(curve instanceof sjcl.ecc.curve)) {
            throw new exception.InvalidCurve(curve);
        }

        paranoia = paranoia || 0;

        return sjcl.bn.random(curve.r, paranoia);
    }
 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);
 });
Exemple #8
0
    .end(function(err, resp) {
      if (err || !resp) {
        return fn(new Error('Could not query PAKDF server ' + opts.host));
      }

      var data = resp.body || resp.text ? JSON.parse(resp.text) : {};

      if (data.result !== 'success') {
        return fn(new Error('Could not query PAKDF server '+opts.host));
      }

      var iSignres = new sjcl.bn(String(data.signres));
      var iRandomInv = iRandom.inverseMod(iModulus);
      var iSigned    = iSignres.mulmod(iRandomInv, iModulus);
      var key        = iSigned.toBits();
      var result     = { };

      tokens.forEach(function(token) {
        result[token] = keyHash(key, token);
      });

      fn(null, result);
    });
sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(
    hash, paranoia, k_for_testing) {

  var self = this;

  // Convert hash to bits and determine encoding for output
  var 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
  var standard_signature = self.sign(hash_bits, paranoia, k_for_testing);
  var canonical_signature = self.canonicalizeSignature(standard_signature);

  // Extract r and s signature components from canonical signature
  var r_and_s = getRandSFromSignature(self._curve, canonical_signature);

  // Rederive public key
  var public_key = self._curve.G.mult(sjcl.bn.fromBits(self.get()));

  // Determine recovery factor based on which possible value
  // returns the correct public key
  var 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
  var value_to_prepend = recovery_factor + 27;

  var final_signature_bits = sjcl.bitArray.concat([value_to_prepend],
    canonical_signature);

  // Return value in bits
  return final_signature_bits;

};
 it('0x0f set bit 4 => 0x1f', function () {
   var val = new sjcl.bn("0f");
   val.setBitM(4);
   assert.strictEqual(val.toString(), '0x1f');
 });
 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);
 });
function(hash, hashObject) {

  var curve = this._curve
  var qlen = this._curveBitLength;

  /* Utility functions */
  /* used to generate k and v */
  function repeat(str, times) {
    return (new Array(times + 1)).join(str);
  }
  function bits2int(bits) {

    var blen = sjcl.bitArray.bitLength(bits);

    if (blen > qlen) {
      return sjcl.bn.fromBits(sjcl.bitArray.clamp(bits, qlen));
    }
    return sjcl.bn.fromBits(bits);
  }
  function int2octets(integer) {
    var iModQ = integer.mulmod(new sjcl.bn(1), curve.r);

    var rlen = 8 * Math.ceil(qlen / 8);
    var ilen = iModQ.bitLength();

    return sjcl.bitArray.concat(
      sjcl.codec.hex.toBits(repeat('0', Math.ceil((rlen - ilen) / 4))),
      iModQ.toBits()
    );
  }
  function bits2octets(bits) {
    return int2octets(bits2int(bits).mulmod(new sjcl.bn(1), curve.r));
  }

  function hmac() {
    var params = Array.prototype.slice.call(arguments);    

    var key = params.shift();
    var hmacK = new sjcl.misc.hmac(key, hashObject);

    var bits = params[0];
    for (var i = 1; i < params.length; i++) {
      bits = sjcl.bitArray.concat(bits, params[i]); 
    }

    return hmacK.encrypt(bits);
  }

  var hlen = sjcl.bitArray.bitLength(hash);
  var x = sjcl.bn.fromBits(this.get());

  var k = sjcl.codec.hex.toBits(repeat('00', Math.ceil(hlen / 8)));
  var v = sjcl.codec.hex.toBits(repeat('01', Math.ceil(hlen / 8)));

  k = hmac(
    k,
    v, sjcl.codec.hex.toBits('00'), int2octets(x), bits2octets(hash)
  );

  v = hmac(k, v);

  k = hmac(
    k,
    v, sjcl.codec.hex.toBits('01'), int2octets(x), bits2octets(hash)
  );

  v = hmac(k, v);
  v = hmac(k, v);

  var T = sjcl.bn.fromBits(v); while (
    T.bitLength() < qlen
  ) {
    v = hmac(k, v);
    T = sjcl.bn.fromBits(sjcl.bitArray.concat(T.toBits(), v));
  }
  T = bits2int(T.toBits());

  while (!(T.greaterEquals(1)) || (T.greaterEquals(curve.r))) {
    k = hmac(
      k,
      v, sjcl.codec.hex.toBits('00')
    );

    v = hmac(k, v);
    T = sjcl.bn.fromBits(v);
    while (
      T.bitLength() < qlen
    ) {
      v = hmac(k, v);
      T = sjcl.bn.fromBits(sjcl.bitArray.concat(T.toBits(), v));
    }
    T = bits2int(T.toBits());
  }

  return T;
};
/**
 *  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) {

  var field_order = curve.r;
  var field_modulus = curve.field.modulus;

  // Reduce the recovery_factor to the two bits used
  recovery_factor = recovery_factor & 3;

  // The less significant bit specifies whether the y coordinate
  // of the compressed point is even or not.
  var compressed_point_y_coord_is_even = recovery_factor & 1;

  // The more significant bit specifies whether we should use the
  // first or second candidate key.
  var use_second_candidate_key = recovery_factor >> 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
  var 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
  var alpha = x.mul(x).mul(x).add(curve.a.mul(x)).add(curve.b).mod(
    field_modulus);
  var 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
  var y;
  var 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
  var 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
  var message_e = sjcl.bn.fromBits(hash_bits);
  var 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);
  var signature_r_inv = signature_r.inverseMod(field_order);
  var 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;
}
Exemple #14
0
Crypt.derive = function(opts, purpose, username, secret, fn) {
  var tokens;

  if (purpose === 'login') {
    tokens = ['id', 'crypt'];
  } else {
    tokens = ['unlock'];
  }

  var iExponent = new sjcl.bn(String(opts.exponent));
  var iModulus  = new sjcl.bn(String(opts.modulus));
  var iAlpha    = new sjcl.bn(String(opts.alpha));

  var publicInfo = [ 'PAKDF_1_0_0', opts.host.length, opts.host, username.length, username, purpose.length, purpose ].join(':') + ':';
  var publicSize = Math.ceil(Math.min((7 + iModulus.bitLength()) >>> 3, 256) / 8);
  var publicHash = fdh(publicInfo, publicSize);
  var publicHex  = sjcl.codec.hex.fromBits(publicHash);
  var iPublic    = new sjcl.bn(String(publicHex)).setBitM(0);
  var secretInfo = [ publicInfo, secret.length, secret ].join(':') + ':';
  var secretSize = (7 + iModulus.bitLength()) >>> 3;
  var secretHash = fdh(secretInfo, secretSize);
  var secretHex  = sjcl.codec.hex.fromBits(secretHash);
  var iSecret    = new sjcl.bn(String(secretHex)).mod(iModulus);

  if (iSecret.jacobi(iModulus) !== 1) {
    iSecret = iSecret.mul(iAlpha).mod(iModulus);
  }

  var iRandom;

  for (;;) {
    iRandom = sjcl.bn.random(iModulus, 0);
    if (iRandom.jacobi(iModulus) === 1) {
      break;
    }
  }

  var iBlind   = iRandom.powermodMontgomery(iPublic.mul(iExponent), iModulus);
  var iSignreq = iSecret.mulmod(iBlind, iModulus);
  var signreq  = sjcl.codec.hex.fromBits(iSignreq.toBits());

  request.post(opts.url)
    .send({ info: publicInfo, signreq: signreq })
    .end(function(err, resp) {
      if (err || !resp) {
        return fn(new Error('Could not query PAKDF server ' + opts.host));
      }

      var data = resp.body || resp.text ? JSON.parse(resp.text) : {};

      if (data.result !== 'success') {
        return fn(new Error('Could not query PAKDF server '+opts.host));
      }

      var iSignres = new sjcl.bn(String(data.signres));
      var iRandomInv = iRandom.inverseMod(iModulus);
      var iSigned    = iSignres.mulmod(iRandomInv, iModulus);
      var key        = iSigned.toBits();
      var result     = { };

      tokens.forEach(function(token) {
        result[token] = keyHash(key, token);
      });

      fn(null, result);
    });
};