node/test/pummel/test-webcrypto-derivebits-pbkdf2.js
Filip Skokan c6c4cdab36 crypto: fix webcrypto deriveBits validations
PR-URL: https://github.com/nodejs/node/pull/44173
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
2022-08-12 21:31:14 +02:00

680 lines
21 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto) {
common.skip('missing crypto');
}
if (common.isPi) {
common.skip('Too slow for Raspberry Pi devices');
}
const assert = require('assert');
const { subtle } = require('crypto').webcrypto;
function getDeriveKeyInfo(name, length, hash, ...usages) {
return [{ name, length, hash }, usages];
}
const kDerivedKeyTypes = [
['AES-CBC', 128, undefined, 'encrypt', 'decrypt'],
['AES-CBC', 256, undefined, 'encrypt', 'decrypt'],
['AES-CTR', 128, undefined, 'encrypt', 'decrypt'],
['AES-CTR', 256, undefined, 'encrypt', 'decrypt'],
['AES-GCM', 128, undefined, 'encrypt', 'decrypt'],
['AES-GCM', 256, undefined, 'encrypt', 'decrypt'],
['AES-KW', 128, undefined, 'wrapKey', 'unwrapKey'],
['AES-KW', 256, undefined, 'wrapKey', 'unwrapKey'],
['HMAC', 256, 'SHA-1', 'sign', 'verify'],
['HMAC', 256, 'SHA-256', 'sign', 'verify'],
['HMAC', 256, 'SHA-384', 'sign', 'verify'],
['HMAC', 256, 'SHA-512', 'sign', 'verify'],
];
const kPasswords = {
short: '5040737377307264',
long: '55736572732073686f756c64207069636b206c6f6' +
'e6720706173737068726173657320286e6f742075' +
'73652073686f72742070617373776f7264732921',
// TODO(@jasnell): Zero-length password not currently supported
// empty: ''
};
const kSalts = {
short: '4e61436c',
long: '536f6469756d2043686c6f7269646520636f6d706f756e64',
empty: ''
};
const kDerivations = {
short: {
short: {
'SHA-384': {
'1': '80cd0f15364366a72551c37975f7b637ba89' +
'c29b4639ec720f69a70dbbed515c',
'1000': 'aaec5a976d4d35cb2024486fc9f9bb9aa' +
'3eae7cef2bce62664b5b3751cf50ff1',
'100000': '6f5ea3c6c6f5e4833467b47c3a671e6' +
'5714e87071bd1e36d716f846b5cd28980'
},
'SHA-512': {
'1': '69f4d5cef5c7d8ba938e880388c8f63b6b2' +
'448b2626d1343fc5cb68bbd7f27b2',
'1000': '865c5945e11f5bf3ddf002e7cb1748f6' +
'224d2671e806dad4aaf090a04367da29',
'100000': '483ba7f2e2fe382cf61d20b29812e2' +
'd49610a60041ae40ecf9fc7ef138e93876'
},
'SHA-1': {
'1': '4624dbd21373ee5659c125b184eedaa26a3' +
'3b77ca11314b9f0c9dae1e44e9b04',
'1000': '5388ea5e62e1b557981abe5ce4132127' +
'58aa6a9d2c5bf08c019d459dba666b90',
'100000': 'f58f435fbc5c05865c914fd972108a' +
'09457d5f9a48f14e75e4cc02d98983038a'
},
'SHA-256': {
'1': 'c6bc55a404adcea36a1ab56798085e0aaf6' +
'97f6bb2c16a5072f838f17dfe6cb6',
'1000': '4e6ca57957439be3a75370424225e221' +
'1d55f05af00561df3f3efee9116bc34c',
'100000': 'ab25796598e74b29c324f5ba4d90ea' +
'7dc89fc6891041b4d56c94156505f722c0'
}
},
long: {
'SHA-384': {
'1': '6807346cc53eded1cb964a72628589a6bd4' +
'8355990bfdfe7465109710207059d',
'1000': 'a310ef3c6b3a95e6d8ca6644e3dcfd88' +
'222a59fe8e00c52d6a12631d82c1d24b',
'100000': '2c8c6674c879cf1850bc9b7fbdcc6e' +
'a7abb0a1522196a866875305dea57486f3'
},
'SHA-512': {
'1': '5777027aff4051fb9b43c1f1ef0463bd677' +
'51175d428a13da3da845a591332cd',
'1000': '9c17fe96895eadbfd1cc095fc1bb834f' +
'28e5ccc9ec96ca814cff941a4bf40727',
'100000': 'b479c9715c421638dce0a705fc0b7b' +
'a7d56fa3063188063580e070dff1db497c'
},
'SHA-1': {
'1': '576f7c165825bef9ef14b4bc2c82469d1e4' +
'08ff8e7ba306694797f9e45b766ed',
'1000': '89d3b27b5f6e8af015f2f87cf368a143' +
'8a206c4ecf5fe681fc3bf94c56213ef6',
'100000': '1e39e8bf6676fcd3156655457afa14' +
'bee771dbcbfcd07241c7cee209a7cb1fe9'
},
'SHA-256': {
'1': '12b90f594f0908cf912d655c948f9c2a1ea' +
'b855765bc12785ef18aa02b8e7edc',
'1000': 'b1a7b7dc20df174a4a0e410dbfaf03b4' +
'c375c450a89d7a9ed349b4e52e64dfd8',
'100000': 'd4594d8a1b59520a48878922a65d66' +
'3d28f6a5fa49e931d300d8f9baf93d0aeb'
}
},
empty: {
'SHA-384': {
'1': '4f1089c01e438bde649a379fa418fbc3b85' +
'6258772dfe911806f9bd0809fbc7e',
'1000': 'aeb5f97d6627eebcde6b139a00895500' +
'30f7401c67e01c057a3338175e3f3a17',
'100000': 'd7687df6c781dc88d64ef9cbaf95d3' +
'd5d1155f66b230239e6e81c1550c8840cf'
},
'SHA-512': {
'1': '8f7b7d459c752f64bf12be625b65d496ac2' +
'4ea36516b168e16fb026845b4e82e',
'1000': 'b5ac720b7abe0832fc51a31b1ec5673b' +
'eb1e41840adfd3d606e8638f4006eb48',
'100000': 'ba1a0f36bad771526564051eb9ca20' +
'7da19b62e53762349976a9a3d1b0ef7e20'
},
'SHA-1': {
'1': 'c0cffb0ce5db351faa24dad5902583cfc30' +
'a9f54d9aa6991fe821d03122127e9',
'1000': '736f3c3d6ebcc2a7b970403e2696c0eb' +
'4cd1770f55f196fc7089e666c11f77da',
'100000': '1c5095ac9a7bd410ef0f72c993eca9' +
'1bb0e571e9b2fbab704f8c131191fad16c'
},
'SHA-256': {
'1': '019e54ab42f00485d3aa1b26fcde21ae5f5' +
'2cb0f0960ffc9767f25c65e2db2f9',
'1000': 'b9d2f2217b4ee5a8bf0345f36b2c9887' +
'33f503a975dfeac7b7135f54a5f29971',
'100000': 'a7a2869829797807b3e576c17878b4' +
'66449e89e60447d541775a96eb7c1a5ded'
}
}
},
long: {
short: {
'SHA-384': {
'1': '5ede8836fdabeec5d3733b434abac443d41' +
'5193b599e0926193b000f406a5a7d',
'1000': 'faa442fbabf4058cc65368b53d7ec511' +
'3c09ea7e5e3743312f4bebedd980ba37',
'100000': 'f62ae6c7871b181aa71232f5eb8837' +
'2498ef32ac0a7d715119e8f052eb102d29'
},
'SHA-512': {
'1': '3e9c12b3f6dfb6441594ec7063fca962ffd' +
'a10b6cf30b898a31ef9f1306b1119',
'1000': 'f0928f50a155f26a8c9c1bc7f3b5cb53' +
'1c53a8f51040c9ce5fc79d430ff0c0f4',
'100000': '974acfbb0f0f20c81ec92829f38c3d' +
'af086a7df58b912b856d1f5ecc9355ef1b'
},
'SHA-1': {
'1': '8ae72f94e6fcd54fcbfca66200a211a51b2' +
'f846787d20b6808bedf156ce46ca0',
'1000': '53b42161134e15c871abd71aba1390d0' +
'1f4c6a940caaf5c179258d8f1b1d680b',
'100000': 'a7fda4c79dd3ba1a875f65e9248b21' +
'0899ca0814ae38995d8ce5a53560cbac31'
},
'SHA-256': {
'1': 'ffa1e9a727a92c27ae6f74b1c7978f9e1af' +
'860e106376340ac43d969d136405b',
'1000': 'eeeb7714420a00b18acec2b5979d1da6' +
'1373202b7f8ba71b086293aab859e0a0',
'100000': 'deac70cbe3f1720e353b4e8016ddb5' +
'9475efb70b6a2385e735d2d6ea6d624a4d'
}
},
long: {
'SHA-384': {
'1': 'cf55422cef6e1bc49e6d082b2273d480e8f' +
'2e8822dadd1469c2a32d9657d12f1',
'1000': '35658551f0ec13398a7b45e0261cfd65' +
'4c1e52411e6e457dee68f4aeabe925a7',
'100000': '1abab5f1e461df378b88c0a22be76e' +
'f2f1627df74ac7cbfb84bdccb354bc8889'
},
'SHA-512': {
'1': 'de4afbc0add3e4d32f4bc6e122a88ae44a2' +
'b3ccf0148e7762bac05c43e94ef7f',
'1000': '43e12024c4d354727f7e58842ccb6033' +
'a161d60dc5ae516f076e4a58a1880d38',
'100000': 'f9a92384a4eadfc3560649b37fb676' +
'e83c453cbbd99f80bba6f0a10ebd150b52'
},
'SHA-1': {
'1': '1d104ea5d235006a12a80f71b80ee528048' +
'b64cc1a7a0f30f7df4ba26b8320c7',
'1000': '6e90c86ee07b873e965071025673ff05' +
'429f678c30f91b37e1e2da512036d320',
'100000': '141030763bf983c8564d5d4c935fe3' +
'ca3549608159ac1934c1599040668c2363'
},
'SHA-256': {
'1': 'fd5caeb8b3abe589bc159c4e51f800570e7' +
'4f64397a6c5ee131dfed93f0511aa',
'1000': '3fd587c94ba946b8b9dccddd2a5b74f6' +
'778d4f61e691f83ac47a2fa9580bfdf8',
'100000': '11992d8b813311244c544b62292945' +
'e208d403cebd6b9552a1a562065d9958ea'
}
},
empty: {
'SHA-384': {
'1': '49ab3f9f882fdb9e528b4d9f1b3e8c71d26' +
'39abf1701d56eb99bd51201e420ff',
'1000': 'f9ca148b0c041890bff8831db6174719' +
'7e94ce68f190edf269694b4d644861ca',
'100000': '1749dfcd77e5258519ea2231ba2cd6' +
'543b073339ac9b1545bb643153faf6d17b'
},
'SHA-512': {
'1': 'd1bfa1a6b8a977839f8c3f9d52dd02104e2' +
'029c0eb2a6208cc408816e7768a8c',
'1000': '457a7955ebecec71a51efb6237e5b1d6' +
'2f4deab5c93d7b3d11d1e70faffa417e',
'100000': 'e805ac9cc1d8412c42446d237d1b50' +
'4f9540b362bd1b75e451531e853e24753d'
},
'SHA-1': {
'1': 'a46a62986d9c3909f41014dd72cfe34a261' +
'247854d7312cf4fbead60b9b69edd',
'1000': 'e7375de5036766c40cb85f43b53fce4f' +
'fa402ab6be3571007ef5d55453fd7f0a',
'100000': '7a403d9a13aed8164e9c072c545462' +
'251fd942f1736a6bf03ce1c88330048e04'
},
'SHA-256': {
'1': '4f510c5181ac5c2c5fd4bd141f9712495be' +
'ca279624742b4d6d30d08b96c0a69',
'1000': '7e66c84bea888f92c348d9145585186c' +
'ae472b12fba7f0ad28179575c1aa815a',
'100000': '5f1a6ac4a56d9796a7309a78daaaf9' +
'18badaf5ed1eecc3f0b8a3a44c3d38d654'
}
}
},
// empty: {
// short: {
// 'SHA-384': {
// '1': 'e9f0da1e97dfa455f858ce6b9af1ecc0299' +
// 'f125ff1a847eb5d4955866f43e604',
// '1000': '7ff7954aeddf41795fc8300666786d49' +
// '74269aa91cc7e93811c953331d56d609',
// '100000': '1c73132b6a55e9d9de2cdbfe1f55bf' +
// '0ab59fd91f78f109c50096038b8557b147'
// },
// 'SHA-512': {
// '1': 'e7e2b41f4887421bcb764eb4a56f63d2502' +
// 'e33c764fbdf60626ad42ed9672342',
// '1000': 'd561c4c84e9c60ba4752a2d383bf55ef' +
// 'f643fc9e452252d6821e39449350cf72',
// '100000': 'efd00752bc9ffafb5a399dd1d5834e' +
// '8d2c2b676ecd4b2063fb1fe581d0f1380b'
// },
// 'SHA-1': {
// '1': 'a667da47b8f857b7c65f70a6c8e7a06ce0d' +
// '25211a2b6ebaf58dcaaf268b46b1d',
// '1000': '72c92bbd3ddab4789e88e42ad1cda83c' +
// 'c0729e6cb5106a577e50d5cf61782481',
// '100000': '06e19e1b83e6480b1554df2b31a2c9' +
// '2d1bfcf9bc1bdbc8751ff8685bdeef7dc9'
// },
// 'SHA-256': {
// '1': '2ddb49243eb3b5912cb260cdd87fb04ef0d' +
// '111bfa44d40a45e02a8a5c3c1518d',
// '1000': '2835f3ed53565420c90951509b0c1173' +
// 'b645174f1546ab3ac3e6c85cb471b53b',
// '100000': '80aed905ca32ae0bb2a9d8f532f048' +
// 'a0e672463eef9f83dfa7d88bca726553ea'
// }
// },
// long: {
// 'SHA-384': {
// '1': '7b0bcca81dd637a3b3398666619716c5f2b1' +
// 'f4a5c24e85c18a9955559e4d7692',
// '1000': '8bb89cf71972fe5acc16fdc5f8cffd2c2' +
// 'e7178c086b3bbe61cc1314619135958',
// '100000': '26c6a8ae4bd1fbe715ae478efff3eca' +
// 'e83afa617ed35bd4a3f63c3da76a42d22'
// },
// 'SHA-512': {
// '1': 'bb73f8168a8f391d3d54ca892fb72b8e603' +
// '5e37f891e5a70491b94dc05510bc4',
// '1000': '5cacc16cdfbe052cfd73a9891b8c0e78' +
// 'b19b2e07eae2423d48fed5e08aa8494b',
// '100000': '87fdfc293392cbf33ecc9b5141a2fe' +
// 'fa74d150499756863c484c0a78b6274d7f'
// },
// 'SHA-1': {
// '1': '1f46b40cf2fb3dc41a3d9ced8897b861050' +
// '36810e2bfac7040814bd65d428d67',
// '1000': 'cc5748ecc41288a0e13368543aaa2ef6' +
// '2c97ba7518fa88f6e11c35763fc930b4',
// '100000': '33e2993bf4729dc993fff66e69cc55' +
// '777135ebfabce533575bce4a96645a742c'
// },
// 'SHA-256': {
// '1': '61c935c462c3321c89663545d13a4f6b52b' +
// '5191cfb7479e58dcfe6444d43106c',
// '1000': '1353f7458237ab332ee052e29f829a2a' +
// 'b90e72630ea10493b4eecffb9ff89e1d',
// '100000': '79baf80ec582920538801e9d929ce0' +
// '7084277987488d733a026852c452f06fb4'
// }
// },
// empty: {
// 'SHA-384': {
// '1': '4bb042a5c28cee6f66f991c717fd7702677' +
// '87e2bb3031eae270d87d63ad99534',
// '1000': '9cbfe72d194da34e17c821dd1569ef50' +
// 'a86eb4d893591776adc6a5c21e0031cf',
// '100000': 'ed6bd7282567abe48d542d067d09f4' +
// '04bd044ae2cefe11dacc531c4764cd35cd'
// },
// 'SHA-512': {
// '1': '6d2ecbbbfb2e6dcd7056faf9af6aa06eae5' +
// '94391db983279a6bf27e0eb228614',
// '1000': 'cb93096c3a02beeb1c5fac36765c9011' +
// 'fe99f8d8ea62366048fc98cb98dfea8f',
// '100000': '89e16254ebad5cba72e0aebe1614c7' +
// 'f9b795a7505f2637206ce10a3449a2b8bb'
// },
// 'SHA-1': {
// '1': '1e437a1c79d75be61e91141dae20affc489' +
// '2cc99abcc3fe753887bccc8920176',
// '1000': '6e40910ac02ec89cebb9d898b13a09d1' +
// 'cd7adf6f8cc08cc473302c8973aa2e19',
// '100000': 'a9e1bebb36bc26d7c997d5483cbc8d' +
// 'e4a419d1e706571342632586ec330a7290'
// },
// 'SHA-256': {
// '1': 'f7ce0b653d2d72a4108cf5abe912ffdd777' +
// '616dbbb27a70e8204f3ae2d0f6fad',
// '1000': '4fc58a21c100ce1835b8f9991d738b56' +
// '965d14b24e1761fbdffc69ac5e0b667a',
// '100000': '64a868d4b23af696d3734d0b814d04' +
// 'cdd1ac280128e97653a05f32b49c13a29a'
// }
// }
// }
};
async function setupBaseKeys() {
const promises = [];
const baseKeys = {};
const noBits = {};
const noKey = {};
let wrongKey = null;
Object.keys(kPasswords).forEach((size) => {
promises.push(
subtle.importKey(
'raw',
Buffer.from(kPasswords[size], 'hex'),
{ name: 'PBKDF2' },
false,
['deriveKey', 'deriveBits'])
.then((key) => baseKeys[size] = key));
promises.push(
subtle.importKey(
'raw',
kPasswords[size],
{ name: 'PBKDF2' },
false,
['deriveBits'])
.then((key) => noKey[size] = key));
promises.push(
subtle.importKey(
'raw',
kPasswords[size],
{ name: 'PBKDF2' },
false,
['deriveKey'])
.then((key) => noBits[size] = key));
});
promises.push(
subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-521' },
false,
['deriveKey', 'deriveBits'])
.then((key) => wrongKey = key.privateKey));
await Promise.all(promises);
return { baseKeys, noBits, noKey, wrongKey };
}
async function testDeriveBits(
baseKeys,
size,
saltSize,
hash,
iterations) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
hash,
iterations
};
const bits = await subtle.deriveBits(algorithm, baseKeys[size], 256);
assert(bits instanceof ArrayBuffer);
assert.strictEqual(
Buffer.from(bits).toString('hex'),
kDerivations[size][saltSize][hash][iterations]);
}
async function testDeriveBitsBadLengths(
baseKeys,
size,
saltSize,
hash,
iterations) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
iterations,
hash
};
return Promise.all([
assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size], 0), {
message: /length cannot be zero/,
name: 'OperationError',
}),
assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size], null), {
message: 'length cannot be null',
name: 'OperationError',
}),
assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size], 15), {
message: /length must be a multiple of 8/,
name: 'OperationError',
}),
]);
}
async function testDeriveBitsBadHash(
baseKeys,
size,
saltSize,
hash,
iterations) {
const salt = Buffer.from(kSalts[saltSize], 'hex');
const algorithm = { name: 'HKDF', salt, iterations };
return Promise.all([
assert.rejects(
subtle.deriveBits(
{
...algorithm,
hash: hash.substring(0, 3) + hash.substring(4)
}, baseKeys[size], 256), {
message: /Unrecognized name/
}),
assert.rejects(
subtle.deriveBits(
{
...algorithm,
hash: 'HKDF'
},
baseKeys[size], 256), {
message: /Unrecognized name/
}),
]);
}
async function testDeriveBitsBadUsage(
noBits,
size,
saltSize,
hash,
iterations) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
iterations,
hash
};
return assert.rejects(
subtle.deriveBits(algorithm, noBits[size], 256), {
message: /baseKey does not have deriveBits usage/
});
}
async function testDeriveKey(
baseKeys,
size,
saltSize,
hash,
iterations,
keyType,
usages) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
hash,
iterations
};
const key = await subtle.deriveKey(
algorithm,
baseKeys[size],
keyType,
true,
usages);
const bits = await subtle.exportKey('raw', key);
assert.strictEqual(
Buffer.from(bits).toString('hex'),
kDerivations[size][saltSize][hash][iterations]
.slice(0, keyType.length / 4));
}
async function testDeriveKeyBadHash(
baseKeys,
size,
saltSize,
hash,
iterations,
keyType,
usages) {
const salt = Buffer.from(kSalts[saltSize], 'hex');
const algorithm = { name: 'PBKDF2', salt, iterations };
return Promise.all([
assert.rejects(
subtle.deriveKey(
{
...algorithm,
hash: hash.substring(0, 3) + hash.substring(4)
},
baseKeys[size],
keyType,
true,
usages),
{ message: /Unrecognized name/ }),
assert.rejects(
subtle.deriveKey(
{
...algorithm,
hash: 'HKDF'
},
baseKeys[size],
keyType,
true,
usages),
{ message: /Unrecognized name/ }),
]);
}
async function testDeriveKeyBadUsage(
noKey,
size,
saltSize,
hash,
iterations,
keyType,
usages) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
iterations,
hash
};
return assert.rejects(
subtle.deriveKey(algorithm, noKey[size], keyType, true, usages), {
message: /baseKey does not have deriveKey usage/
});
}
async function testWrongKeyType(
wrongKey,
saltSize,
hash,
iterations,
keyType,
usages
) {
const algorithm = {
name: 'PBKDF2',
salt: Buffer.from(kSalts[saltSize], 'hex'),
iterations,
hash
};
return assert.rejects(
subtle.deriveKey(algorithm, wrongKey, keyType, true, usages), {
message: /Key algorithm mismatch/
});
}
(async function() {
const {
baseKeys,
noBits,
noKey,
wrongKey
} = await setupBaseKeys();
const variations = [];
Object.keys(kDerivations).forEach((size) => {
Object.keys(kDerivations[size]).forEach((saltSize) => {
Object.keys(kDerivations[size][saltSize]).forEach((hash) => {
Object.keys(kDerivations[size][saltSize][hash])
.forEach((iterations) => {
const args = [baseKeys, size, saltSize, hash, iterations | 0];
variations.push(testDeriveBits(...args));
variations.push(testDeriveBitsBadLengths(...args));
variations.push(testDeriveBitsBadHash(...args));
variations.push(
testDeriveBitsBadUsage(
noBits,
size,
saltSize,
hash,
iterations | 0));
kDerivedKeyTypes.forEach((keyType) => {
const keyArgs = getDeriveKeyInfo(...keyType);
variations.push(testDeriveKey(...args, ...keyArgs));
variations.push(testDeriveKeyBadHash(...args, ...keyArgs));
variations.push(
testDeriveKeyBadUsage(
noKey,
size,
saltSize,
hash,
iterations | 0,
...keyArgs));
variations.push(
testWrongKeyType(
wrongKey,
saltSize,
hash,
iterations,
...keyArgs));
});
});
});
});
});
await Promise.all(variations);
})().then(common.mustCall());