bip70: javascript rsa implementation.
This commit is contained in:
parent
a987513ce9
commit
41cc642a32
@ -348,7 +348,7 @@ asn1.parseRSAPrivate = function parseRSAPrivate(data) {
|
||||
var p = BufferReader(data);
|
||||
p = BufferReader(asn1.parseSeq(p));
|
||||
return {
|
||||
version: asn1.parseExplicitInt(p, 0, true),
|
||||
version: asn1.parseInt(p, true),
|
||||
modulus: asn1.parseInt(p),
|
||||
publicExponent: asn1.parseInt(p),
|
||||
privateExponent: asn1.parseInt(p),
|
||||
@ -404,15 +404,28 @@ asn1.fromPEM = function fromPEM(pem) {
|
||||
var chunks = asn1.parsePEM(pem);
|
||||
var body = chunks[0];
|
||||
var extra = chunks[1];
|
||||
var params;
|
||||
var params, alg;
|
||||
|
||||
if (extra) {
|
||||
if (extra.tag.indexOf('PARAMETERS') !== -1)
|
||||
params = extra.data;
|
||||
}
|
||||
|
||||
switch (body.type) {
|
||||
case 'dsa':
|
||||
alg = 'dsa';
|
||||
break;
|
||||
case 'rsa':
|
||||
alg = 'rsa';
|
||||
break;
|
||||
case 'ec':
|
||||
alg = 'ecdsa';
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
type: body.type,
|
||||
alg: alg,
|
||||
data: body.data,
|
||||
params: params
|
||||
};
|
||||
|
||||
261
lib/bcoin/bip70/pk.js
Normal file
261
lib/bcoin/bip70/pk.js
Normal file
@ -0,0 +1,261 @@
|
||||
/*!
|
||||
* 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 utils = require('../utils');
|
||||
|
||||
var crypto;
|
||||
|
||||
try {
|
||||
crypto = require('cryp' + 'to');
|
||||
} 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 = utils.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 = ceq(em[0], 0x00);
|
||||
ok &= ceq(em[1], 0x01);
|
||||
ok &= utils.ccmp(em.slice(k - hash.length, k), hash);
|
||||
ok &= utils.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;
|
||||
};
|
||||
|
||||
rsa.sign = function sign(hashAlg, msg, key) {
|
||||
var hash = utils.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 = utils.hash(hashAlg, msg);
|
||||
var ec = elliptic.ec(curve);
|
||||
return ec.verify(hash, sig, key);
|
||||
};
|
||||
|
||||
ecdsa.sign = function sign(curve, msg, hashAlg, key) {
|
||||
var hash = utils.hash(hashAlg, msg);
|
||||
var ec = elliptic.ec(curve);
|
||||
return ec.sign(hash, key);
|
||||
};
|
||||
|
||||
native.verify = function verify(alg, hash, msg, sig, key) {
|
||||
var algo, verify;
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
algo = normalizeAlg(alg, hash);
|
||||
verify = crypto.createVerify(algo);
|
||||
verify.update(msg);
|
||||
|
||||
return verify.verify(key, sig);
|
||||
};
|
||||
|
||||
native.sign = function _sign(alg, hash, msg, key) {
|
||||
var algo, sig;
|
||||
|
||||
if (!crypto)
|
||||
return false;
|
||||
|
||||
algo = normalizeAlg(alg, hash);
|
||||
sig = crypto.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 (crypto) {
|
||||
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)
|
||||
return false;
|
||||
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 (crypto) {
|
||||
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)
|
||||
return false;
|
||||
return ecdsa.sign(key.curve, hash, msg, key.data);
|
||||
default:
|
||||
throw new Error('Unsupported algorithm.');
|
||||
}
|
||||
};
|
||||
|
||||
function ceq(a, b) {
|
||||
var r = ~(a ^ b) & 0xff;
|
||||
r &= r >>> 4;
|
||||
r &= r >>> 2;
|
||||
r &= r >>> 1;
|
||||
return r === 1;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -7,9 +7,9 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var crypto = require('crypto');
|
||||
var asn1 = require('./asn1');
|
||||
var utils = require('../utils');
|
||||
var pk = require('./pk');
|
||||
var x509 = exports;
|
||||
|
||||
x509.getSubjectOID = function getSubjectOID(cert, oid) {
|
||||
@ -112,10 +112,11 @@ x509.oid = {
|
||||
'1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' }
|
||||
};
|
||||
|
||||
x509.pemTag = {
|
||||
dsa: 'DSA',
|
||||
rsa: 'RSA',
|
||||
ecdsa: 'EC'
|
||||
x509.curves = {
|
||||
'1.3.132.0.33': 'p224',
|
||||
'1.2.840.10045.3.1.7': 'p256',
|
||||
'1.3.132.0.34': 'p384',
|
||||
'1.3.132.0.35': 'p521'
|
||||
};
|
||||
|
||||
x509.getKeyAlgorithm = function getKeyAlgorithm(cert) {
|
||||
@ -128,6 +129,21 @@ x509.getSigAlgorithm = function getSigAlgorithm(cert) {
|
||||
return x509.oid[alg];
|
||||
};
|
||||
|
||||
x509.getCurve = function getCurve(params) {
|
||||
var oid;
|
||||
|
||||
if (!params)
|
||||
return;
|
||||
|
||||
try {
|
||||
oid = asn1.parseOID(params);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
return x509.curves[oid];
|
||||
};
|
||||
|
||||
x509.parse = function parse(der) {
|
||||
try {
|
||||
return asn1.parseCert(der);
|
||||
@ -138,23 +154,23 @@ x509.parse = function parse(der) {
|
||||
|
||||
x509.getPublicKey = function getPublicKey(cert) {
|
||||
var alg = x509.getKeyAlgorithm(cert);
|
||||
var key, params, pem, tag;
|
||||
var key, params, curve;
|
||||
|
||||
if (!alg)
|
||||
return;
|
||||
|
||||
key = cert.tbs.pubkey.pubkey;
|
||||
params = cert.tbs.pubkey.alg.params;
|
||||
tag = x509.pemTag[alg.key];
|
||||
|
||||
pem = asn1.toPEM(key, tag, 'public key');
|
||||
if (alg.key === 'ecdsa')
|
||||
curve = x509.getCurve(params);
|
||||
|
||||
// Key parameters, usually present
|
||||
// if selecting an EC curve.
|
||||
if (params)
|
||||
pem += asn1.toPEM(params, tag, 'parameters');
|
||||
|
||||
return pem;
|
||||
return {
|
||||
alg: alg.key,
|
||||
data: key,
|
||||
params: params,
|
||||
curve: curve
|
||||
};
|
||||
};
|
||||
|
||||
x509.verifyTime = function verifyTime(cert) {
|
||||
@ -164,26 +180,39 @@ x509.verifyTime = function verifyTime(cert) {
|
||||
};
|
||||
|
||||
x509.signSubject = function signSubject(hash, msg, key, chain) {
|
||||
var cert, alg, tag;
|
||||
var cert, pub, curve;
|
||||
|
||||
assert(chain.length !== 0, 'No chain available.');
|
||||
|
||||
cert = x509.parse(chain[0]);
|
||||
assert(cert, 'Could not parse certificate.');
|
||||
|
||||
alg = x509.getKeyAlgorithm(cert);
|
||||
assert(alg, 'Certificate uses an unknown algorithm.');
|
||||
|
||||
if (Buffer.isBuffer(key)) {
|
||||
tag = x509.pemTag[alg.key];
|
||||
key = asn1.toPEM(key, tag, 'private key');
|
||||
if (typeof key === 'string') {
|
||||
key = asn1.fromPEM(key);
|
||||
if (key.alg === 'ecdsa')
|
||||
curve = x509.getCurve(key.params);
|
||||
key = {
|
||||
alg: key.alg,
|
||||
data: key.data,
|
||||
params: key.params,
|
||||
curve: curve
|
||||
};
|
||||
} else {
|
||||
pub = x509.getPublicKey(cert);
|
||||
assert(pub, 'Certificate uses an unknown algorithm.');
|
||||
key = {
|
||||
alg: pub.alg,
|
||||
data: key,
|
||||
params: pub.params,
|
||||
curve: pub.curve
|
||||
};
|
||||
}
|
||||
|
||||
return x509.sign(alg.key, hash, msg, key);
|
||||
return pk.sign(hash, msg, key);
|
||||
};
|
||||
|
||||
x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
|
||||
var cert, key, alg;
|
||||
var cert, key;
|
||||
|
||||
if (chain.length === 0)
|
||||
return false;
|
||||
@ -198,12 +227,7 @@ x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
alg = x509.getKeyAlgorithm(cert);
|
||||
|
||||
if (!alg)
|
||||
return false;
|
||||
|
||||
return x509.verify(alg.key, hash, msg, sig, key);
|
||||
return pk.verify(hash, msg, sig, key);
|
||||
};
|
||||
|
||||
x509.verifyChain = function verifyChain(chain) {
|
||||
@ -241,7 +265,7 @@ x509.verifyChain = function verifyChain(chain) {
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (!x509.verify(alg.key, alg.hash, msg, sig, key))
|
||||
if (!pk.verify(alg.hash, msg, sig, key))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -266,41 +290,6 @@ x509.verifyChain = function verifyChain(chain) {
|
||||
return false;
|
||||
};
|
||||
|
||||
x509.normalizeAlg = 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;
|
||||
};
|
||||
|
||||
x509.verify = function verify(alg, hash, msg, sig, key) {
|
||||
var algo = x509.normalizeAlg(alg, hash);
|
||||
var verify;
|
||||
|
||||
try {
|
||||
verify = crypto.createVerify(algo);
|
||||
verify.update(msg);
|
||||
return verify.verify(key, sig);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
x509.sign = function sign(alg, hash, msg, key) {
|
||||
var algo = x509.normalizeAlg(alg, hash);
|
||||
var sig = crypto.createSign(algo);
|
||||
sig.update(msg);
|
||||
return sig.sign(key);
|
||||
};
|
||||
|
||||
x509.asn1 = asn1;
|
||||
|
||||
function isHash(data) {
|
||||
|
||||
@ -404,7 +404,7 @@ Worker.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
this._bind();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize worker. Bind to more events.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user