LibCrypto: Add SHAKE digest support
Introduce a new SHAKE hash wrapper in LibCrypto backed by OpenSSL. Wire cSHAKE128 and cSHAKE256 into WebCrypto. Note that cSHAKE with non-empty functionName or customization is currently rejected due to OpenSSL EVP limitations. This fixes WPT: WebCryptoAPI/digest/cshake.tentative.https.any.html
This commit is contained in:
parent
a9bd0ca96c
commit
cd8465a6b5
9 changed files with 485 additions and 13 deletions
|
|
@ -14,6 +14,7 @@ set(SOURCES
|
|||
Curves/EdwardsCurve.cpp
|
||||
Curves/SECPxxxr1.cpp
|
||||
Hash/Argon2.cpp
|
||||
Hash/SHAKE.cpp
|
||||
Hash/BLAKE2b.cpp
|
||||
Hash/HKDF.cpp
|
||||
Hash/MD5.cpp
|
||||
|
|
|
|||
50
Libraries/LibCrypto/Hash/SHAKE.cpp
Normal file
50
Libraries/LibCrypto/Hash/SHAKE.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2025, mikiubo <michele.uboldi@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <LibCrypto/Hash/SHAKE.h>
|
||||
#include <LibCrypto/OpenSSL.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
SHAKE::SHAKE(SHAKEKind kind)
|
||||
{
|
||||
m_md = (kind == SHAKEKind::CSHAKE128) ? EVP_shake128() : EVP_shake256();
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> SHAKE::digest(
|
||||
ReadonlyBytes data,
|
||||
u32 length,
|
||||
Optional<ReadonlyBytes> customization,
|
||||
Optional<ReadonlyBytes> function_name) const
|
||||
{
|
||||
bool wants_cshake = (customization.has_value() && !customization->is_empty()) || (function_name.has_value() && !function_name->is_empty());
|
||||
|
||||
if (wants_cshake) {
|
||||
// FIXME: Implement cSHAKE with non-empty N or S
|
||||
return Error::from_string_literal("cSHAKE with non-empty N or S is not supported yet (OpenSSL EVP limitation)");
|
||||
}
|
||||
|
||||
if (length % 8 != 0) {
|
||||
return Error::from_string_literal("SHAKE output length must be a multiple of 8 bits");
|
||||
}
|
||||
|
||||
size_t output_bytes = length / 8;
|
||||
auto buf = TRY(ByteBuffer::create_uninitialized(output_bytes));
|
||||
|
||||
auto ctx = TRY(OpenSSL_MD_CTX::wrap(EVP_MD_CTX_new()));
|
||||
|
||||
OPENSSL_TRY(EVP_DigestInit_ex(ctx.ptr(), m_md, nullptr));
|
||||
|
||||
OPENSSL_TRY(EVP_DigestUpdate(ctx.ptr(), data.data(), data.size()));
|
||||
|
||||
OPENSSL_TRY(EVP_DigestFinalXOF(ctx.ptr(), buf.data(), output_bytes));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
||||
39
Libraries/LibCrypto/Hash/SHAKE.h
Normal file
39
Libraries/LibCrypto/Hash/SHAKE.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2025, mikiubo <michele.uboldi@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <LibCrypto/OpenSSLForward.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
enum class SHAKEKind {
|
||||
CSHAKE128,
|
||||
CSHAKE256
|
||||
};
|
||||
|
||||
class SHAKE {
|
||||
AK_MAKE_NONCOPYABLE(SHAKE);
|
||||
|
||||
public:
|
||||
explicit SHAKE(SHAKEKind);
|
||||
|
||||
~SHAKE() = default;
|
||||
|
||||
ErrorOr<ByteBuffer> digest(
|
||||
ReadonlyBytes data,
|
||||
u32 length,
|
||||
Optional<ReadonlyBytes> customization,
|
||||
Optional<ReadonlyBytes> function_name) const;
|
||||
|
||||
private:
|
||||
EVP_MD const* m_md;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
#include <LibCrypto/Hash/PBKDF2.h>
|
||||
#include <LibCrypto/Hash/SHA1.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
#include <LibCrypto/Hash/SHAKE.h>
|
||||
#include <LibCrypto/PK/MLDSA.h>
|
||||
#include <LibCrypto/PK/MLKEM.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
|
|
@ -631,6 +632,19 @@ JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> Ed448Params::from_value(JS
|
|||
return adopt_own<AlgorithmParams>(*new Ed448Params { maybe_context });
|
||||
}
|
||||
|
||||
static inline JS::ThrowCompletionOr<Optional<ByteBuffer>> get_optional_buffer_source(JS::VM& vm, JS::Object const& object, JS::PropertyKey const& name)
|
||||
{
|
||||
if (!TRY(object.has_property(name)))
|
||||
return OptionalNone {};
|
||||
|
||||
auto value = TRY(object.get(name));
|
||||
|
||||
if (!WebIDL::is_buffer_source_type(value))
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource");
|
||||
|
||||
return TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(value.as_object()));
|
||||
}
|
||||
|
||||
Argon2Params::~Argon2Params() = default;
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> Argon2Params::from_value(JS::VM& vm, JS::Value value)
|
||||
|
|
@ -664,23 +678,34 @@ JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> Argon2Params::from_value(J
|
|||
maybe_version = TRY(WebIDL::convert_to_int<WebIDL::Octet>(vm, version_value, WebIDL::EnforceRange::Yes, WebIDL::Clamp::No));
|
||||
}
|
||||
|
||||
auto const extract_optional_buffer_source_value = [&](auto const& name) -> JS::ThrowCompletionOr<Optional<ByteBuffer>> {
|
||||
auto maybe_buffer = Optional<ByteBuffer> {};
|
||||
if (MUST(object.has_property(name))) {
|
||||
auto key_value = TRY(object.get(name));
|
||||
if (!key_value.is_object() || !(is<JS::TypedArrayBase>(key_value.as_object()) || is<JS::ArrayBuffer>(key_value.as_object()) || is<JS::DataView>(key_value.as_object())))
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource");
|
||||
maybe_buffer = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(key_value.as_object()));
|
||||
}
|
||||
return maybe_buffer;
|
||||
};
|
||||
|
||||
auto const secret_value = TRY(extract_optional_buffer_source_value("secretValue"_utf16_fly_string));
|
||||
auto const associated_data = TRY(extract_optional_buffer_source_value("associatedData"_utf16_fly_string));
|
||||
auto const secret_value = TRY(get_optional_buffer_source(vm, object, "secretValue"_utf16_fly_string));
|
||||
auto const associated_data = TRY(get_optional_buffer_source(vm, object, "associatedData"_utf16_fly_string));
|
||||
|
||||
return adopt_own<AlgorithmParams>(*new Argon2Params { nonce, parallelism, memory, passes, maybe_version, secret_value, associated_data });
|
||||
}
|
||||
|
||||
CShakeParams::~CShakeParams() = default;
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> CShakeParams::from_value(JS::VM& vm, JS::Value value)
|
||||
{
|
||||
VERIFY(value.is_object());
|
||||
auto& object = value.as_object();
|
||||
|
||||
if (!MUST(object.has_property("length"_utf16_fly_string))) {
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::MissingRequiredProperty, "length");
|
||||
}
|
||||
|
||||
auto const length_value = TRY(object.get("length"_utf16_fly_string));
|
||||
|
||||
auto const length = TRY(WebIDL::convert_to_int<WebIDL::UnsignedLong>(vm, length_value, WebIDL::EnforceRange::Yes, WebIDL::Clamp::No));
|
||||
|
||||
auto const function_name = TRY(get_optional_buffer_source(vm, object, "functionName"_utf16_fly_string));
|
||||
|
||||
auto const customization = TRY(get_optional_buffer_source(vm, object, "customization"_utf16_fly_string));
|
||||
|
||||
return adopt_own<AlgorithmParams>(*new CShakeParams { length, function_name, customization });
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#rsa-oaep-operations
|
||||
WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> RSAOAEP::encrypt(AlgorithmParams const& params, GC::Ref<CryptoKey> key, ByteBuffer const& plaintext)
|
||||
{
|
||||
|
|
@ -9634,4 +9659,47 @@ WebIDL::ExceptionOr<JS::Value> Argon2::get_key_length(AlgorithmParams const&)
|
|||
return JS::js_null();
|
||||
}
|
||||
|
||||
// https://wicg.github.io/webcrypto-modern-algos/#cshake-operations-digest
|
||||
WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> CShake::digest(AlgorithmParams const& params, ByteBuffer const& data)
|
||||
{
|
||||
auto const& normalized_algorithm = static_cast<CShakeParams const&>(params);
|
||||
|
||||
// 1. Let length be the length member of normalizedAlgorithm.
|
||||
auto const& length = normalized_algorithm.length;
|
||||
|
||||
// 2. Let functionName be the functionName member of normalizedAlgorithm if present or the empty octet string otherwise.
|
||||
auto const& function_name = normalized_algorithm.function_name;
|
||||
|
||||
// 3. Let customization be the customization member of normalizedAlgorithm if present or the empty octet string otherwise.
|
||||
auto const& customization = normalized_algorithm.customization;
|
||||
|
||||
auto const algorithm = [&]() {
|
||||
// 4. If the name member of normalizedAlgorithm is a case-sensitive string match for "cSHAKE128":
|
||||
if (normalized_algorithm.name == "cSHAKE128"sv)
|
||||
return ::Crypto::Hash::SHAKE(::Crypto::Hash::SHAKEKind::CSHAKE128);
|
||||
// 4. If the name member of normalizedAlgorithm is a case-sensitive string match for "cSHAKE256":
|
||||
if (normalized_algorithm.name == "cSHAKE256"sv)
|
||||
return ::Crypto::Hash::SHAKE(::Crypto::Hash::SHAKEKind::CSHAKE256);
|
||||
VERIFY_NOT_REACHED();
|
||||
}();
|
||||
|
||||
// 4. Let result be the result of performing the cSHAKE128/cSHAKE256 function defined in Section 3 of [NIST-SP800-185]
|
||||
// using message as the X input parameter,
|
||||
// length as the L input parameter,
|
||||
// functionName as the N input parameter,
|
||||
// and customization as the S input parameter.
|
||||
auto maybe_result = algorithm.digest(
|
||||
data,
|
||||
length,
|
||||
customization.map([](auto const& value) { return value.span(); }),
|
||||
function_name.map([](auto const& value) { return value.span(); }));
|
||||
|
||||
// 5. If performing the operation results in an error, then throw an OperationError.
|
||||
if (maybe_result.is_error())
|
||||
return WebIDL::OperationError::create(m_realm, Utf16String::formatted("Hash function failed: {}", maybe_result.error()));
|
||||
|
||||
// 6. Return result.
|
||||
return JS::ArrayBuffer::create(m_realm, maybe_result.release_value());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -739,6 +739,18 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class CShake : public AlgorithmMethods {
|
||||
public:
|
||||
virtual WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> digest(AlgorithmParams const&, ByteBuffer const&) override;
|
||||
static NonnullOwnPtr<AlgorithmMethods> create(JS::Realm& realm) { return adopt_own(*new CShake(realm)); }
|
||||
|
||||
private:
|
||||
explicit CShake(JS::Realm& realm)
|
||||
: AlgorithmMethods(realm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct EcdhKeyDeriveParams : public AlgorithmParams {
|
||||
virtual ~EcdhKeyDeriveParams() override;
|
||||
|
||||
|
|
@ -808,6 +820,25 @@ struct Argon2Params : public AlgorithmParams {
|
|||
static JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> from_value(JS::VM&, JS::Value);
|
||||
};
|
||||
|
||||
// https://wicg.github.io/webcrypto-modern-algos/#cshake-params
|
||||
struct CShakeParams : public AlgorithmParams {
|
||||
virtual ~CShakeParams() override;
|
||||
|
||||
CShakeParams(u32 length, Optional<ByteBuffer> function_name, Optional<ByteBuffer> customization)
|
||||
: length(length)
|
||||
, function_name(move(function_name))
|
||||
, customization(move(customization))
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
u32 length;
|
||||
Optional<ByteBuffer> function_name;
|
||||
Optional<ByteBuffer> customization;
|
||||
|
||||
static JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> from_value(JS::VM&, JS::Value);
|
||||
};
|
||||
|
||||
ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger);
|
||||
WebIDL::ExceptionOr<ByteBuffer> base64_url_bytes_decode(JS::Realm&, String const& base64_url_string);
|
||||
WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm&, String const& base64_url_string);
|
||||
|
|
|
|||
|
|
@ -1547,6 +1547,10 @@ SupportedAlgorithmsMap const& supported_algorithms()
|
|||
define_an_algorithm<SHA>("digest"_string, "SHA3-384"_string);
|
||||
define_an_algorithm<SHA>("digest"_string, "SHA3-512"_string);
|
||||
|
||||
// https://wicg.github.io/webcrypto-modern-algos/#cshake-registration
|
||||
define_an_algorithm<CShake, CShakeParams>("digest"_string, "cSHAKE128"_string);
|
||||
define_an_algorithm<CShake, CShakeParams>("digest"_string, "cSHAKE256"_string);
|
||||
|
||||
// https://w3c.github.io/webcrypto/#hkdf-registration
|
||||
define_an_algorithm<HKDF, HKDFParams>("deriveBits"_string, "HKDF"_string);
|
||||
define_an_algorithm<HKDF>("importKey"_string, "HKDF"_string);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 48 tests
|
||||
|
||||
48 Pass
|
||||
Pass cSHAKE128 with 0 bit output and empty source data
|
||||
Pass cSHAKE128 with 0 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE128 with 0 bit output and short source data
|
||||
Pass cSHAKE128 with 0 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE128 with 0 bit output and medium source data
|
||||
Pass cSHAKE128 with 0 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE128 with 256 bit output and empty source data
|
||||
Pass cSHAKE128 with 256 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE128 with 256 bit output and short source data
|
||||
Pass cSHAKE128 with 256 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE128 with 256 bit output and medium source data
|
||||
Pass cSHAKE128 with 256 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE128 with 384 bit output and empty source data
|
||||
Pass cSHAKE128 with 384 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE128 with 384 bit output and short source data
|
||||
Pass cSHAKE128 with 384 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE128 with 384 bit output and medium source data
|
||||
Pass cSHAKE128 with 384 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE128 with 512 bit output and empty source data
|
||||
Pass cSHAKE128 with 512 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE128 with 512 bit output and short source data
|
||||
Pass cSHAKE128 with 512 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE128 with 512 bit output and medium source data
|
||||
Pass cSHAKE128 with 512 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE256 with 0 bit output and empty source data
|
||||
Pass cSHAKE256 with 0 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE256 with 0 bit output and short source data
|
||||
Pass cSHAKE256 with 0 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE256 with 0 bit output and medium source data
|
||||
Pass cSHAKE256 with 0 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE256 with 256 bit output and empty source data
|
||||
Pass cSHAKE256 with 256 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE256 with 256 bit output and short source data
|
||||
Pass cSHAKE256 with 256 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE256 with 256 bit output and medium source data
|
||||
Pass cSHAKE256 with 256 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE256 with 384 bit output and empty source data
|
||||
Pass cSHAKE256 with 384 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE256 with 384 bit output and short source data
|
||||
Pass cSHAKE256 with 384 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE256 with 384 bit output and medium source data
|
||||
Pass cSHAKE256 with 384 bit output and medium source data and altered buffer after call
|
||||
Pass cSHAKE256 with 512 bit output and empty source data
|
||||
Pass cSHAKE256 with 512 bit output and empty source data and altered buffer after call
|
||||
Pass cSHAKE256 with 512 bit output and short source data
|
||||
Pass cSHAKE256 with 512 bit output and short source data and altered buffer after call
|
||||
Pass cSHAKE256 with 512 bit output and medium source data
|
||||
Pass cSHAKE256 with 512 bit output and medium source data and altered buffer after call
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>WebCryptoAPI: digest() cSHAKE algorithms</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
|
||||
<div id=log></div>
|
||||
<script src="../../WebCryptoAPI/digest/cshake.tentative.https.any.js"></script>
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
// META: title=WebCryptoAPI: digest() cSHAKE algorithms
|
||||
// META: timeout=long
|
||||
|
||||
var subtle = crypto.subtle; // Change to test prefixed implementations
|
||||
|
||||
var sourceData = {
|
||||
empty: new Uint8Array(0),
|
||||
short: new Uint8Array([
|
||||
21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140,
|
||||
184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178,
|
||||
227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107,
|
||||
129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7,
|
||||
131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37,
|
||||
171, 172, 161, 147,
|
||||
]),
|
||||
};
|
||||
|
||||
// Test different output lengths for cSHAKE
|
||||
var digestLengths = [0, 256, 384, 512];
|
||||
|
||||
var digestedData = {
|
||||
cSHAKE128: {
|
||||
0: {
|
||||
empty: new Uint8Array([]),
|
||||
short: new Uint8Array([]),
|
||||
medium: new Uint8Array([]),
|
||||
},
|
||||
256: {
|
||||
empty: new Uint8Array([
|
||||
127, 156, 43, 164, 232, 143, 130, 125, 97, 96, 69, 80, 118, 5, 133, 62,
|
||||
215, 59, 128, 147, 246, 239, 188, 136, 235, 26, 110, 172, 250, 102, 239,
|
||||
38,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
222, 166, 45, 115, 230, 181, 156, 247, 37, 208, 50, 13, 102, 0, 137,
|
||||
164, 71, 92, 187, 211, 184, 83, 158, 54, 105, 31, 21, 13, 71, 85, 103,
|
||||
148,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
177, 172, 213, 58, 3, 231, 106, 34, 30, 82, 234, 87, 142, 4, 47, 104,
|
||||
106, 104, 195, 209, 201, 131, 42, 177, 130, 133, 207, 79, 48, 76, 163,
|
||||
45,
|
||||
]),
|
||||
},
|
||||
384: {
|
||||
empty: new Uint8Array([
|
||||
127, 156, 43, 164, 232, 143, 130, 125, 97, 96, 69, 80, 118, 5, 133, 62,
|
||||
215, 59, 128, 147, 246, 239, 188, 136, 235, 26, 110, 172, 250, 102, 239,
|
||||
38, 60, 177, 238, 169, 136, 0, 75, 147, 16, 60, 251, 10, 238, 253, 42,
|
||||
104,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
222, 166, 45, 115, 230, 181, 156, 247, 37, 208, 50, 13, 102, 0, 137,
|
||||
164, 71, 92, 187, 211, 184, 83, 158, 54, 105, 31, 21, 13, 71, 85, 103,
|
||||
148, 240, 55, 64, 1, 183, 136, 138, 188, 54, 152, 212, 11, 137, 174, 49,
|
||||
52,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
177, 172, 213, 58, 3, 231, 106, 34, 30, 82, 234, 87, 142, 4, 47, 104,
|
||||
106, 104, 195, 209, 201, 131, 42, 177, 130, 133, 207, 79, 48, 76, 163,
|
||||
45, 63, 170, 9, 252, 130, 170, 225, 66, 211, 223, 205, 121, 5, 138, 93,
|
||||
92,
|
||||
]),
|
||||
},
|
||||
512: {
|
||||
empty: new Uint8Array([
|
||||
127, 156, 43, 164, 232, 143, 130, 125, 97, 96, 69, 80, 118, 5, 133, 62,
|
||||
215, 59, 128, 147, 246, 239, 188, 136, 235, 26, 110, 172, 250, 102, 239,
|
||||
38, 60, 177, 238, 169, 136, 0, 75, 147, 16, 60, 251, 10, 238, 253, 42,
|
||||
104, 110, 1, 250, 74, 88, 232, 163, 99, 156, 168, 161, 227, 249, 174,
|
||||
87, 226,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
222, 166, 45, 115, 230, 181, 156, 247, 37, 208, 50, 13, 102, 0, 137,
|
||||
164, 71, 92, 187, 211, 184, 83, 158, 54, 105, 31, 21, 13, 71, 85, 103,
|
||||
148, 240, 55, 64, 1, 183, 136, 138, 188, 54, 152, 212, 11, 137, 174, 49,
|
||||
52, 233, 51, 245, 26, 132, 202, 127, 218, 136, 12, 59, 253, 217, 220,
|
||||
58, 94,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
177, 172, 213, 58, 3, 231, 106, 34, 30, 82, 234, 87, 142, 4, 47, 104,
|
||||
106, 104, 195, 209, 201, 131, 42, 177, 130, 133, 207, 79, 48, 76, 163,
|
||||
45, 63, 170, 9, 252, 130, 170, 225, 66, 211, 223, 205, 121, 5, 138, 93,
|
||||
92, 60, 17, 189, 45, 17, 195, 248, 169, 51, 31, 98, 172, 221, 186, 225,
|
||||
93,
|
||||
]),
|
||||
},
|
||||
},
|
||||
cSHAKE256: {
|
||||
0: {
|
||||
empty: new Uint8Array([]),
|
||||
short: new Uint8Array([]),
|
||||
medium: new Uint8Array([]),
|
||||
},
|
||||
256: {
|
||||
empty: new Uint8Array([
|
||||
70, 185, 221, 43, 11, 168, 141, 19, 35, 59, 63, 235, 116, 62, 235, 36,
|
||||
63, 205, 82, 234, 98, 184, 27, 130, 181, 12, 39, 100, 110, 213, 118, 47,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
23, 56, 17, 63, 90, 187, 62, 229, 50, 14, 225, 138, 162, 102, 195, 97,
|
||||
122, 116, 117, 219, 216, 237, 154, 152, 89, 148, 253, 221, 97, 18, 173,
|
||||
153,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
65, 70, 193, 61, 134, 217, 188, 24, 107, 11, 48, 154, 182, 161, 36, 238,
|
||||
12, 116, 186, 38, 184, 198, 13, 204, 123, 62, 213, 5, 150, 154, 168,
|
||||
209,
|
||||
]),
|
||||
},
|
||||
384: {
|
||||
empty: new Uint8Array([
|
||||
70, 185, 221, 43, 11, 168, 141, 19, 35, 59, 63, 235, 116, 62, 235, 36,
|
||||
63, 205, 82, 234, 98, 184, 27, 130, 181, 12, 39, 100, 110, 213, 118, 47,
|
||||
215, 93, 196, 221, 216, 192, 242, 0, 203, 5, 1, 157, 103, 181, 146, 246,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
23, 56, 17, 63, 90, 187, 62, 229, 50, 14, 225, 138, 162, 102, 195, 97,
|
||||
122, 116, 117, 219, 216, 237, 154, 152, 89, 148, 253, 221, 97, 18, 173,
|
||||
153, 158, 200, 226, 235, 223, 234, 251, 150, 231, 111, 107, 179, 163,
|
||||
173, 186, 67,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
65, 70, 193, 61, 134, 217, 188, 24, 107, 11, 48, 154, 182, 161, 36, 238,
|
||||
12, 116, 186, 38, 184, 198, 13, 204, 123, 62, 213, 5, 150, 154, 168,
|
||||
209, 144, 40, 198, 49, 121, 153, 160, 133, 177, 230, 182, 167, 133, 206,
|
||||
79, 246,
|
||||
]),
|
||||
},
|
||||
512: {
|
||||
empty: new Uint8Array([
|
||||
70, 185, 221, 43, 11, 168, 141, 19, 35, 59, 63, 235, 116, 62, 235, 36,
|
||||
63, 205, 82, 234, 98, 184, 27, 130, 181, 12, 39, 100, 110, 213, 118, 47,
|
||||
215, 93, 196, 221, 216, 192, 242, 0, 203, 5, 1, 157, 103, 181, 146, 246,
|
||||
252, 130, 28, 73, 71, 154, 180, 134, 64, 41, 46, 172, 179, 183, 196,
|
||||
190,
|
||||
]),
|
||||
short: new Uint8Array([
|
||||
23, 56, 17, 63, 90, 187, 62, 229, 50, 14, 225, 138, 162, 102, 195, 97,
|
||||
122, 116, 117, 219, 216, 237, 154, 152, 89, 148, 253, 221, 97, 18, 173,
|
||||
153, 158, 200, 226, 235, 223, 234, 251, 150, 231, 111, 107, 179, 163,
|
||||
173, 186, 67, 218, 96, 240, 12, 209, 36, 150, 223, 90, 243, 226, 138,
|
||||
230, 211, 222, 66,
|
||||
]),
|
||||
medium: new Uint8Array([
|
||||
65, 70, 193, 61, 134, 217, 188, 24, 107, 11, 48, 154, 182, 161, 36, 238,
|
||||
12, 116, 186, 38, 184, 198, 13, 204, 123, 62, 213, 5, 150, 154, 168,
|
||||
209, 144, 40, 198, 49, 121, 153, 160, 133, 177, 230, 182, 167, 133, 206,
|
||||
79, 246, 50, 174, 178, 116, 147, 34, 126, 68, 35, 47, 183, 179, 149, 33,
|
||||
65, 123,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Test cSHAKE digest algorithms with variable output lengths
|
||||
Object.keys(digestedData).forEach(function (alg) {
|
||||
digestLengths.forEach(function (length) {
|
||||
Object.keys(sourceData).forEach(function (size) {
|
||||
promise_test(function (test) {
|
||||
return crypto.subtle
|
||||
.digest({ name: alg, length: length }, sourceData[size])
|
||||
.then(function (result) {
|
||||
assert_true(
|
||||
equalBuffers(result, digestedData[alg][length][size]),
|
||||
'digest matches expected'
|
||||
);
|
||||
});
|
||||
}, alg + ' with ' + length + ' bit output and ' + size + ' source data');
|
||||
|
||||
promise_test(function (test) {
|
||||
var buffer = new Uint8Array(sourceData[size]);
|
||||
return crypto.subtle
|
||||
.digest({ name: alg, length: length }, buffer)
|
||||
.then(function (result) {
|
||||
// Alter the buffer after calling digest
|
||||
if (buffer.length > 0) {
|
||||
buffer[0] = ~buffer[0];
|
||||
}
|
||||
assert_true(
|
||||
equalBuffers(result, digestedData[alg][length][size]),
|
||||
'digest matches expected'
|
||||
);
|
||||
});
|
||||
}, alg +
|
||||
' with ' +
|
||||
length +
|
||||
' bit output and ' +
|
||||
size +
|
||||
' source data and altered buffer after call');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function equalBuffers(a, b) {
|
||||
if (a.byteLength !== b.byteLength) {
|
||||
return false;
|
||||
}
|
||||
var aBytes = new Uint8Array(a);
|
||||
var bBytes = new Uint8Array(b);
|
||||
for (var i = 0; i < a.byteLength; i++) {
|
||||
if (aBytes[i] !== bBytes[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in a new issue