/** * 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; };