249 lines
5.5 KiB
JavaScript
249 lines
5.5 KiB
JavaScript
|
/**
|
||
|
* deps/ecc/index.js - Elliptic Curve Entry Point
|
||
|
*
|
||
|
* Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file.
|
||
|
*/
|
||
|
"use strict";
|
||
|
|
||
|
var forge = require("../../deps/forge"),
|
||
|
BigInteger = forge.jsbn.BigInteger,
|
||
|
ec = require("./math.js"),
|
||
|
CURVES = require("./curves.js");
|
||
|
|
||
|
// ### Helpers
|
||
|
function hex2bn(s) {
|
||
|
return new BigInteger(s, 16);
|
||
|
}
|
||
|
|
||
|
function bn2bin(bn, len) {
|
||
|
if (!len) {
|
||
|
len = Math.ceil(bn.bitLength() / 8);
|
||
|
}
|
||
|
len = len * 2;
|
||
|
|
||
|
var hex = bn.toString(16);
|
||
|
// truncate-left if too large
|
||
|
hex = hex.substring(Math.max(hex.length - len, 0));
|
||
|
// pad-left if too small
|
||
|
while (len > hex.length) {
|
||
|
hex = "0" + hex;
|
||
|
}
|
||
|
|
||
|
return Buffer.from(hex, "hex");
|
||
|
}
|
||
|
function bin2bn(s) {
|
||
|
if ("string" === typeof s) {
|
||
|
s = Buffer.from(s, "binary");
|
||
|
}
|
||
|
return hex2bn(s.toString("hex"));
|
||
|
}
|
||
|
|
||
|
function keySizeBytes(params) {
|
||
|
return Math.ceil(params.getN().bitLength() / 8);
|
||
|
}
|
||
|
|
||
|
function namedCurve(curve) {
|
||
|
var params = CURVES[curve];
|
||
|
if (!params) {
|
||
|
throw new TypeError("unsupported named curve: " + curve);
|
||
|
}
|
||
|
|
||
|
return params;
|
||
|
}
|
||
|
|
||
|
function normalizeEcdsa(params, md) {
|
||
|
var log2n = params.getN().bitLength(),
|
||
|
mdLen = md.length * 8;
|
||
|
|
||
|
var e = bin2bn(md);
|
||
|
if (log2n < mdLen) {
|
||
|
e = e.shiftRight(mdLen - log2n);
|
||
|
}
|
||
|
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
// ### EC Public Key
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param {String} curve The named curve
|
||
|
* @param {BigInteger} x The X coordinate
|
||
|
* @param {BigInteger} y The Y coordinate
|
||
|
*/
|
||
|
function ECPublicKey(curve, x, y) {
|
||
|
var params = namedCurve(curve),
|
||
|
c = params.getCurve();
|
||
|
var key = new ec.ECPointFp(c,
|
||
|
c.fromBigInteger(x),
|
||
|
c.fromBigInteger(y));
|
||
|
|
||
|
this.curve = curve;
|
||
|
this.params = params;
|
||
|
this.point = key;
|
||
|
|
||
|
var size = keySizeBytes(params);
|
||
|
this.x = bn2bin(x, size);
|
||
|
this.y = bn2bin(y, size);
|
||
|
}
|
||
|
|
||
|
// basics
|
||
|
ECPublicKey.prototype.isValid = function() {
|
||
|
return this.params.curve.contains(this.point);
|
||
|
}
|
||
|
|
||
|
// ECDSA
|
||
|
ECPublicKey.prototype.verify = function(md, sig) {
|
||
|
var N = this.params.getN(),
|
||
|
G = this.params.getG();
|
||
|
|
||
|
// prepare and validate (r, s)
|
||
|
var r = bin2bn(sig.r),
|
||
|
s = bin2bn(sig.s);
|
||
|
if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(N) >= 0) {
|
||
|
return false;
|
||
|
}
|
||
|
if (s.compareTo(BigInteger.ONE) < 0 || r.compareTo(N) >= 0) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// normalize input
|
||
|
var e = normalizeEcdsa(this.params, md);
|
||
|
// verify (r, s)
|
||
|
var w = s.modInverse(N),
|
||
|
u1 = e.multiply(w).mod(N),
|
||
|
u2 = r.multiply(w).mod(N);
|
||
|
|
||
|
var v = G.multiplyTwo(u1, this.point, u2).getX().toBigInteger();
|
||
|
v = v.mod(N);
|
||
|
|
||
|
return v.equals(r);
|
||
|
};
|
||
|
|
||
|
// ### EC Private Key
|
||
|
|
||
|
/**
|
||
|
* @param {String} curve The named curve
|
||
|
* @param {Buffer} key The private key value
|
||
|
*/
|
||
|
function ECPrivateKey(curve, key) {
|
||
|
var params = namedCurve(curve);
|
||
|
this.curve = curve;
|
||
|
this.params = params;
|
||
|
|
||
|
var size = keySizeBytes(params);
|
||
|
this.d = bn2bin(key, size);
|
||
|
}
|
||
|
|
||
|
ECPrivateKey.prototype.toPublicKey = function() {
|
||
|
var d = bin2bn(this.d);
|
||
|
var P = this.params.getG().multiply(d);
|
||
|
return new ECPublicKey(this.curve,
|
||
|
P.getX().toBigInteger(),
|
||
|
P.getY().toBigInteger());
|
||
|
};
|
||
|
|
||
|
// ECDSA
|
||
|
ECPrivateKey.prototype.sign = function(md) {
|
||
|
var keysize = keySizeBytes(this.params),
|
||
|
N = this.params.getN(),
|
||
|
G = this.params.getG(),
|
||
|
e = normalizeEcdsa(this.params, md),
|
||
|
d = bin2bn(this.d);
|
||
|
|
||
|
var r, s;
|
||
|
var k, x1, z;
|
||
|
do {
|
||
|
do {
|
||
|
// determine random nonce
|
||
|
do {
|
||
|
k = bin2bn(forge.random.getBytes(keysize));
|
||
|
} while (k.equals(BigInteger.ZERO) || k.compareTo(N) >= 0);
|
||
|
// (x1, y1) = k * G
|
||
|
x1 = G.multiply(k).getX().toBigInteger();
|
||
|
// r = x1 mod N
|
||
|
r = x1.mod(N);
|
||
|
} while (r.equals(BigInteger.ZERO));
|
||
|
// s = (k^-1 * (e + r * d)) mod N
|
||
|
z = d.multiply(r);
|
||
|
z = e.add(z);
|
||
|
s = k.modInverse(N).multiply(z).mod(N);
|
||
|
} while (s.equals(BigInteger.ONE));
|
||
|
|
||
|
// convert (r, s) to bytes
|
||
|
var len = keySizeBytes(this.params);
|
||
|
r = bn2bin(r, len);
|
||
|
s = bn2bin(s, len);
|
||
|
|
||
|
return {
|
||
|
r: r,
|
||
|
s: s
|
||
|
};
|
||
|
};
|
||
|
|
||
|
// basics
|
||
|
ECPrivateKey.prototype.isValid = function() {
|
||
|
var d = bin2bn(this.d),
|
||
|
n1 = this.params.getN().subtract(BigInteger.ONE);
|
||
|
|
||
|
return (d.compareTo(BigInteger.ONE) >= 0) &&
|
||
|
(d.compareTo(n1) < 0);
|
||
|
}
|
||
|
|
||
|
// ECDH
|
||
|
ECPrivateKey.prototype.computeSecret = function(pubkey) {
|
||
|
var d = bin2bn(this.d);
|
||
|
var S = pubkey.point.multiply(d).getX().toBigInteger();
|
||
|
S = bn2bin(S, keySizeBytes(this.params));
|
||
|
return S;
|
||
|
};
|
||
|
|
||
|
// ### Public API
|
||
|
exports.generateKeyPair = function(curve) {
|
||
|
var params = namedCurve(curve),
|
||
|
n = params.getN();
|
||
|
|
||
|
// generate random within range [1, N-1)
|
||
|
var r = forge.random.getBytes(keySizeBytes(params));
|
||
|
r = bin2bn(r);
|
||
|
|
||
|
var n1 = n.subtract(BigInteger.ONE);
|
||
|
var d = r.mod(n1).add(BigInteger.ONE);
|
||
|
|
||
|
var privkey = new ECPrivateKey(curve, d),
|
||
|
pubkey = privkey.toPublicKey();
|
||
|
|
||
|
return {
|
||
|
"private": privkey,
|
||
|
"public": pubkey
|
||
|
};
|
||
|
};
|
||
|
|
||
|
exports.asPublicKey = function(curve, x, y) {
|
||
|
if ("string" === typeof x) {
|
||
|
x = hex2bn(x);
|
||
|
} else if (Buffer.isBuffer(x)) {
|
||
|
x = bin2bn(x);
|
||
|
}
|
||
|
|
||
|
if ("string" === typeof y) {
|
||
|
y = hex2bn(y);
|
||
|
} else if (Buffer.isBuffer(y)) {
|
||
|
y = bin2bn(y);
|
||
|
}
|
||
|
|
||
|
var pubkey = new ECPublicKey(curve, x, y);
|
||
|
return pubkey;
|
||
|
};
|
||
|
exports.asPrivateKey = function(curve, d) {
|
||
|
// Elaborate way to get to a Buffer from a (String|Buffer|BigInteger)
|
||
|
if ("string" === typeof d) {
|
||
|
d = hex2bn(d);
|
||
|
} else if (Buffer.isBuffer(d)) {
|
||
|
d = bin2bn(d);
|
||
|
}
|
||
|
|
||
|
var privkey = new ECPrivateKey(curve, d);
|
||
|
return privkey;
|
||
|
};
|