| // much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js |
| var createHmac = require('create-hmac') |
| var crt = require('browserify-rsa') |
| var EC = require('elliptic').ec |
| var BN = require('bn.js') |
| var parseKeys = require('parse-asn1') |
| var curves = require('./curves.json') |
| |
| function sign (hash, key, hashType, signType, tag) { |
| var priv = parseKeys(key) |
| if (priv.curve) { |
| // rsa keys can be interpreted as ecdsa ones in openssl |
| if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') |
| return ecSign(hash, priv) |
| } else if (priv.type === 'dsa') { |
| if (signType !== 'dsa') throw new Error('wrong private key type') |
| return dsaSign(hash, priv, hashType) |
| } else { |
| if (signType !== 'rsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') |
| } |
| hash = Buffer.concat([tag, hash]) |
| var len = priv.modulus.byteLength() |
| var pad = [ 0, 1 ] |
| while (hash.length + pad.length + 1 < len) pad.push(0xff) |
| pad.push(0x00) |
| var i = -1 |
| while (++i < hash.length) pad.push(hash[i]) |
| |
| var out = crt(pad, priv) |
| return out |
| } |
| |
| function ecSign (hash, priv) { |
| var curveId = curves[priv.curve.join('.')] |
| if (!curveId) throw new Error('unknown curve ' + priv.curve.join('.')) |
| |
| var curve = new EC(curveId) |
| var key = curve.keyFromPrivate(priv.privateKey) |
| var out = key.sign(hash) |
| |
| return new Buffer(out.toDER()) |
| } |
| |
| function dsaSign (hash, priv, algo) { |
| var x = priv.params.priv_key |
| var p = priv.params.p |
| var q = priv.params.q |
| var g = priv.params.g |
| var r = new BN(0) |
| var k |
| var H = bits2int(hash, q).mod(q) |
| var s = false |
| var kv = getKey(x, q, hash, algo) |
| while (s === false) { |
| k = makeKey(q, kv, algo) |
| r = makeR(g, k, p, q) |
| s = k.invm(q).imul(H.add(x.mul(r))).mod(q) |
| if (s.cmpn(0) === 0) { |
| s = false |
| r = new BN(0) |
| } |
| } |
| return toDER(r, s) |
| } |
| |
| function toDER (r, s) { |
| r = r.toArray() |
| s = s.toArray() |
| |
| // Pad values |
| if (r[0] & 0x80) r = [ 0 ].concat(r) |
| if (s[0] & 0x80) s = [ 0 ].concat(s) |
| |
| var total = r.length + s.length + 4 |
| var res = [ 0x30, total, 0x02, r.length ] |
| res = res.concat(r, [ 0x02, s.length ], s) |
| return new Buffer(res) |
| } |
| |
| function getKey (x, q, hash, algo) { |
| x = new Buffer(x.toArray()) |
| if (x.length < q.byteLength()) { |
| var zeros = new Buffer(q.byteLength() - x.length) |
| zeros.fill(0) |
| x = Buffer.concat([ zeros, x ]) |
| } |
| var hlen = hash.length |
| var hbits = bits2octets(hash, q) |
| var v = new Buffer(hlen) |
| v.fill(1) |
| var k = new Buffer(hlen) |
| k.fill(0) |
| k = createHmac(algo, k).update(v).update(new Buffer([ 0 ])).update(x).update(hbits).digest() |
| v = createHmac(algo, k).update(v).digest() |
| k = createHmac(algo, k).update(v).update(new Buffer([ 1 ])).update(x).update(hbits).digest() |
| v = createHmac(algo, k).update(v).digest() |
| return { k: k, v: v } |
| } |
| |
| function bits2int (obits, q) { |
| var bits = new BN(obits) |
| var shift = (obits.length << 3) - q.bitLength() |
| if (shift > 0) bits.ishrn(shift) |
| return bits |
| } |
| |
| function bits2octets (bits, q) { |
| bits = bits2int(bits, q) |
| bits = bits.mod(q) |
| var out = new Buffer(bits.toArray()) |
| if (out.length < q.byteLength()) { |
| var zeros = new Buffer(q.byteLength() - out.length) |
| zeros.fill(0) |
| out = Buffer.concat([ zeros, out ]) |
| } |
| return out |
| } |
| |
| function makeKey (q, kv, algo) { |
| var t |
| var k |
| |
| do { |
| t = new Buffer(0) |
| |
| while (t.length * 8 < q.bitLength()) { |
| kv.v = createHmac(algo, kv.k).update(kv.v).digest() |
| t = Buffer.concat([ t, kv.v ]) |
| } |
| |
| k = bits2int(t, q) |
| kv.k = createHmac(algo, kv.k).update(kv.v).update(new Buffer([ 0 ])).digest() |
| kv.v = createHmac(algo, kv.k).update(kv.v).digest() |
| } while (k.cmp(q) !== -1) |
| |
| return k |
| } |
| |
| function makeR (g, k, p, q) { |
| return g.toRed(BN.mont(p)).redPow(k).fromRed().mod(q) |
| } |
| |
| module.exports = sign |
| module.exports.getKey = getKey |
| module.exports.makeKey = makeKey |