crypto: support ML-KEM, DHKEM, and RSASVE key encapsulation mechanisms · nodejs/node@1474153

1+

'use strict';

2+3+

const common = require('../common.js');

4+

const { hasOpenSSL } = require('../../test/common/crypto.js');

5+

const crypto = require('crypto');

6+

const fs = require('fs');

7+

const path = require('path');

8+

const fixtures_keydir = path.resolve(__dirname, '../../test/fixtures/keys/');

9+10+

function readKey(name) {

11+

return fs.readFileSync(`${fixtures_keydir}/${name}.pem`, 'utf8');

12+

}

13+14+

const keyFixtures = {};

15+16+

if (hasOpenSSL(3, 5)) {

17+

keyFixtures['ml-kem-512'] = readKey('ml_kem_512_private');

18+

keyFixtures['ml-kem-768'] = readKey('ml_kem_768_private');

19+

keyFixtures['ml-kem-1024'] = readKey('ml_kem_1024_private');

20+

}

21+

if (hasOpenSSL(3, 2)) {

22+

keyFixtures['p-256'] = readKey('ec_p256_private');

23+

keyFixtures['p-384'] = readKey('ec_p384_private');

24+

keyFixtures['p-521'] = readKey('ec_p521_private');

25+

keyFixtures.x25519 = readKey('x25519_private');

26+

keyFixtures.x448 = readKey('x448_private');

27+

}

28+

if (hasOpenSSL(3, 0)) {

29+

keyFixtures.rsa = readKey('rsa_private_2048');

30+

}

31+32+

if (Object.keys(keyFixtures).length === 0) {

33+

console.log('no supported key types available for this OpenSSL version');

34+

process.exit(0);

35+

}

36+37+

const bench = common.createBenchmark(main, {

38+

keyType: Object.keys(keyFixtures),

39+

mode: ['sync', 'async', 'async-parallel'],

40+

keyFormat: ['keyObject', 'keyObject.unique'],

41+

op: ['encapsulate', 'decapsulate'],

42+

n: [1e3],

43+

}, {

44+

combinationFilter(p) {

45+

// "keyObject.unique" allows to compare the result with "keyObject" to

46+

// assess whether mutexes over the key material impact the operation

47+

return p.keyFormat !== 'keyObject.unique' ||

48+

(p.keyFormat === 'keyObject.unique' && p.mode === 'async-parallel');

49+

},

50+

});

51+52+

function measureSync(n, op, privateKey, keys, ciphertexts) {

53+

bench.start();

54+

for (let i = 0; i < n; ++i) {

55+

const key = privateKey || keys[i];

56+

if (op === 'encapsulate') {

57+

crypto.encapsulate(key);

58+

} else {

59+

crypto.decapsulate(key, ciphertexts[i]);

60+

}

61+

}

62+

bench.end(n);

63+

}

64+65+

function measureAsync(n, op, privateKey, keys, ciphertexts) {

66+

let remaining = n;

67+

function done() {

68+

if (--remaining === 0)

69+

bench.end(n);

70+

else

71+

one();

72+

}

73+74+

function one() {

75+

const key = privateKey || keys[n - remaining];

76+

if (op === 'encapsulate') {

77+

crypto.encapsulate(key, done);

78+

} else {

79+

crypto.decapsulate(key, ciphertexts[n - remaining], done);

80+

}

81+

}

82+

bench.start();

83+

one();

84+

}

85+86+

function measureAsyncParallel(n, op, privateKey, keys, ciphertexts) {

87+

let remaining = n;

88+

function done() {

89+

if (--remaining === 0)

90+

bench.end(n);

91+

}

92+

bench.start();

93+

for (let i = 0; i < n; ++i) {

94+

const key = privateKey || keys[i];

95+

if (op === 'encapsulate') {

96+

crypto.encapsulate(key, done);

97+

} else {

98+

crypto.decapsulate(key, ciphertexts[i], done);

99+

}

100+

}

101+

}

102+103+

function main({ n, mode, keyFormat, keyType, op }) {

104+

const pems = [...Buffer.alloc(n)].map(() => keyFixtures[keyType]);

105+

const keyObjects = pems.map(crypto.createPrivateKey);

106+107+

let privateKey, keys, ciphertexts;

108+109+

switch (keyFormat) {

110+

case 'keyObject':

111+

privateKey = keyObjects[0];

112+

break;

113+

case 'keyObject.unique':

114+

keys = keyObjects;

115+

break;

116+

default:

117+

throw new Error('not implemented');

118+

}

119+120+

// Pre-generate ciphertexts for decapsulate operations

121+

if (op === 'decapsulate') {

122+

if (privateKey) {

123+

ciphertexts = [...Buffer.alloc(n)].map(() => crypto.encapsulate(privateKey).ciphertext);

124+

} else {

125+

ciphertexts = keys.map((key) => crypto.encapsulate(key).ciphertext);

126+

}

127+

}

128+129+

switch (mode) {

130+

case 'sync':

131+

measureSync(n, op, privateKey, keys, ciphertexts);

132+

break;

133+

case 'async':

134+

measureAsync(n, op, privateKey, keys, ciphertexts);

135+

break;

136+

case 'async-parallel':

137+

measureAsyncParallel(n, op, privateKey, keys, ciphertexts);

138+

break;

139+

}

140+

}