<commit_before>const crypto = require('crypto'),
      bignum = require('bignum'),
      assert = require('assert'),
      ALG = 'sha256';

const zero = bignum(0);

/*
 * If a conversion is explicitly specified with the operator PAD(),
 * the integer will first be implicitly converted, then the resultant
 * byte-string will be left-padded with zeros (if necessary) until its
 * length equals the implicitly-converted length of N.
 *
 * params:
 *         n (buffer)       Number to pad
 *         N (buffer)       N
 *
 * returns: buffer
 */
function pad(n, N) {
  var padding = N.length - n.length;
  assert(padding > -1, "Negative padding.  Very uncomfortable.");
  var result = new Buffer(N.length);
  result.fill(0, 0, padding);
  n.copy(result, padding);
  return result;
};

function assertIsBuffer(arg, argname) {
  argname = argname || "arg";
  assert(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
}

function assertIsNBuffer(arg, Nbuf, argname) {
  argname = argname || "arg";
  assert(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
  assert.equal(arg.length, Nbuf.length);
}

function assertIsBignum(arg) {
  // I'm not actually sure how to test this.
  assert(arg.shiftRight, "arg might not be a bignum");
}

/*
 * compute the intermediate value x as a hash of three buffers:
 * salt, identity, and password.  And a colon.  FOUR buffers.
 *
 *      x = H(s | H(I | ":" | P))
 *
 * params:
 *         s (buffer)       salt
 *         I (buffer)       user identity
 *         P (buffer)       user password
 *
 * returns: x (bignum)      user secret
 */
function getx(s, I, P, alg) {
  assertIsBuffer(s, "salt (s)");
  assertIsBuffer(I, "identity (I)");
  assertIsBuffer(P, "password (P)");
  alg = alg || ALG;
  var hashIP = crypto.createHash(alg)
    .update(Buffer.concat([I, new Buffer(':'), P]))
    .digest();
  var hashX = crypto.createHash(alg)
    .update(s)
    .update(hashIP)
    .digest();
  return bignum.fromBuffer(hashX);
};

/*
 * The verifier is calculated as described in Section 3 of [SRP-RFC].
 * We give the algorithm here for convenience.
 *
 * The verifier (v) is computed based on the salt (s), user name (I),
 * password (P), and group parameters (N, g).
 *
 *         x = H(s | H(I | ":" | P))
 *         v = g^x % N
 *
 * params:
 *         s (buffer)       salt
 *         I (buffer)       user identity
 *         P (buffer)       user password
 *         N (bignum)       group parameter N
 *         g (bignum)       generator
 *         alg (string)     default = ALG
 *
 * returns: bignum
 */
function getv(s, I, P, N, g, alg) {
  assertIsBuffer(s, "salt (s)");
  assertIsBuffer(I, "identity (I)");
  assertIsBuffer(P, "password (P)");
  assertIsBignum(N);
  assertIsBignum(g);
  alg = alg || ALG;
  return g.powm(getx(s, I, P, alg), N);
};

function getkBuffer(N, g, alg) {
  assertIsBuffer(N);
  assertIsBuffer(g);
  alg = alg || ALG;
  return crypto
    .createHash(alg)
    .update(N)
    .update(pad(g, N))
    .digest();
}

/*
 * calculate the SRP-6 multiplier
 *
 * params:
 *         N (bignum)       group parameter N
 *         g (bignum)       generator
 *         alg (string)     default = ALG
 *
 * returns: bignum
 */
function getk(N, g, alg) {
  assertIsBignum(N);
  assertIsBignum(g);
  return bignum.fromBuffer(getkBuffer(N.toBuffer(), g.toBuffer(), alg));
};

/*
 * Generate a random key
 *
 * params:
 *         bytes (int)      length of key (default=32)
 *         callback (func)  function to call with err,key
 *
 * returns: nothing, but runs callback with a Buffer
 */
function genKey(bytes, callback) {
  // bytes is optional
  if (arguments.length < 2) {
    callback = bytes;
    bytes = 32;
  }
  if (typeof callback !== 'function') {
    throw("Callback required");
  }
  crypto.randomBytes(bytes, function(err, buf) {
    if (err) return callback (err);
    return callback(null, buf);
  });
};

/*
 * The server key exchange message also contains the server's public
 * value (B).  The server calculates this value as B = k*v + g^b % N,
 * where b is a random number that SHOULD be at least 256 bits in length
 * and k = H(N | PAD(g)).
 *
 * Note: as the tests imply, the entire expression is mod N.
 *
 * params:
 *         v (bignum)       verifier (stored)
 *         b (bignum)       server secret exponent
 *
 * returns: B (bignum)      the server public message
 */
function getB(v, g, b, N, alg) {
  assertIsBignum(v);
  assertIsBignum(g);
  assertIsBignum(b);
  assertIsBignum(N);
  alg = alg || ALG;
  var k = getk(N, g, alg);
  var r = k.mul(v).add(g.powm(b, N)).mod(N);
  return r;
};

/*
 * The client key exchange message carries the client's public value
 * (A).  The client calculates this value as A = g^a % N, where a is a
 * random number that SHOULD be at least 256 bits in length.
 *
 * Note: for this implementation, we take that to mean 256/8 bytes.
 *
 * params:
 *         a (bignum)       client secret exponent
 *
 * returns A (bignum)       the client public message
 */
function getA(g, a, N) {
  assertIsBignum(g);
  assertIsBignum(a);
  assertIsBignum(N);
  if (Math.ceil(a.bitLength() / 8) < 256/8) {
    console.warn("getA: client key length", a.bitLength(), "is less than the recommended 256");
  }
  return g.powm(a, N);
};

/*
 * getuBuffer() hashes the two public messages together, to obtain a
 * scrambling parameter "u" which cannot be predicted by either party ahead
 * of time. This makes it safe to use the message ordering defined in the
 * SRP-6a paper, in which the server reveals their "B" value before the
 * client commits to their "A" value.
 *
 * params:
 *        A (buffer)      client public message
 *        B (buffer)      server public message
 *
 * returns u (Buffer)     shared scrambling parameter
 */
function getuBuffer(A, B, N, alg) {
  assertIsBuffer(A);
  assertIsBuffer(B);
  assertIsBuffer(N);
  alg = alg || ALG;
  return crypto
    .createHash(alg)
    .update(pad(A, N))
    .update(pad(B, N))
    .digest()
}

/*
 * Random scrambling parameter u
 *
 * params:
 *         A (bignum)       client ephemeral public key
 *         B (bignum)       server ephemeral public key
 *         N (bignum)       group parameter N
 *
 * returns: u (bignum)      shared scrambling parameter
 */
function getu(A, B, N, alg) {
  assertIsBignum(A);
  assertIsBignum(B);
  assertIsBignum(N);
  return bignum.fromBuffer(getuBuffer(A.toBuffer(), B.toBuffer(), N.toBuffer(), alg));
};

/*
 * The TLS premaster secret as calculated by the client
 *
 * params:
 *         s (buffer)       salt (read from server)
 *         I (buffer)       user identity (read from user)
 *         P (buffer)       user password (read from user)
 *         N (bignum)       group parameter N (known in advance)
 *         g (bignum)       generator for N (known in advance)
 *         a (bignum)       ephemeral private key (generated for session)
 *         B (bignum)       server ephemeral public key (read from server)
 *
 * returns: bignum
 */
function client_getS(s, I, P, N, g, a, B, alg) {
  assertIsBuffer(s);
  assertIsBuffer(I);
  assertIsBuffer(P);
  assertIsBignum(N);
  assertIsBignum(g);
  assertIsBignum(a);
  assertIsBignum(B);
  if (zero.ge(B) || N.le(B))
    throw new Error("invalid server-supplied 'B', must be 1..N-1");
  var A = getA(g, a, N);
  var u = getu(A, B, N, alg);
  var k = getk(N, g, alg);
  var x = getx(s, I, P, alg);
  return B.sub(k.mul(g.powm(x, N))).powm(a.add(u.mul(x)), N).mod(N);
};

/*
 * The TLS premastersecret as calculated by the server
 *
 * params:
 *         s (bignum)       salt (stored on server)
 *         v (bignum)       verifier (stored on server)
 *         N (bignum)       group parameter N (known in advance)
 *         g (bignum)       generator for N (known in advance)
 *         A (bignum)       ephemeral client public key (read from client)
 *         b (bignum)       server ephemeral private key (generated for session)
 *
 * returns: bignum
 */
function server_getS(s, v, N, g, A, b, alg) {
  assertIsBignum(s);
  assertIsBignum(v);
  assertIsBignum(N);
  assertIsBignum(g);
  assertIsBignum(A);
  assertIsBignum(b);
  if (zero.ge(A) || N.le(A))
    throw new Error("invalid client-supplied 'A', must be 1..N-1");
  var k = getk(N, g, alg);
  var B = getB(v, g, b, N, alg);
  var u = getu(A, B, N, alg);
  return A.mul(v.powm(u, N)).powm(b, N).mod(N);
};

/*
 * Compute the shared session key K from S
 *
 * params:
 *         S (buffer)       Session key
 *         N (buffer)       group parameter N
 *
 * returns: buffer
 */
function getK(S, N, alg) {
  assertIsBuffer(S);
  assertIsBuffer(N);
  alg = alg || ALG;
  var S_pad = pad(S, N);
  return crypto
      .createHash(alg)
      .update(S_pad)
      .digest();
};

function getM(A, B, S, N, alg) {
  assertIsBuffer(A);
  assertIsBuffer(B);
  assertIsBuffer(S);
  alg = alg || ALG;
  return crypto
    .createHash(alg)
    .update(pad(A, N))
    .update(pad(B, N))
    .update(pad(S, N))
    .digest();
}

module.exports = {
  getx: function (s, I, P, alg) {
    return getx(s, I, P, alg).toBuffer()
  },
  getv: function (s, I, P, N, g, alg) {
    return getv(
      s,
      I,
      P,
      bignum.fromBuffer(N),
      bignum.fromBuffer(g),
      alg
    ).toBuffer()
  },
  genKey: genKey,
  getB: function (v, g, b, N, alg) {
    return getB(
      bignum.fromBuffer(v),
      bignum.fromBuffer(g),
      bignum.fromBuffer(b),
      bignum.fromBuffer(N),
      alg
    ).toBuffer()
  },
  getA: function (g, a, N) {
    return getA(
      bignum.fromBuffer(g),
      bignum.fromBuffer(a),
      bignum.fromBuffer(N)
    ).toBuffer()
  },
  client_getS: function (s, I, P, N, g, a, B, alg) {
    return client_getS(
      s,
      I,
      P,
      bignum.fromBuffer(N),
      bignum.fromBuffer(g),
      bignum.fromBuffer(a),
      bignum.fromBuffer(B),
      alg
    ).toBuffer()
  },
  server_getS: function (s, v, N, g, A, b, alg) {
    return server_getS(
      bignum.fromBuffer(s),
      bignum.fromBuffer(v),
      bignum.fromBuffer(N),
      bignum.fromBuffer(g),
      bignum.fromBuffer(A),
      bignum.fromBuffer(b),
      alg
    ).toBuffer()
  },
  getu: getuBuffer,
  getk: getkBuffer,
  getK: getK,
  getM: getM
}<commit_msg>srp.js: convert API to accept parameter sets<commit_after>const crypto = require('crypto'),
      bignum = require('bignum'),
      assert = require('assert');

const zero = bignum(0);

function assert_(val, msg) {
  if (!val)
    throw new Error(msg||"assertion");
}

/*
 * If a conversion is explicitly specified with the operator PAD(),
 * the integer will first be implicitly converted, then the resultant
 * byte-string will be left-padded with zeros (if necessary) until its
 * length equals the implicitly-converted length of N.
 *
 * params:
 *         n (buffer)       Number to pad
 *         len (int)        length of the resulting Buffer
 *
 * returns: buffer
 */
function padTo(n, len) {
  assertIsBuffer(n, "n");
  var padding = len - n.length;
  assert_(padding > -1, "Negative padding.  Very uncomfortable.");
  var result = new Buffer(len);
  result.fill(0, 0, padding);
  n.copy(result, padding);
  assert.equal(result.length, len);
  return result;
};

function padToN(number, params) {
  assertIsBignum(number);
  return padTo(number.toBuffer(), params.N_length_bits/8);
}

function assertIsBuffer(arg, argname) {
  argname = argname || "arg";
  assert_(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
}

function assertIsNBuffer(arg, params, argname) {
  argname = argname || "arg";
  assert_(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
  assert.equal(arg.length, params.N_length_bits/8);
}

function assertIsBignum(arg) {
  // I'm not actually sure how to test this.
  assert_(arg.shiftRight, "arg might not be a bignum");
}

/*
 * compute the intermediate value x as a hash of three buffers:
 * salt, identity, and password.  And a colon.  FOUR buffers.
 *
 *      x = H(s | H(I | ":" | P))
 *
 * params:
 *         s (buffer)       salt
 *         I (buffer)       user identity
 *         P (buffer)       user password
 *
 * returns: x (bignum)      user secret
 */
function getx(params, salt, I, P) {
  assertIsBuffer(salt, "salt (salt)");
  assertIsBuffer(I, "identity (I)");
  assertIsBuffer(P, "password (P)");
  var hashIP = crypto.createHash(params.hash)
    .update(Buffer.concat([I, new Buffer(':'), P]))
    .digest();
  var hashX = crypto.createHash(params.hash)
    .update(salt)
    .update(hashIP)
    .digest();
  return bignum.fromBuffer(hashX);
};

/*
 * The verifier is calculated as described in Section 3 of [SRP-RFC].
 * We give the algorithm here for convenience.
 *
 * The verifier (v) is computed based on the salt (s), user name (I),
 * password (P), and group parameters (N, g).
 *
 *         x = H(s | H(I | ":" | P))
 *         v = g^x % N
 *
 * params:
 *         s (buffer)       salt
 *         I (buffer)       user identity
 *         P (buffer)       user password
 *         N (bignum)       group parameter N
 *         g (bignum)       generator
 *         alg (string)     default = ALG
 *
 * returns: bignum
 */
function getv(params, salt, I, P) {
  assertIsBuffer(salt, "salt (salt)");
  assertIsBuffer(I, "identity (I)");
  assertIsBuffer(P, "password (P)");
  return params.g.powm(getx(params, salt, I, P), params.N);
};

function getkBuffer(params) {
  return crypto
    .createHash(params.hash)
    .update(padToN(params.N, params))
    .update(padToN(params.g, params))
    .digest();
}

/*
 * calculate the SRP-6 multiplier
 *
 * params:
 *         N (bignum)       group parameter N
 *         g (bignum)       generator
 *         alg (string)     default = ALG
 *
 * returns: bignum
 */
function getk(params) {
  return bignum.fromBuffer(getkBuffer(params));
};

/*
 * Generate a random key
 *
 * params:
 *         bytes (int)      length of key (default=32)
 *         callback (func)  function to call with err,key
 *
 * returns: nothing, but runs callback with a Buffer
 */
function genKey(bytes, callback) {
  // bytes is optional
  if (arguments.length < 2) {
    callback = bytes;
    bytes = 32;
  }
  if (typeof callback !== 'function') {
    throw("Callback required");
  }
  crypto.randomBytes(bytes, function(err, buf) {
    if (err) return callback (err);
    return callback(null, buf);
  });
};

/*
 * The server key exchange message also contains the server's public
 * value (B).  The server calculates this value as B = k*v + g^b % N,
 * where b is a random number that SHOULD be at least 256 bits in length
 * and k = H(N | PAD(g)).
 *
 * Note: as the tests imply, the entire expression is mod N.
 *
 * params:
 *         v (bignum)       verifier (stored)
 *         b (bignum)       server secret exponent
 *
 * returns: B (bignum)      the server public message
 */
function getB(params, v, b) {
  assertIsBignum(v);
  assertIsBignum(b);
  var k = getk(params);
  var N = params.N;
  var r = k.mul(v).add(params.g.powm(b, N)).mod(N);
  return r;
};

/*
 * The client key exchange message carries the client's public value
 * (A).  The client calculates this value as A = g^a % N, where a is a
 * random number that SHOULD be at least 256 bits in length.
 *
 * Note: for this implementation, we take that to mean 256/8 bytes.
 *
 * params:
 *         a (bignum)       client secret exponent
 *
 * returns A (bignum)       the client public message
 */
function getA(params, a) {
  assertIsBignum(a);
  if (Math.ceil(a.bitLength() / 8) < 256/8) {
    console.warn("getA: client key length", a.bitLength(), "is less than the recommended 256");
  }
  return params.g.powm(a, params.N);
};

/*
 * getuBuffer() hashes the two public messages together, to obtain a
 * scrambling parameter "u" which cannot be predicted by either party ahead
 * of time. This makes it safe to use the message ordering defined in the
 * SRP-6a paper, in which the server reveals their "B" value before the
 * client commits to their "A" value.
 *
 * params:
 *        A (buffer)      client public message
 *        B (buffer)      server public message
 *
 * returns u (Buffer)     shared scrambling parameter
 */
function getuBuffer(params, A, B) {
  assertIsBuffer(A);
  assertIsBuffer(B);
  return crypto
    .createHash(params.hash)
    .update(padTo(A, params.N_length_bits/8))
    .update(padTo(B, params.N_length_bits/8))
    .digest();
}

/*
 * Random scrambling parameter u
 *
 * params:
 *         A (bignum)       client ephemeral public key
 *         B (bignum)       server ephemeral public key
 *         N (bignum)       group parameter N
 *
 * returns: u (bignum)      shared scrambling parameter
 */
function getu(params, A, B) {
  assertIsBignum(A);
  assertIsBignum(B);
  return bignum.fromBuffer(getuBuffer(params, A.toBuffer(), B.toBuffer()));
};

/*
 * The TLS premaster secret as calculated by the client
 *
 * params:
 *         s (buffer)       salt (read from server)
 *         I (buffer)       user identity (read from user)
 *         P (buffer)       user password (read from user)
 *         N (bignum)       group parameter N (known in advance)
 *         g (bignum)       generator for N (known in advance)
 *         a (bignum)       ephemeral private key (generated for session)
 *         B (bignum)       server ephemeral public key (read from server)
 *
 * returns: bignum
 */
function client_getS(params, salt, I, P, a, B) {
  assertIsBuffer(salt);
  assertIsBuffer(I);
  assertIsBuffer(P);
  assertIsBignum(a);
  assertIsBignum(B);
  var g = params.g;
  var N = params.N;
  if (zero.ge(B) || N.le(B))
    throw new Error("invalid server-supplied 'B', must be 1..N-1");
  var A = getA(params, a);
  var u = getu(params, A, B);
  var k = getk(params);
  var x = getx(params, salt, I, P);
  return B.sub(k.mul(g.powm(x, N))).powm(a.add(u.mul(x)), N).mod(N);
};

/*
 * The TLS premastersecret as calculated by the server
 *
 * params:
 *         s (bignum)       salt (stored on server)
 *         v (bignum)       verifier (stored on server)
 *         N (bignum)       group parameter N (known in advance)
 *         g (bignum)       generator for N (known in advance)
 *         A (bignum)       ephemeral client public key (read from client)
 *         b (bignum)       server ephemeral private key (generated for session)
 *
 * returns: bignum
 */
function server_getS(params, salt, v, A, b) {
  assertIsBignum(salt);
  assertIsBignum(v);
  assertIsBignum(A);
  assertIsBignum(b);
  var N = params.N;
  if (zero.ge(A) || N.le(A))
    throw new Error("invalid client-supplied 'A', must be 1..N-1");
  var k = getk(params);
  var B = getB(params, v, b);
  var u = getu(params, A, B);
  return A.mul(v.powm(u, N)).powm(b, N).mod(N);
};

/*
 * Compute the shared session key K from S
 *
 * params:
 *         S (buffer)       Session key
 *         N (buffer)       group parameter N
 *
 * returns: buffer
 */
function getK(params, S) {
  assertIsBuffer(S);
  var S_pad = padTo(S, params.N_length_bits/8);
  return crypto
      .createHash(params.hash)
      .update(S_pad)
      .digest();
};

function getM(params, A, B, S) {
  assertIsBuffer(A);
  assertIsBuffer(B);
  assertIsBuffer(S);
  return crypto
    .createHash(params.hash)
    .update(padTo(A, params.N_length_bits/8))
    .update(padTo(B, params.N_length_bits/8))
    .update(padTo(S, params.N_length_bits/8))
    .digest();
}

module.exports = {
  getx: function (params, salt, I, P) {
    return getx(params, salt, I, P).toBuffer();
  },
  getv: function (params, salt, I, P) {
    return getv(params, salt, I, P).toBuffer();
  },
  genKey: genKey,
  getB: function (params, v, b) {
    return getB(params, bignum.fromBuffer(v), bignum.fromBuffer(b)).toBuffer();
  },
  getA: function (params, a) {
    return getA(params, bignum.fromBuffer(a)).toBuffer();
  },
  client_getS: function (params, salt, I, P, a, B) {
    return client_getS(params, salt, I, P,
                       bignum.fromBuffer(a), bignum.fromBuffer(B)).toBuffer();
  },
  server_getS: function (params, salt, v, A, b) {
    return server_getS(params, bignum.fromBuffer(salt), bignum.fromBuffer(v),
                       bignum.fromBuffer(A), bignum.fromBuffer(b)).toBuffer();
  },
  getu: getuBuffer,
  getk: getkBuffer,
  getK: getK,
  getM: getM
}
{'fraction_non_alphanumeric': 0.1377115507968619, 'fraction_numerical': 0.0038979622045690037, 'mean_word_length': 2.6817438692098094, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5466279', 'n_tokens_mistral': 7150, 'n_tokens_neox': 7024, 'n_words': 2143}