/*! * pk-browser.js - public key algorithms for bcoin * Copyright (c) 2016-2017, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin */ 'use strict'; /** * @module crypto/pk-browser * @ignore */ var assert = require('assert'); var BN = require('bn.js'); var elliptic = require('elliptic'); var ASN1 = require('../utils/asn1'); var backend = require('./backend'); var rsa, ecdsa; /** * RSA * @namespace module:crypto/pk-browser.rsa * @ignore */ rsa = {}; /** * PKCS signature prefixes. * @type {Object} * @memberof module:crypto/pk-browser.rsa */ rsa.prefixes = { md5: new Buffer('3020300c06082a864886f70d020505000410', 'hex'), sha1: new Buffer('3021300906052b0e03021a05000414', 'hex'), sha224: new Buffer('302d300d06096086480165030402040500041c', 'hex'), sha256: new Buffer('3031300d060960864801650304020105000420', 'hex'), sha384: new Buffer('3041300d060960864801650304020205000430', 'hex'), sha512: new Buffer('3051300d060960864801650304020305000440', 'hex'), ripemd160: new Buffer('30203008060628cf060300310414', 'hex') }; /** * Verify RSA signature. * @memberof module:crypto/pk-browser.rsa * @param {String} alg - Hash algorithm. * @param {Buffer} msg - Signed message. * @param {Buffer} sig - Signature. * @param {Buffer} key - ASN1 serialized RSA key. * @returns {Boolean} */ rsa.verify = function verify(alg, msg, sig, key) { var prefix = rsa.prefixes[alg]; var hash, len, pub; var N, e, k, m, em, ok, i; if (!prefix) throw new Error('Unknown PKCS prefix.'); hash = backend.hash(alg, msg); len = prefix.length + hash.length; pub = ASN1.parseRSAPublic(key); N = new BN(pub.modulus); e = new BN(pub.publicExponent); k = Math.ceil(N.bitLength() / 8); if (k < len + 11) throw new Error('Message too long.'); m = rsa.encrypt(N, e, sig); em = leftpad(m, k); ok = ceq(em[0], 0x00); ok &= ceq(em[1], 0x01); ok &= backend.ccmp(em.slice(k - hash.length, k), hash); ok &= backend.ccmp(em.slice(k - len, k - hash.length), prefix); ok &= ceq(em[k - len - 1], 0x00); for (i = 2; i < k - len - 1; i++) ok &= ceq(em[i], 0xff); return ok === 1; }; /** * Sign message with RSA key. * @memberof module:crypto/pk-browser.rsa * @param {String} alg - Hash algorithm. * @param {Buffer} msg - Signed message. * @param {Buffer} key - ASN1 serialized RSA key. * @returns {Buffer} Signature (DER) */ rsa.sign = function sign(alg, msg, key) { var prefix = rsa.prefixes[alg]; var hash, len, priv; var N, D, k, i, em; if (!prefix) throw new Error('Unknown PKCS prefix.'); hash = backend.hash(alg, msg); len = prefix.length + hash.length; priv = ASN1.parseRSAPrivate(key); N = new BN(priv.modulus); D = new BN(priv.privateExponent); k = Math.ceil(N.bitLength() / 8); if (k < len + 11) throw new Error('Message too long.'); em = new Buffer(k); em.fill(0); em[1] = 0x01; for (i = 2; i < k - len - 1; i++) em[i] = 0xff; prefix.copy(em, k - len); hash.copy(em, k - hash.length); return rsa.decrypt(N, D, em); }; /** * Decrypt with modulus and exponent. * @memberof module:crypto/pk-browser.rsa * @param {BN} N * @param {BN} D * @param {Buffer} m * @returns {Buffer} */ rsa.decrypt = function decrypt(N, D, m) { var c = new BN(m); if (c.cmp(N) > 0) throw new Error('Cannot decrypt.'); return c .toRed(BN.red(N)) .redPow(D) .fromRed() .toArrayLike(Buffer, 'be'); }; /** * Encrypt with modulus and exponent. * @memberof module:crypto/pk-browser.rsa * @param {BN} N * @param {BN} e * @param {Buffer} m * @returns {Buffer} */ rsa.encrypt = function encrypt(N, e, m) { return new BN(m) .toRed(BN.red(N)) .redPow(e) .fromRed() .toArrayLike(Buffer, 'be'); }; /** * ECDSA * @namespace module:crypto/pk.ecdsa * @ignore */ ecdsa = {}; /** * Verify ECDSA signature. * @memberof module:crypto/pk-browser.ecdsa * @param {String} curve - Curve name. * @param {String} alg - Hash algorithm. * @param {Buffer} msg - Signed message. * @param {Buffer} sig - Signature. * @param {Buffer} key - ASN1 serialized ECDSA key. * @returns {Boolean} */ ecdsa.verify = function verify(curve, alg, msg, sig, key) { var ec, hash; assert(curve, 'No curve selected.'); ec = elliptic.ec(curve); hash = backend.hash(alg, msg); return ec.verify(hash, sig, key); }; /** * Sign message with ECDSA key. * @memberof module:crypto/pk-browser.ecdsa * @param {String} curve - Curve name. * @param {String} alg - Hash algorithm. * @param {Buffer} msg - Signed message. * @param {Buffer} key - ASN1 serialized ECDSA key. * @returns {Buffer} Signature (DER) */ ecdsa.sign = function sign(curve, alg, msg, key) { var ec, hash; assert(curve, 'No curve selected.'); ec = elliptic.ec(curve); hash = backend.hash(alg, msg); return new Buffer(ec.sign(hash, key)); }; /* * Helpers */ function leftpad(input, size) { var n = input.length; var out; if (n > size) n = size; out = new Buffer(size); out.fill(0); input.copy(out, out.length - n); return out; } function ceq(a, b) { var r = ~(a ^ b) & 0xff; r &= r >>> 4; r &= r >>> 2; r &= r >>> 1; return r === 1; } /* * Expose */ exports.rsa = rsa; exports.ecdsa = ecdsa;