// 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'); } }); });