1'use strict';
2
3const common = require('../common.js');
4const crypto = require('crypto');
5const fs = require('fs');
6const path = require('path');
7const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/');
8
9function readKey(name) {
10  return fs.readFileSync(`${fixtures_keydir}/${name}.pem`, 'utf8');
11}
12
13function readKeyPair(publicKeyName, privateKeyName) {
14  return {
15    publicKey: readKey(publicKeyName),
16    privateKey: readKey(privateKeyName),
17  };
18}
19
20const keyFixtures = {
21  ec: readKeyPair('ec_p256_public', 'ec_p256_private'),
22  rsa: readKeyPair('rsa_public_2048', 'rsa_private_2048'),
23  ed25519: readKeyPair('ed25519_public', 'ed25519_private'),
24};
25
26const data = crypto.randomBytes(256);
27
28let pems;
29let keyObjects;
30
31const bench = common.createBenchmark(main, {
32  keyType: ['rsa', 'ec', 'ed25519'],
33  mode: ['sync', 'async', 'async-parallel'],
34  keyFormat: ['pem', 'der', 'jwk', 'keyObject', 'keyObject.unique'],
35  n: [1e3],
36}, {
37  combinationFilter(p) {
38    // "keyObject.unique" allows to compare the result with "keyObject" to
39    // assess whether mutexes over the key material impact the operation
40    return p.keyFormat !== 'keyObject.unique' ||
41      (p.keyFormat === 'keyObject.unique' && p.mode === 'async-parallel');
42  },
43});
44
45function measureSync(n, digest, signature, publicKey, keys) {
46  bench.start();
47  for (let i = 0; i < n; ++i) {
48    crypto.verify(
49      digest,
50      data,
51      publicKey || keys[i],
52      signature);
53  }
54  bench.end(n);
55}
56
57function measureAsync(n, digest, signature, publicKey, keys) {
58  let remaining = n;
59  function done() {
60    if (--remaining === 0)
61      bench.end(n);
62    else
63      one();
64  }
65
66  function one() {
67    crypto.verify(
68      digest,
69      data,
70      publicKey || keys[n - remaining],
71      signature,
72      done);
73  }
74  bench.start();
75  one();
76}
77
78function measureAsyncParallel(n, digest, signature, publicKey, keys) {
79  let remaining = n;
80  function done() {
81    if (--remaining === 0)
82      bench.end(n);
83  }
84  bench.start();
85  for (let i = 0; i < n; ++i) {
86    crypto.verify(
87      digest,
88      data,
89      publicKey || keys[i],
90      signature,
91      done);
92  }
93}
94
95function main({ n, mode, keyFormat, keyType }) {
96  pems ||= [...Buffer.alloc(n)].map(() => keyFixtures[keyType].publicKey);
97  keyObjects ||= pems.map(crypto.createPublicKey);
98
99  let publicKey, keys, digest;
100
101  switch (keyType) {
102    case 'rsa':
103    case 'ec':
104      digest = 'sha256';
105      break;
106    case 'ed25519':
107      break;
108    default:
109      throw new Error('not implemented');
110  }
111
112  switch (keyFormat) {
113    case 'keyObject':
114      publicKey = keyObjects[0];
115      break;
116    case 'pem':
117      publicKey = pems[0];
118      break;
119    case 'jwk': {
120      publicKey = { key: keyObjects[0].export({ format: 'jwk' }), format: 'jwk' };
121      break;
122    }
123    case 'der': {
124      publicKey = { key: keyObjects[0].export({ format: 'der', type: 'spki' }), format: 'der', type: 'spki' };
125      break;
126    }
127    case 'keyObject.unique':
128      keys = keyObjects;
129      break;
130    default:
131      throw new Error('not implemented');
132  }
133
134
135  const { privateKey } = keyFixtures[keyType];
136  const signature = crypto.sign(digest, data, privateKey);
137
138  switch (mode) {
139    case 'sync':
140      measureSync(n, digest, signature, publicKey, keys);
141      break;
142    case 'async':
143      measureAsync(n, digest, signature, publicKey, keys);
144      break;
145    case 'async-parallel':
146      measureAsyncParallel(n, digest, signature, publicKey, keys);
147      break;
148    default:
149      throw new Error('not implemented');
150  }
151}
152