// META: title=WebCryptoAPI: digest() KangarooTwelve 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(, customization)] var kt128Vectors = [ [new Uint8Array(0), 256, '1ac2d450fc3b4205d19da7bfca1b3751' + '3c0803577ac7167f06fe2ce1f0ef39e5'], [new Uint8Array(0), 512, '1ac2d450fc3b4205d19da7bfca1b3751' + '3c0803577ac7167f06fe2ce1f0ef39e5' + '4269c056b8c82e48276038b6d292966c' + 'c07a3d4645272e31ff38508139eb0a71'], [ptn(1), 256, '2bda92450e8b147f8a7cb629e784a058' + 'efca7cf7d8218e02d345dfaa65244a1f'], [ptn(17), 256, '6bf75fa2239198db4772e36478f8e19b' + '0f371205f6a9a93a273f51df37122888'], [ptn(Math.pow(17, 2)), 256, '0c315ebcdedbf61426de7dcf8fb725d1' + 'e74675d7f5327a5067f367b108ecb67c'], [ptn(Math.pow(17, 3)), 256, 'cb552e2ec77d9910701d578b457ddf77' + '2c12e322e4ee7fe417f92c758f0d59d0'], [ptn(Math.pow(17, 4)), 256, '8701045e22205345ff4dda05555cbb5c' + '3af1a771c2b89baef37db43d9998b9fe'], [ptn(Math.pow(17, 5)), 256, '844d610933b1b9963cbdeb5ae3b6b05c' + 'c7cbd67ceedf883eb678a0a8e0371682'], [ptn(Math.pow(17, 6)), 256, '3c390782a8a4e89fa6367f72feaaf132' + '55c8d95878481d3cd8ce85f58e880af8'], [new Uint8Array(0), 256, 'fab658db63e94a246188bf7af69a1330' + '45f46ee984c56e3c3328caaf1aa1a583', ptn(1)], [new Uint8Array([0xff]), 256, 'd848c5068ced736f4462159b9867fd4c' + '20b808acc3d5bc48e0b06ba0a3762ec4', ptn(41)], [new Uint8Array([0xff, 0xff, 0xff]), 256, 'c389e5009ae57120854c2e8c64670ac0' + '1358cf4c1baf89447a724234dc7ced74', ptn(Math.pow(41, 2))], [new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 256, '75d2f86a2e644566726b4fbcfc5657b9' + 'dbcf070c7b0dca06450ab291d7443bcf', ptn(Math.pow(41, 3))], [ptn(8191), 256, '1b577636f723643e990cc7d6a6598374' + '36fd6a103626600eb8301cd1dbe553d6'], [ptn(8192), 256, '48f256f6772f9edfb6a8b661ec92dc93' + 'b95ebd05a08a17b39ae3490870c926c3'], [ptn(8192), 256, '3ed12f70fb05ddb58689510ab3e4d23c' + '6c6033849aa01e1d8c220a297fedcd0b', ptn(8189)], [ptn(8192), 256, '6a7c1b6a5cd0d8c9ca943a4a216cc646' + '04559a2ea45f78570a15253d67ba00ae', ptn(8190)], ]; var kt256Vectors = [ [new Uint8Array(0), 512, 'b23d2e9cea9f4904e02bec06817fc10c' + 'e38ce8e93ef4c89e6537076af8646404' + 'e3e8b68107b8833a5d30490aa3348235' + '3fd4adc7148ecb782855003aaebde4a9'], [new Uint8Array(0), 1024, 'b23d2e9cea9f4904e02bec06817fc10c' + 'e38ce8e93ef4c89e6537076af8646404' + 'e3e8b68107b8833a5d30490aa3348235' + '3fd4adc7148ecb782855003aaebde4a9' + 'b0925319d8ea1e121a609821ec19efea' + '89e6d08daee1662b69c840289f188ba8' + '60f55760b61f82114c030c97e5178449' + '608ccd2cd2d919fc7829ff69931ac4d0'], [ptn(1), 512, '0d005a194085360217128cf17f91e1f7' + '1314efa5564539d444912e3437efa17f' + '82db6f6ffe76e781eaa068bce01f2bbf' + '81eacb983d7230f2fb02834a21b1ddd0'], [ptn(17), 512, '1ba3c02b1fc514474f06c8979978a905' + '6c8483f4a1b63d0dccefe3a28a2f323e' + '1cdcca40ebf006ac76ef039715234683' + '7b1277d3e7faa9c9653b19075098527b'], [ptn(Math.pow(17, 2)), 512, 'de8ccbc63e0f133ebb4416814d4c66f6' + '91bbf8b6a61ec0a7700f836b086cb029' + 'd54f12ac7159472c72db118c35b4e6aa' + '213c6562caaa9dcc518959e69b10f3ba'], [ptn(Math.pow(17, 3)), 512, '647efb49fe9d717500171b41e7f11bd4' + '91544443209997ce1c2530d15eb1ffbb' + '598935ef954528ffc152b1e4d731ee26' + '83680674365cd191d562bae753b84aa5'], [ptn(Math.pow(17, 4)), 512, 'b06275d284cd1cf205bcbe57dccd3ec1' + 'ff6686e3ed15776383e1f2fa3c6ac8f0' + '8bf8a162829db1a44b2a43ff83dd89c3' + 'cf1ceb61ede659766d5ccf817a62ba8d'], [ptn(Math.pow(17, 5)), 512, '9473831d76a4c7bf77ace45b59f1458b' + '1673d64bcd877a7c66b2664aa6dd149e' + '60eab71b5c2bab858c074ded81ddce2b' + '4022b5215935c0d4d19bf511aeeb0772'], [ptn(Math.pow(17, 6)), 512, '0652b740d78c5e1f7c8dcc1777097382' + '768b7ff38f9a7a20f29f413bb1b3045b' + '31a5578f568f911e09cf44746da84224' + 'a5266e96a4a535e871324e4f9c7004da'], [new Uint8Array(0), 512, '9280f5cc39b54a5a594ec63de0bb9937' + '1e4609d44bf845c2f5b8c316d72b1598' + '11f748f23e3fabbe5c3226ec96c62186' + 'df2d33e9df74c5069ceecbb4dd10eff6', ptn(1)], [new Uint8Array([0xff]), 512, '47ef96dd616f200937aa7847e34ec2fe' + 'ae8087e3761dc0f8c1a154f51dc9ccf8' + '45d7adbce57ff64b639722c6a1672e3b' + 'f5372d87e00aff89be97240756998853', ptn(41)], [new Uint8Array([0xff, 0xff, 0xff]), 512, '3b48667a5051c5966c53c5d42b95de45' + '1e05584e7806e2fb765eda959074172c' + 'b438a9e91dde337c98e9c41bed94c4e0' + 'aef431d0b64ef2324f7932caa6f54969', ptn(Math.pow(41, 2))], [new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 512, 'e0911cc00025e1540831e266d94add9b' + '98712142b80d2629e643aac4efaf5a3a' + '30a88cbf4ac2a91a2432743054fbcc98' + '97670e86ba8cec2fc2ace9c966369724', ptn(Math.pow(41, 3))], [ptn(8191), 512, '3081434d93a4108d8d8a3305b89682ce' + 'bedc7ca4ea8a3ce869fbb73cbe4a58ee' + 'f6f24de38ffc170514c70e7ab2d01f03' + '812616e863d769afb3753193ba045b20'], [ptn(8192), 512, 'c6ee8e2ad3200c018ac87aaa031cdac2' + '2121b412d07dc6e0dccbb53423747e9a' + '1c18834d99df596cf0cf4b8dfafb7bf0' + '2d139d0c9035725adc1a01b7230a41fa'], [ptn(8192), 512, '74e47879f10a9c5d11bd2da7e194fe57' + 'e86378bf3c3f7448eff3c576a0f18c5c' + 'aae0999979512090a7f348af4260d4de' + '3c37f1ecaf8d2c2c96c1d16c64b12496', ptn(8189)], [ptn(8192), 512, 'f4b5908b929ffe01e0f79ec2f21243d4' + '1a396b2e7303a6af1d6399cd6c7a0a2d' + 'd7c4f607e8277f9c9b1cb4ab9ddc59d4' + 'b92d1fc7558441f1832c3279a4241b8b', ptn(8190)], ]; // Large output tests: verify last N bytes of extended output var largeOutputTests = [ // [algorithm, outputLengthBits, lastNBytes, expectedLastBytes] ['KT128', 10032 * 8, 32, 'e8dc563642f7228c84684c898405d3a8' + '34799158c079b12880277a1d28e2ff6d'], ['KT256', 10064 * 8, 64, 'ad4a1d718cf950506709a4c33396139b' + '4449041fc79a05d68da35f1e453522e0' + '56c64fe94958e7085f2964888259b993' + '2752f3ccd855288efee5fcbb8b563069'], ]; 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 customizationEqual(emptyDataVector, customization) { return equalBuffers(customization ?? new Uint8Array(0), emptyDataVector[3] ?? new Uint8Array(0)); } function outputLengthLessOrEqual(emptyDataVector, outputLength) { return outputLength <= emptyDataVector[1]; } var allVectors = { KT128: kt128Vectors, KT256: kt256Vectors, }; 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 customization = vector[3]; var algorithmParams = { name: alg, outputLength: outputLength }; if (customization !== undefined) algorithmParams.customization = customization; var label = alg + ' vector #' + (i + 1) + ' (' + outputLength + ' bit output, ' + input.length + ' byte input' + (customization !== undefined ? ', C=' + customization.length + ' bytes' : '') + ')'; 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, customization: customization, }, 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, customization: customization, }, buffer) .then(function (result) { if (customizationEqual(emptyDataVector, customization) && 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'); } }); });