blob: 243931cd119802e5ec0afdabb6e47a1af7cd6bb3 [file] [log] [blame] [edit]
// META: title=WebCryptoAPI: digest() TurboSHAKE algorithms
// META: script=../util/helpers.js
// META: timeout=long
var subtle = crypto.subtle; // Change to test prefixed implementations
// Generates a Uint8Array of length n by repeating the pattern 00 01 02 .. F9 FA.
function ptn(n) {
var buf = new Uint8Array(n);
for (var i = 0; i < n; i++)
buf[i] = i % 251;
return buf;
}
function hexToBytes(hex) {
var bytes = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length; i += 2)
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
return bytes;
}
// RFC 9861 Section 5 test vectors
// [input, outputLengthBits, expected hex(, domainSeparation)]
var turboSHAKE128Vectors = [
[new Uint8Array(0), 256,
'1e415f1c5983aff2169217277d17bb53' +
'8cd945a397ddec541f1ce41af2c1b74c'],
[new Uint8Array(0), 512,
'1e415f1c5983aff2169217277d17bb53' +
'8cd945a397ddec541f1ce41af2c1b74c' +
'3e8ccae2a4dae56c84a04c2385c03c15' +
'e8193bdf58737363321691c05462c8df'],
[ptn(1), 256,
'55cedd6f60af7bb29a4042ae832ef3f5' +
'8db7299f893ebb9247247d856958daa9'],
[ptn(17), 256,
'9c97d036a3bac819db70ede0ca554ec6' +
'e4c2a1a4ffbfd9ec269ca6a111161233'],
[ptn(Math.pow(17, 2)), 256,
'96c77c279e0126f7fc07c9b07f5cdae1' +
'e0be60bdbe10620040e75d7223a624d2'],
[ptn(Math.pow(17, 3)), 256,
'd4976eb56bcf118520582b709f73e1d6' +
'853e001fdaf80e1b13e0d0599d5fb372'],
[ptn(Math.pow(17, 4)), 256,
'da67c7039e98bf530cf7a37830c6664e' +
'14cbab7f540f58403b1b82951318ee5c'],
[ptn(Math.pow(17, 5)), 256,
'b97a906fbf83ef7c812517abf3b2d0ae' +
'a0c4f60318ce11cf103925127f59eecd'],
[ptn(Math.pow(17, 6)), 256,
'35cd494adeded2f25239af09a7b8ef0c' +
'4d1ca4fe2d1ac370fa63216fe7b4c2b1'],
[new Uint8Array([0xff, 0xff, 0xff]), 256,
'bf323f940494e88ee1c540fe660be8a0' +
'c93f43d15ec006998462fa994eed5dab', 0x01],
[new Uint8Array([0xff]), 256,
'8ec9c66465ed0d4a6c35d13506718d68' +
'7a25cb05c74cca1e42501abd83874a67', 0x06],
[new Uint8Array([0xff, 0xff, 0xff]), 256,
'b658576001cad9b1e5f399a9f77723bb' +
'a05458042d68206f7252682dba3663ed', 0x07],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 256,
'8deeaa1aec47ccee569f659c21dfa8e1' +
'12db3cee37b18178b2acd805b799cc37', 0x0b],
[new Uint8Array([0xff]), 256,
'553122e2135e363c3292bed2c6421fa2' +
'32bab03daa07c7d6636603286506325b', 0x30],
[new Uint8Array([0xff, 0xff, 0xff]), 256,
'16274cc656d44cefd422395d0f9053bd' +
'a6d28e122aba15c765e5ad0e6eaf26f9', 0x7f],
];
var turboSHAKE256Vectors = [
[new Uint8Array(0), 512,
'367a329dafea871c7802ec67f905ae13' +
'c57695dc2c6663c61035f59a18f8e7db' +
'11edc0e12e91ea60eb6b32df06dd7f00' +
'2fbafabb6e13ec1cc20d995547600db0'],
[ptn(1), 512,
'3e1712f928f8eaf1054632b2aa0a246e' +
'd8b0c378728f60bc970410155c28820e' +
'90cc90d8a3006aa2372c5c5ea176b068' +
'2bf22bae7467ac94f74d43d39b0482e2'],
[ptn(17), 512,
'b3bab0300e6a191fbe61379398359235' +
'78794ea54843f5011090fa2f3780a9e5' +
'cb22c59d78b40a0fbff9e672c0fbe097' +
'0bd2c845091c6044d687054da5d8e9c7'],
[ptn(Math.pow(17, 2)), 512,
'66b810db8e90780424c0847372fdc957' +
'10882fde31c6df75beb9d4cd9305cfca' +
'e35e7b83e8b7e6eb4b78605880116316' +
'fe2c078a09b94ad7b8213c0a738b65c0'],
[ptn(Math.pow(17, 3)), 512,
'c74ebc919a5b3b0dd1228185ba02d29e' +
'f442d69d3d4276a93efe0bf9a16a7dc0' +
'cd4eabadab8cd7a5edd96695f5d360ab' +
'e09e2c6511a3ec397da3b76b9e1674fb'],
[ptn(Math.pow(17, 4)), 512,
'02cc3a8897e6f4f6ccb6fd46631b1f52' +
'07b66c6de9c7b55b2d1a23134a170afd' +
'ac234eaba9a77cff88c1f020b7372461' +
'8c5687b362c430b248cd38647f848a1d'],
[ptn(Math.pow(17, 5)), 512,
'add53b06543e584b5823f626996aee50' +
'fe45ed15f20243a7165485acb4aa76b4' +
'ffda75cedf6d8cdc95c332bd56f4b986' +
'b58bb17d1778bfc1b1a97545cdf4ec9f'],
[ptn(Math.pow(17, 6)), 512,
'9e11bc59c24e73993c1484ec66358ef7' +
'1db74aefd84e123f7800ba9c4853e02c' +
'fe701d9e6bb765a304f0dc34a4ee3ba8' +
'2c410f0da70e86bfbd90ea877c2d6104'],
[new Uint8Array([0xff, 0xff, 0xff]), 512,
'd21c6fbbf587fa2282f29aea620175fb' +
'0257413af78a0b1b2a87419ce031d933' +
'ae7a4d383327a8a17641a34f8a1d1003' +
'ad7da6b72dba84bb62fef28f62f12424', 0x01],
[new Uint8Array([0xff]), 512,
'738d7b4e37d18b7f22ad1b5313e357e3' +
'dd7d07056a26a303c433fa3533455280' +
'f4f5a7d4f700efb437fe6d281405e07b' +
'e32a0a972e22e63adc1b090daefe004b', 0x06],
[new Uint8Array([0xff, 0xff, 0xff]), 512,
'18b3b5b7061c2e67c1753a00e6ad7ed7' +
'ba1c906cf93efb7092eaf27fbeebb755' +
'ae6e292493c110e48d260028492b8e09' +
'b5500612b8f2578985ded5357d00ec67', 0x07],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 512,
'bb36764951ec97e9d85f7ee9a67a7718' +
'fc005cf42556be79ce12c0bde50e5736' +
'd6632b0d0dfb202d1bbb8ffe3dd74cb0' +
'0834fa756cb03471bab13a1e2c16b3c0', 0x0b],
[new Uint8Array([0xff]), 512,
'f3fe12873d34bcbb2e608779d6b70e7f' +
'86bec7e90bf113cbd4fdd0c4e2f4625e' +
'148dd7ee1a52776cf77f240514d9ccfc' +
'3b5ddab8ee255e39ee389072962c111a', 0x30],
[new Uint8Array([0xff, 0xff, 0xff]), 512,
'abe569c1f77ec340f02705e7d37c9ab7' +
'e155516e4a6a150021d70b6fac0bb40c' +
'069f9a9828a0d575cd99f9bae435ab1a' +
'cf7ed9110ba97ce0388d074bac768776', 0x7f],
];
// Large output tests: verify last 32 bytes of extended output
var largeOutputTests = [
// [algorithm, outputLengthBits, lastNBytes, expectedLastBytes]
['TurboSHAKE128', 10032 * 8, 32,
'a3b9b0385900ce761f22aed548e754da' +
'10a5242d62e8c658e3f3a923a7555607'],
['TurboSHAKE256', 10032 * 8, 32,
'abefa11630c661269249742685ec082f' +
'207265dccf2f43534e9c61ba0c9d1d75'],
];
largeOutputTests.forEach(function (entry) {
var alg = entry[0];
var outputLength = entry[1];
var lastN = entry[2];
var expected = entry[3];
promise_test(function (test) {
return subtle
.digest({ name: alg, outputLength: outputLength }, new Uint8Array(0))
.then(function (result) {
var full = new Uint8Array(result);
var last = full.slice(full.length - lastN);
assert_true(
equalBuffers(last.buffer, hexToBytes(expected)),
'last ' + lastN + ' bytes of digest match expected'
);
});
}, alg + ' with ' + outputLength + ' bit output, verify last ' + lastN + ' bytes');
});
function domainSeparationEqual(emptyDataVector, domainSeparation) {
return (domainSeparation ?? 0x1f) === (emptyDataVector[3] ?? 0x1f);
}
function outputLengthLessOrEqual(emptyDataVector, outputLength) {
return outputLength <= emptyDataVector[1];
}
var allVectors = {
TurboSHAKE128: turboSHAKE128Vectors,
TurboSHAKE256: turboSHAKE256Vectors,
};
Object.keys(allVectors).forEach(function (alg) {
var emptyDataVector = allVectors[alg][0];
allVectors[alg].forEach(function (vector, i) {
var input = vector[0];
var outputLength = vector[1];
var expected = vector[2];
var domainSeparation = vector[3];
var algorithmParams = { name: alg, outputLength: outputLength };
if (domainSeparation !== undefined)
algorithmParams.domainSeparation = domainSeparation;
var label = alg + ' vector #' + (i + 1) +
' (' + outputLength + ' bit output, ' + input.length + ' byte input' +
(domainSeparation !== undefined ? ', D=0x' + domainSeparation.toString(16) : '') + ')';
promise_test(function (test) {
return subtle
.digest(algorithmParams, input)
.then(function (result) {
assert_true(
equalBuffers(result, hexToBytes(expected)),
'digest matches expected'
);
});
}, label);
if (input.length > 0) {
promise_test(function (test) {
var buffer = new Uint8Array(input);
// Alter the buffer before calling digest
buffer[0] = ~buffer[0];
return subtle
.digest({
get name() {
// Alter the buffer back while calling digest
buffer[0] = input[0];
return alg;
},
outputLength: outputLength,
domainSeparation: domainSeparation,
}, buffer)
.then(function (result) {
assert_true(
equalBuffers(result, hexToBytes(expected)),
'digest matches expected'
);
});
}, label + ' and altered buffer during call');
promise_test(function (test) {
var buffer = new Uint8Array(input);
var promise = subtle
.digest(algorithmParams, buffer)
.then(function (result) {
assert_true(
equalBuffers(result, hexToBytes(expected)),
'digest matches expected'
);
});
// Alter the buffer after calling digest
buffer[0] = ~buffer[0];
return promise;
}, label + ' and altered buffer after call');
promise_test(function (test) {
var buffer = new Uint8Array(input);
return subtle
.digest({
get name() {
// Transfer the buffer while calling digest
buffer.buffer.transfer();
return alg;
},
outputLength: outputLength,
domainSeparation: domainSeparation,
}, buffer)
.then(function (result) {
if (domainSeparationEqual(emptyDataVector, domainSeparation) && outputLengthLessOrEqual(emptyDataVector, outputLength)) {
assert_true(
equalBuffers(result, Uint8Array.fromHex(emptyDataVector[2]).subarray(0, outputLength / 8)),
'digest on transferred buffer should match result for empty buffer'
);
} else {
assert_equals(result.byteLength, outputLength / 8,
'digest on transferred buffer should have correct output length');
}
});
}, label + ' and transferred buffer during call');
promise_test(function (test) {
var buffer = new Uint8Array(input);
var promise = subtle
.digest(algorithmParams, buffer)
.then(function (result) {
assert_true(
equalBuffers(result, hexToBytes(expected)),
'digest matches expected'
);
});
// Transfer the buffer after calling digest
buffer.buffer.transfer();
return promise;
}, label + ' and transferred buffer after call');
}
});
});