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