mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-08 16:05:05 -04:00
crypto: Implement HKDF for Node.js < 15.0.0
This commit is contained in:
parent
675c0130b9
commit
df7938f3c9
2 changed files with 84 additions and 0 deletions
43
src/node/utils/crypto.js
Normal file
43
src/node/utils/crypto.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
const {Buffer} = require('buffer');
|
||||
const crypto = require('crypto');
|
||||
const util = require('util');
|
||||
|
||||
// TODO: Delete this once support for Node.js < 15.0.0 is dropped.
|
||||
const hkdfFallback = async (digest, ikm, salt, info, keylen) => {
|
||||
// https://datatracker.ietf.org/doc/html/rfc5869#section-2.2
|
||||
const prkHmac = crypto.createHmac(digest, salt);
|
||||
prkHmac.update(ikm);
|
||||
const prk = prkHmac.digest();
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5869#section-2.3
|
||||
let len = 0;
|
||||
const t = [Buffer.alloc(0)];
|
||||
while (len < keylen) {
|
||||
const hmac = crypto.createHmac(digest, prk);
|
||||
hmac.update(t[t.length - 1]);
|
||||
hmac.update(info);
|
||||
hmac.update(Buffer.from([t.length % 256]));
|
||||
const tn = hmac.digest();
|
||||
t.push(tn);
|
||||
len += tn.length;
|
||||
}
|
||||
const buf = Buffer.concat(t);
|
||||
return (buf.byteOffset === 0 && buf.buffer.byteLength === keylen
|
||||
? buf : Uint8Array.prototype.slice.call(buf, 0, keylen)).buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Promisified version of Node.js's crypto.hkdf.
|
||||
*/
|
||||
exports.hkdf = crypto.hkdf ? util.promisify(crypto.hkdf) : hkdfFallback;
|
||||
|
||||
/**
|
||||
* Promisified version of Node.js's crypto.randomBytes
|
||||
*/
|
||||
exports.randomBytes = util.promisify(crypto.randomBytes);
|
||||
|
||||
exports.exportedForTesting = {
|
||||
hkdfFallback,
|
||||
};
|
41
src/tests/backend/specs/crypto.js
Normal file
41
src/tests/backend/specs/crypto.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert').strict;
|
||||
const {Buffer} = require('buffer');
|
||||
const crypto = require('../../../node/utils/crypto');
|
||||
const nodeCrypto = require('crypto');
|
||||
const util = require('util');
|
||||
|
||||
const nodeHkdf = nodeCrypto.hkdf ? util.promisify(nodeCrypto.hkdf) : null;
|
||||
|
||||
const ab2hex = (ab) => Buffer.from(ab).toString('hex');
|
||||
|
||||
describe(__filename, function () {
|
||||
describe('hkdf fallback', function () {
|
||||
before(async function () {
|
||||
if (!nodeHkdf) this.skip();
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
['minimal', 'sha256', 1, 0, 0, 1],
|
||||
['huge', 'sha512', 1024, 1024, 1024, 16320],
|
||||
];
|
||||
|
||||
for (const [desc, digest, ikmLen, saltLen, infoLen, keyLen] of testCases) {
|
||||
for (const strings of [false, true]) {
|
||||
it(`${desc} (${strings ? 'strings' : 'buffers'})`, async function () {
|
||||
let isi = await Promise.all([
|
||||
crypto.randomBytes(ikmLen),
|
||||
crypto.randomBytes(saltLen),
|
||||
crypto.randomBytes(infoLen),
|
||||
]);
|
||||
if (strings) isi = isi.map((b) => b.toString('hex').slice(0, b.length));
|
||||
const args = [digest, ...isi, keyLen];
|
||||
assert.equal(
|
||||
ab2hex(await crypto.exportedForTesting.hkdfFallback(...args)),
|
||||
ab2hex(await nodeHkdf(...args)));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue