/*! * x509.js - x509 handling for bcoin * Copyright (c) 2016, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin */ 'use strict'; var BN = require('bn.js'); var asn1 = require('./asn1'); var elliptic = require('elliptic'); var crypto = require('../crypto/crypto'); var nativeCrypto; try { nativeCrypto = require('crypto'); } catch (e) { ; } var pk = exports; var rsa = {}; var ecdsa = {}; var native = {}; 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'), md5sha1: new Buffer(0), ripemd160: new Buffer('30203008060628cf060300310414', 'hex') }; // Ported from: // https://github.com/golang/go/blob/master/src/crypto/rsa/pkcs1v15.go rsa.verify = function verify(hashAlg, msg, sig, key) { var hash = crypto.hash(hashAlg, msg); var prefix = rsa.prefixes[hashAlg]; var len = prefix.length + hash.length; var pub = asn1.parseRSAPublic(key); var N = new BN(pub.modulus); var e = new BN(pub.publicExponent); var k = Math.ceil(N.bitLength() / 8); var m, em, ok, i; if (k < len + 11) throw new Error('Message too long.'); m = rsa.encrypt(N, e, sig); em = leftpad(m, k); ok = crypto.ceq(em[0], 0x00); ok &= crypto.ceq(em[1], 0x01); ok &= crypto.ccmp(em.slice(k - hash.length, k), hash); ok &= crypto.ccmp(em.slice(k - len, k - hash.length), prefix); ok &= crypto.ceq(em[k - len - 1], 0x00); for (i = 2; i < k - len - 1; i++) ok &= crypto.ceq(em[i], 0xff); return ok === 1; }; rsa.sign = function sign(hashAlg, msg, key) { var hash = crypto.hash(hashAlg, msg); var prefix = rsa.prefixes[hashAlg]; var len = prefix.length + hash.length; var priv = asn1.parseRSAPrivate(key); var N = new BN(priv.modulus); var D = new BN(priv.privateExponent); var k = Math.ceil(N.bitLength() / 8); var i, em; 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); }; 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'); }; rsa.encrypt = function encrypt(N, e, m) { return new BN(m) .toRed(BN.red(N)) .redPow(e) .fromRed() .toArrayLike(Buffer, 'be'); }; ecdsa.verify = function verify(curve, msg, hashAlg, key, sig) { var hash = crypto.hash(hashAlg, msg); var ec = elliptic.ec(curve); return ec.verify(hash, sig, key); }; ecdsa.sign = function sign(curve, msg, hashAlg, key) { var hash = crypto.hash(hashAlg, msg); var ec = elliptic.ec(curve); return new Buffer(ec.sign(hash, key)); }; native.verify = function verify(alg, hash, msg, sig, key) { var algo, verify; if (!nativeCrypto) return false; algo = normalizeAlg(alg, hash); verify = nativeCrypto.createVerify(algo); verify.update(msg); return verify.verify(key, sig); }; native.sign = function _sign(alg, hash, msg, key) { var algo, sig; if (!nativeCrypto) return false; algo = normalizeAlg(alg, hash); sig = nativeCrypto.createSign(algo); sig.update(msg); return sig.sign(key); }; pk.pemTag = { dsa: 'DSA', rsa: 'RSA', ecdsa: 'EC' }; pk.toPEM = function toPEM(key, type) { var tag = pk.pemTag[key.alg]; var pem = asn1.toPEM(key.data, tag, type); // Key parameters, usually present // if selecting an EC curve. if (key.params) pem += asn1.toPEM(key.params, tag, 'parameters'); return pem; }; pk._verify = function verify(hash, msg, sig, key) { var pem; switch (key.alg) { case 'dsa': pem = pk.toPEM(key, 'public key'); return native.verify(key.alg, hash, msg, sig, pem); case 'rsa': if (nativeCrypto) { pem = pk.toPEM(key, 'public key'); return native.verify(key.alg, hash, msg, sig, pem); } return rsa.verify(hash, msg, sig, key.data); case 'ecdsa': if (!key.curve) throw new Error('No curve present.'); return ecdsa.verify(key.curve, hash, msg, sig, key.data); default: throw new Error('Unsupported algorithm.'); } }; pk.verify = function verify(hash, msg, sig, key) { try { return pk._verify(hash, msg, sig, key); } catch (e) { return false; } }; pk.sign = function sign(hash, msg, key) { var pem; switch (key.alg) { case 'dsa': pem = pk.toPEM(key, 'private key'); return native.sign(key.alg, hash, msg, pem); case 'rsa': if (nativeCrypto) { pem = pk.toPEM(key, 'private key'); return native.sign(key.alg, hash, msg, pem); } return rsa.sign(hash, msg, key.data); case 'ecdsa': if (!key.curve) throw new Error('No curve present.'); return ecdsa.sign(key.curve, hash, msg, key.data); default: throw new Error('Unsupported algorithm.'); } }; 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 normalizeAlg(alg, hash) { var name = alg.toUpperCase() + '-' + hash.toUpperCase(); switch (name) { case 'ECDSA-SHA1': name = 'ecdsa-with-SHA1'; break; case 'ECDSA-SHA256': name = 'ecdsa-with-SHA256'; break; } return name; } pk.rsa = rsa; pk.ecdsa = ecdsa; pk.native = native;