mh-two-thousand-and-two
2024-04-12 7fc6dbf547b8899d949b67cdec36b96a7d1701c7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
'use strict';
 
// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js
var Buffer = require('safe-buffer').Buffer;
var BN = require('bn.js');
var EC = require('elliptic').ec;
var parseKeys = require('parse-asn1');
var curves = require('./curves.json');
 
function verify(sig, hash, key, signType, tag) {
  var pub = parseKeys(key);
  if (pub.type === 'ec') {
    // rsa keys can be interpreted as ecdsa ones in openssl
    if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong public key type'); }
    return ecVerify(sig, hash, pub);
  } else if (pub.type === 'dsa') {
    if (signType !== 'dsa') { throw new Error('wrong public key type'); }
    return dsaVerify(sig, hash, pub);
  }
  if (signType !== 'rsa' && signType !== 'ecdsa/rsa') { throw new Error('wrong public key type'); }
 
  hash = Buffer.concat([tag, hash]);
  var len = pub.modulus.byteLength();
  var pad = [1];
  var padNum = 0;
  while (hash.length + pad.length + 2 < len) {
    pad.push(0xff);
    padNum += 1;
  }
  pad.push(0x00);
  var i = -1;
  while (++i < hash.length) {
    pad.push(hash[i]);
  }
  pad = Buffer.from(pad);
  var red = BN.mont(pub.modulus);
  sig = new BN(sig).toRed(red);
 
  sig = sig.redPow(new BN(pub.publicExponent));
  sig = Buffer.from(sig.fromRed().toArray());
  var out = padNum < 8 ? 1 : 0;
  len = Math.min(sig.length, pad.length);
  if (sig.length !== pad.length) { out = 1; }
 
  i = -1;
  while (++i < len) { out |= sig[i] ^ pad[i]; }
  return out === 0;
}
 
function ecVerify(sig, hash, pub) {
  var curveId = curves[pub.data.algorithm.curve.join('.')];
  if (!curveId) { throw new Error('unknown curve ' + pub.data.algorithm.curve.join('.')); }
 
  var curve = new EC(curveId);
  var pubkey = pub.data.subjectPrivateKey.data;
 
  return curve.verify(hash, sig, pubkey);
}
 
function dsaVerify(sig, hash, pub) {
  var p = pub.data.p;
  var q = pub.data.q;
  var g = pub.data.g;
  var y = pub.data.pub_key;
  var unpacked = parseKeys.signature.decode(sig, 'der');
  var s = unpacked.s;
  var r = unpacked.r;
  checkValue(s, q);
  checkValue(r, q);
  var montp = BN.mont(p);
  var w = s.invm(q);
  var v = g.toRed(montp)
    .redPow(new BN(hash).mul(w).mod(q))
    .fromRed()
    .mul(y.toRed(montp).redPow(r.mul(w).mod(q)).fromRed())
    .mod(p)
    .mod(q);
  return v.cmp(r) === 0;
}
 
function checkValue(b, q) {
  if (b.cmpn(0) <= 0) { throw new Error('invalid sig'); }
  if (b.cmp(q) >= 0) { throw new Error('invalid sig'); }
}
 
module.exports = verify;