diff --git a/scripts/build/deps/boringssl.ts b/scripts/build/deps/boringssl.ts index 0930d0ed07d..e1e12d494d7 100644 --- a/scripts/build/deps/boringssl.ts +++ b/scripts/build/deps/boringssl.ts @@ -5,7 +5,7 @@ import type { Dependency } from "../source.ts"; -const BORINGSSL_COMMIT = "4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac"; +const BORINGSSL_COMMIT = "0c5fce43b7ed5eb6001487ee48ac65766f5ddcd1"; export const boringssl: Dependency = { name: "boringssl", diff --git a/scripts/verify-baseline-static/allowlist-x64.txt b/scripts/verify-baseline-static/allowlist-x64.txt index e9c20af9521..9cd64dde57c 100644 --- a/scripts/verify-baseline-static/allowlist-x64.txt +++ b/scripts/verify-baseline-static/allowlist-x64.txt @@ -584,11 +584,12 @@ rsaz_1024_sqr_avx2 [AVX, AVX2] # ---------------------------------------------------------------------------- # BoringSSL Curve25519. -# (3 symbols) +# (4 symbols) # ---------------------------------------------------------------------------- -fiat_curve25519_adx_mul [ADX, BMI2] -fiat_curve25519_adx_square [ADX, BMI2] -x25519_scalar_mult_adx [BMI2] +fiat_curve25519_adx_mul [ADX, BMI2] +fiat_curve25519_adx_square [ADX, BMI2] +_ZN4bssl22x25519_scalar_mult_adxEPhPKhS2_ [ADX, BMI2] +_ZN4bssl29x25519_ge_scalarmult_base_adxEPA32_hPKh [ADX, BMI2] # ---------------------------------------------------------------------------- diff --git a/src/bun.js/api/crypto/CryptoHasher.zig b/src/bun.js/api/crypto/CryptoHasher.zig index fd90be83ac0..53c93f885c4 100644 --- a/src/bun.js/api/crypto/CryptoHasher.zig +++ b/src/bun.js/api/crypto/CryptoHasher.zig @@ -273,10 +273,6 @@ pub const CryptoHasher = union(enum) { return CryptoHasher.new(brk: { if (hmac_key) |*key| { const chosen_algorithm = try algorithm_name.toEnumFromMap(globalThis, "algorithm", EVP.Algorithm, EVP.Algorithm.map); - if (chosen_algorithm == .ripemd160) { - // crashes at runtime. - return globalThis.throw("ripemd160 is not supported", .{}); - } break :brk .{ .hmac = HMAC.init(chosen_algorithm, key.slice()) orelse { diff --git a/src/bun.js/api/crypto/EVP.zig b/src/bun.js/api/crypto/EVP.zig index 2d3f3b088fa..9e3de48a454 100644 --- a/src/bun.js/api/crypto/EVP.zig +++ b/src/bun.js/api/crypto/EVP.zig @@ -54,6 +54,10 @@ pub const Algorithm = enum { .sha512 => BoringSSL.EVP_sha512(), .@"sha512-224" => BoringSSL.EVP_sha512_224(), .@"sha512-256" => BoringSSL.EVP_sha512_256(), + .@"sha3-224" => BoringSSL.EVP_sha3_224(), + .@"sha3-256" => BoringSSL.EVP_sha3_256(), + .@"sha3-384" => BoringSSL.EVP_sha3_384(), + .@"sha3-512" => BoringSSL.EVP_sha3_512(), else => null, }; } diff --git a/src/bun.js/api/crypto/PBKDF2.zig b/src/bun.js/api/crypto/PBKDF2.zig index b3199307929..36b2fb9f708 100644 --- a/src/bun.js/api/crypto/PBKDF2.zig +++ b/src/bun.js/api/crypto/PBKDF2.zig @@ -163,7 +163,7 @@ pub fn fromJS(globalThis: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame, is_asy invalid: { switch (try EVP.Algorithm.map.fromJSCaseInsensitive(globalThis, arg4) orelse break :invalid) { - .shake128, .shake256, .@"sha3-224", .@"sha3-256", .@"sha3-384", .@"sha3-512" => break :invalid, + .shake128, .shake256 => break :invalid, else => |alg| break :brk alg, } } diff --git a/src/bun.js/bindings/AsymmetricKeyValue.cpp b/src/bun.js/bindings/AsymmetricKeyValue.cpp index 0e7cb6744c4..d72f9abce0b 100644 --- a/src/bun.js/bindings/AsymmetricKeyValue.cpp +++ b/src/bun.js/bindings/AsymmetricKeyValue.cpp @@ -127,6 +127,9 @@ AsymmetricKeyValue::AsymmetricKeyValue(WebCore::CryptoKey& cryptoKey) case CryptoAlgorithmIdentifier::SHA_256: case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: case CryptoAlgorithmIdentifier::HKDF: case CryptoAlgorithmIdentifier::PBKDF2: case CryptoAlgorithmIdentifier::None: diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp index 673ac650323..3125ee8411a 100644 --- a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp +++ b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp @@ -520,9 +520,12 @@ enum class CryptoAlgorithmIdentifierTag { PBKDF2 = 21, ED25519 = 22, X25519 = 23, + SHA3_256 = 24, + SHA3_384 = 25, + SHA3_512 = 26, }; -const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 22; +const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 26; static unsigned countUsages(CryptoKeyUsageBitmap usages) { @@ -2394,6 +2397,15 @@ class CloneSerializer : public CloneBase { case CryptoAlgorithmIdentifier::SHA_512: write(CryptoAlgorithmIdentifierTag::SHA_512); break; + case CryptoAlgorithmIdentifier::SHA3_256: + write(CryptoAlgorithmIdentifierTag::SHA3_256); + break; + case CryptoAlgorithmIdentifier::SHA3_384: + write(CryptoAlgorithmIdentifierTag::SHA3_384); + break; + case CryptoAlgorithmIdentifier::SHA3_512: + write(CryptoAlgorithmIdentifierTag::SHA3_512); + break; case CryptoAlgorithmIdentifier::HKDF: write(CryptoAlgorithmIdentifierTag::HKDF); break; @@ -3880,6 +3892,15 @@ class CloneDeserializer : public CloneBase { case CryptoAlgorithmIdentifierTag::SHA_512: result = CryptoAlgorithmIdentifier::SHA_512; break; + case CryptoAlgorithmIdentifierTag::SHA3_256: + result = CryptoAlgorithmIdentifier::SHA3_256; + break; + case CryptoAlgorithmIdentifierTag::SHA3_384: + result = CryptoAlgorithmIdentifier::SHA3_384; + break; + case CryptoAlgorithmIdentifierTag::SHA3_512: + result = CryptoAlgorithmIdentifier::SHA3_512; + break; case CryptoAlgorithmIdentifierTag::HKDF: result = CryptoAlgorithmIdentifier::HKDF; break; diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.cpp index f0c036ac8cd..2ffec58a851 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.cpp @@ -125,6 +125,10 @@ void CryptoAlgorithmHMAC::importKey(CryptoKeyFormat format, KeyData&& data, cons return alg.isNull() || alg == ALG384; case CryptoAlgorithmIdentifier::SHA_512: return alg.isNull() || alg == ALG512; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + return alg.isNull(); default: return false; } @@ -178,6 +182,10 @@ void CryptoAlgorithmHMAC::exportKey(CryptoKeyFormat format, Ref&& key case CryptoAlgorithmIdentifier::SHA_512: jwk.alg = String(ALG512); break; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + break; default: ASSERT_NOT_REACHED(); } diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h index 8f0ef468e8e..75567bf2fe2 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h @@ -48,6 +48,9 @@ enum class CryptoAlgorithmIdentifier : uint8_t { SHA_256, SHA_384, SHA_512, + SHA3_256, + SHA3_384, + SHA3_512, HKDF, PBKDF2, Ed25519, diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.cpp index bc159ec11cb..8a6214ee598 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.cpp @@ -137,6 +137,11 @@ void CryptoAlgorithmRSASSA_PKCS1_v1_5::importKey(CryptoKeyFormat format, KeyData case CryptoAlgorithmIdentifier::SHA_512: isMatched = key.alg.isNull() || key.alg == ALG512; break; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + isMatched = key.alg.isNull(); + break; default: break; } @@ -208,6 +213,10 @@ void CryptoAlgorithmRSASSA_PKCS1_v1_5::exportKey(CryptoKeyFormat format, Ref&& case CryptoAlgorithmIdentifier::SHA_512: jwk.alg = String(ALG512); break; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + break; default: ASSERT_NOT_REACHED(); } diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.cpp index d6e585d6832..b858e896d32 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.cpp @@ -139,6 +139,11 @@ void CryptoAlgorithmRSA_PSS::importKey(CryptoKeyFormat format, KeyData&& data, c case CryptoAlgorithmIdentifier::SHA_512: isMatched = key.alg.isNull() || key.alg == ALG512; break; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + isMatched = key.alg.isNull(); + break; default: break; } @@ -210,6 +215,10 @@ void CryptoAlgorithmRSA_PSS::exportKey(CryptoKeyFormat format, Ref&& case CryptoAlgorithmIdentifier::SHA_512: jwk.alg = String(ALG512); break; + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: + break; default: ASSERT_NOT_REACHED(); } diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistryOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistryOpenSSL.cpp index b0d12c4970b..0ae50fe162e 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistryOpenSSL.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistryOpenSSL.cpp @@ -48,6 +48,7 @@ #include "CryptoAlgorithmSHA256.h" #include "CryptoAlgorithmSHA384.h" #include "CryptoAlgorithmSHA512.h" +#include "CryptoAlgorithmSHA3.h" #include "CryptoAlgorithmX25519.h" namespace WebCore { @@ -73,6 +74,9 @@ void CryptoAlgorithmRegistry::platformRegisterAlgorithms() registerAlgorithmWithAlternativeName(); registerAlgorithmWithAlternativeName(); registerAlgorithmWithAlternativeName(); + registerAlgorithm(); + registerAlgorithm(); + registerAlgorithm(); registerAlgorithm(); registerAlgorithm(); } diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.cpp new file mode 100644 index 00000000000..5ca78400bcf --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CryptoAlgorithmSHA3.h" + +#if ENABLE(WEB_CRYPTO) + +#include "CryptoDigest.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +static void dispatchDigest(PAL::CryptoDigest::Algorithm algorithm, + Vector&& message, CryptoAlgorithm::VectorCallback&& callback, + CryptoAlgorithm::ExceptionCallback&& exceptionCallback, + ScriptExecutionContext& context, WorkQueue& workQueue) +{ + auto digest = PAL::CryptoDigest::create(algorithm); + if (!digest) { + exceptionCallback(OperationError, ""_s); + return; + } + + if (message.size() < 64) { + auto moved = WTF::move(message); + digest->addBytes(moved.begin(), moved.size()); + auto result = digest->computeHash(); + ScriptExecutionContext::postTaskTo(context.identifier(), + [callback = WTF::move(callback), result = WTF::move(result)](auto&) { + callback(result); + }); + return; + } + + workQueue.dispatch(context.globalObject(), + [digest = WTF::move(digest), message = WTF::move(message), + callback = WTF::move(callback), + contextIdentifier = context.identifier()]() mutable { + digest->addBytes(message.begin(), message.size()); + auto result = digest->computeHash(); + ScriptExecutionContext::postTaskTo(contextIdentifier, + [callback = WTF::move(callback), result = WTF::move(result)](auto&) { + callback(result); + }); + }); +} + +#define DEFINE_SHA3(ClassName, DigestAlgo) \ + Ref ClassName::create() { return adoptRef(*new ClassName); } \ + CryptoAlgorithmIdentifier ClassName::identifier() const { return s_identifier; } \ + void ClassName::digest(Vector&& message, VectorCallback&& callback, \ + ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, \ + WorkQueue& workQueue) \ + { \ + dispatchDigest(DigestAlgo, WTF::move(message), WTF::move(callback), \ + WTF::move(exceptionCallback), context, workQueue); \ + } + +DEFINE_SHA3(CryptoAlgorithmSHA3_256, PAL::CryptoDigest::Algorithm::SHA3_256) +DEFINE_SHA3(CryptoAlgorithmSHA3_384, PAL::CryptoDigest::Algorithm::SHA3_384) +DEFINE_SHA3(CryptoAlgorithmSHA3_512, PAL::CryptoDigest::Algorithm::SHA3_512) + +#undef DEFINE_SHA3 + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.h new file mode 100644 index 00000000000..549dc5f5794 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA3.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(WEB_CRYPTO) + +#include "CryptoAlgorithm.h" + +namespace WebCore { + +class CryptoAlgorithmSHA3_256 final : public CryptoAlgorithm { +public: + static constexpr ASCIILiteral s_name = "SHA3-256"_s; + static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::SHA3_256; + static Ref create(); + +private: + CryptoAlgorithmSHA3_256() = default; + CryptoAlgorithmIdentifier identifier() const final; + void digest(Vector&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; +}; + +class CryptoAlgorithmSHA3_384 final : public CryptoAlgorithm { +public: + static constexpr ASCIILiteral s_name = "SHA3-384"_s; + static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::SHA3_384; + static Ref create(); + +private: + CryptoAlgorithmSHA3_384() = default; + CryptoAlgorithmIdentifier identifier() const final; + void digest(Vector&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; +}; + +class CryptoAlgorithmSHA3_512 final : public CryptoAlgorithm { +public: + static constexpr ASCIILiteral s_name = "SHA3-512"_s; + static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::SHA3_512; + static Ref create(); + +private: + CryptoAlgorithmSHA3_512() = default; + CryptoAlgorithmIdentifier identifier() const final; + void digest(Vector&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoDigest.cpp b/src/bun.js/bindings/webcrypto/CryptoDigest.cpp index 0c8f3aa5e8f..f4ee9a71803 100644 --- a/src/bun.js/bindings/webcrypto/CryptoDigest.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoDigest.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "CryptoDigest.h" +#include #include namespace { @@ -103,6 +104,41 @@ struct CryptoDigestContextImpl : public CryptoDigestContext { SHAContext m_context; }; +struct CryptoDigestContextEVP : public CryptoDigestContext { + WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED(CryptoDigestContextEVP); + + static std::unique_ptr create(const EVP_MD* md) + { + return makeUnique(md); + } + + explicit CryptoDigestContextEVP(const EVP_MD* md) + { + EVP_MD_CTX_init(&m_context); + int rc = EVP_DigestInit_ex(&m_context, md, nullptr); + ASSERT_UNUSED(rc, rc == 1); + } + + ~CryptoDigestContextEVP() override { EVP_MD_CTX_cleanup(&m_context); } + + void addBytes(const void* input, size_t length) override + { + int rc = EVP_DigestUpdate(&m_context, input, length); + ASSERT_UNUSED(rc, rc == 1); + } + + Vector computeHash() override + { + Vector result(EVP_MD_CTX_size(&m_context)); + int rc = EVP_DigestFinal_ex(&m_context, result.begin(), nullptr); + ASSERT_UNUSED(rc, rc == 1); + return result; + } + +private: + EVP_MD_CTX m_context; +}; + CryptoDigest::CryptoDigest() { } @@ -131,6 +167,15 @@ std::unique_ptr CryptoDigest::create(CryptoDigest::Algorithm algor case CryptoDigest::Algorithm::SHA_512: digest->m_context = CryptoDigestContextImpl::create(); return digest; + case CryptoDigest::Algorithm::SHA3_256: + digest->m_context = CryptoDigestContextEVP::create(EVP_sha3_256()); + return digest; + case CryptoDigest::Algorithm::SHA3_384: + digest->m_context = CryptoDigestContextEVP::create(EVP_sha3_384()); + return digest; + case CryptoDigest::Algorithm::SHA3_512: + digest->m_context = CryptoDigestContextEVP::create(EVP_sha3_512()); + return digest; } return nullptr; diff --git a/src/bun.js/bindings/webcrypto/CryptoDigest.h b/src/bun.js/bindings/webcrypto/CryptoDigest.h index 5da849a1d33..12c3907e1a5 100644 --- a/src/bun.js/bindings/webcrypto/CryptoDigest.h +++ b/src/bun.js/bindings/webcrypto/CryptoDigest.h @@ -47,6 +47,9 @@ class CryptoDigest { SHA_256, SHA_384, SHA_512, + SHA3_256, + SHA3_384, + SHA3_512, }; PAL_EXPORT static std::unique_ptr create(Algorithm); PAL_EXPORT ~CryptoDigest(); diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.cpp index 5bde29b77e3..3ef96ae00fb 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoKeyHMAC.cpp @@ -48,6 +48,12 @@ static size_t getKeyLengthFromHash(CryptoAlgorithmIdentifier hash) case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: return 1024; + case CryptoAlgorithmIdentifier::SHA3_256: + return 1088; + case CryptoAlgorithmIdentifier::SHA3_384: + return 832; + case CryptoAlgorithmIdentifier::SHA3_512: + return 576; default: ASSERT_NOT_REACHED(); return 0; diff --git a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp index 74bc0523241..c5be62c262f 100644 --- a/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp +++ b/src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp @@ -45,6 +45,12 @@ const EVP_MD* digestAlgorithm(CryptoAlgorithmIdentifier hashFunction) return EVP_sha384(); case CryptoAlgorithmIdentifier::SHA_512: return EVP_sha512(); + case CryptoAlgorithmIdentifier::SHA3_256: + return EVP_sha3_256(); + case CryptoAlgorithmIdentifier::SHA3_384: + return EVP_sha3_384(); + case CryptoAlgorithmIdentifier::SHA3_512: + return EVP_sha3_512(); default: return nullptr; } diff --git a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp index 5f57e71a2d4..5c6f4df7d39 100644 --- a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp +++ b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp @@ -204,6 +204,9 @@ static ExceptionOr> normalizeCryptoAl case CryptoAlgorithmIdentifier::SHA_256: case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: result = makeUnique(params); break; default: @@ -381,6 +384,9 @@ static ExceptionOr> normalizeCryptoAl case CryptoAlgorithmIdentifier::SHA_256: case CryptoAlgorithmIdentifier::SHA_384: case CryptoAlgorithmIdentifier::SHA_512: + case CryptoAlgorithmIdentifier::SHA3_256: + case CryptoAlgorithmIdentifier::SHA3_384: + case CryptoAlgorithmIdentifier::SHA3_512: return Exception { NotSupportedError }; case CryptoAlgorithmIdentifier::None: return Exception { NotSupportedError }; @@ -778,6 +784,7 @@ void SubtleCrypto::digest(JSC::JSGlobalObject& state, AlgorithmIdentifier&& algo auto& vm = state.vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTF::move(algorithmIdentifier), Operations::Digest); + RETURN_IF_EXCEPTION(scope, void()); if (paramsOrException.hasException()) { promise->reject(paramsOrException.releaseException().code(), "Unrecognized algorithm name"_s); return; diff --git a/src/deps/boringssl.translated.zig b/src/deps/boringssl.translated.zig index 156f2c203a7..b7e886318d1 100644 --- a/src/deps/boringssl.translated.zig +++ b/src/deps/boringssl.translated.zig @@ -343,7 +343,7 @@ pub const struct_evp_md_pctx_ops = opaque {}; pub const struct_env_md_ctx_st = extern struct { // md_data contains the hash-specific context md_data: extern union { - data: [208]u8, + data: [240]u8, alignment: u64, }, // digest is the underlying digest function, or NULL if not set @@ -6377,7 +6377,7 @@ pub const CIPHER_R_NO_DIRECTION_SET = @as(c_int, 124); pub const CIPHER_R_INVALID_NONCE = @as(c_int, 125); pub const OPENSSL_HEADER_DIGEST_H = ""; pub const EVP_MAX_MD_SIZE = @as(c_int, 64); -pub const EVP_MAX_MD_BLOCK_SIZE = @as(c_int, 128); +pub const EVP_MAX_MD_BLOCK_SIZE = @as(c_int, 144); pub const EVP_MD_FLAG_PKEY_DIGEST = @as(c_int, 1); pub const EVP_MD_FLAG_DIGALGID_ABSENT = @as(c_int, 2); pub const EVP_MD_FLAG_XOF = @as(c_int, 4); @@ -18756,6 +18756,10 @@ pub extern fn EVP_sha384() *const EVP_MD; pub extern fn EVP_sha512() *const EVP_MD; pub extern fn EVP_sha512_224() *const EVP_MD; pub extern fn EVP_sha512_256() *const EVP_MD; +pub extern fn EVP_sha3_224() *const EVP_MD; +pub extern fn EVP_sha3_256() *const EVP_MD; +pub extern fn EVP_sha3_384() *const EVP_MD; +pub extern fn EVP_sha3_512() *const EVP_MD; pub extern fn EVP_blake2b256() *const EVP_MD; pub extern fn EVP_blake2b512() *const EVP_MD; diff --git a/test/js/bun/util/bun-cryptohasher.test.ts b/test/js/bun/util/bun-cryptohasher.test.ts index 545c3f884e6..0b69b6f15a6 100644 --- a/test/js/bun/util/bun-cryptohasher.test.ts +++ b/test/js/bun/util/bun-cryptohasher.test.ts @@ -29,6 +29,12 @@ describe("HMAC", () => { "sha224": "d34c3a2647d4f82a4e6baeaa7d94379eafd931e0c16cbc44b4ba4d1e", "sha512-224": "af398c7f21f58e1377580227a89590d3ab8be52b31182fad9ec4d667", "sha512-256": "0ed15b2750a2a7281e96af006ab79e82ed54a7a2081bdb49e70a70d8c6bfeff0", + "sha3-224": "3dd0595758af01c6a9d662326acc3bc0c7e49b94573f74f800b6c114", + "sha3-256": "5b246f6c8b41fbd23b7aa3a73c0c93c6a35d4973bc727b24ad65f538d51ff3b6", + "sha3-384": "f0af5d4479dc409e11c6e23014893c42a51fbd3435c93452f6154a87128174e2492a6b31994b1436ae681b3f1d838613", + "sha3-512": + "b15ed8373f1b493ccd417a7591745fdefbb4aa7b85c6937284de678e1a7b73b31e4da07561d358fefa30c6b1cf1a4b19a4c0d2f4f6e90ddfadc3a12367cb1a3c", + "ripemd160": "5291464ec22d15e61190b00b81b87c1a9dcb966f", }; for (let key of ["key", Buffer.from("key"), Buffer.from("key").buffer]) { test.each(Object.entries(hashes))("%s (key: " + key.constructor.name + ")", (algorithm, expected) => { @@ -58,15 +64,7 @@ describe("HMAC", () => { }); } - const unsupported = [ - ["sha3-224"], - ["sha3-256"], - ["sha3-384"], - ["sha3-512"], - ["shake128"], - ["shake256"], - ["ripemd160"], - ] as const; + const unsupported = [["shake128"], ["shake256"]] as const; test.each(unsupported)("%s is not supported", algorithm => { expect(() => new Bun.CryptoHasher(algorithm, "key")).toThrow(); expect(() => new Bun.CryptoHasher(algorithm)).not.toThrow(); diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 58ef76494d1..3e07fef8d5e 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -273,7 +273,7 @@ it("process.versions", () => { // These are the ACTUAL commits built into bun (not derived values, so // bumping a dep requires updating this test too). const expectedVersions = { - boringssl: "4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac", + boringssl: "0c5fce43b7ed5eb6001487ee48ac65766f5ddcd1", libarchive: "ded82291ab41d5e355831b96b0e1ff49e24d8939", mimalloc: "9a5e1f52cdf4662f9590b69de104a4469140796f", picohttpparser: "066d2b1e9ab820703db0837a7255d92d30f0c9f5", diff --git a/test/js/web/crypto/web-crypto-sha3.test.ts b/test/js/web/crypto/web-crypto-sha3.test.ts new file mode 100644 index 00000000000..705abbf7ba9 --- /dev/null +++ b/test/js/web/crypto/web-crypto-sha3.test.ts @@ -0,0 +1,133 @@ +import { describe, expect, it } from "bun:test"; +import { createHash, createHmac, getHashes, pbkdf2Sync } from "node:crypto"; + +const hex = (buf: ArrayBuffer) => [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, "0")).join(""); + +// NIST FIPS 202 test vectors +const vectors = [ + ["SHA3-256", "", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"], + ["SHA3-256", "abc", "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"], + ["SHA3-384", "", "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"], + [ + "SHA3-384", + "abc", + "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", + ], + [ + "SHA3-512", + "", + "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", + ], + [ + "SHA3-512", + "abc", + "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", + ], +] as const; + +describe("crypto.subtle.digest SHA-3", () => { + for (const [alg, input, expected] of vectors) { + it(`${alg}(${JSON.stringify(input)})`, async () => { + const buf = await crypto.subtle.digest(alg, new TextEncoder().encode(input)); + expect(hex(buf)).toBe(expected); + }); + } + + it("SHA3-256 large input (>64 bytes, async path)", async () => { + const input = Buffer.alloc(1_000_000, "a"); + const buf = await crypto.subtle.digest("SHA3-256", input); + expect(hex(buf)).toBe("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"); + }); + + it("rejects unknown digest", async () => { + await expect(crypto.subtle.digest("SHA3-1024" as any, new Uint8Array())).rejects.toThrow(); + }); +}); + +describe("HMAC with SHA-3", () => { + it("generateKey + sign + verify with SHA3-256", async () => { + const key = await crypto.subtle.generateKey({ name: "HMAC", hash: "SHA3-256" }, true, ["sign", "verify"]); + const data = new TextEncoder().encode("hello world"); + const sig = await crypto.subtle.sign("HMAC", key, data); + expect(sig.byteLength).toBe(32); + expect(await crypto.subtle.verify("HMAC", key, sig, data)).toBe(true); + + const tampered = new Uint8Array(sig); + tampered[0] ^= 0xff; + expect(await crypto.subtle.verify("HMAC", key, tampered, data)).toBe(false); + }); + + it("HMAC-SHA3-256 against NIST vector", async () => { + const keyBytes = new Uint8Array(32).map((_, i) => i); + const key = await crypto.subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA3-256" }, false, ["sign"]); + const msg = new TextEncoder().encode("Sample message for keylen { + const key = await crypto.subtle.generateKey({ name: "HMAC", hash: "SHA3-384" }, true, ["sign"]); + const raw = await crypto.subtle.exportKey("raw", key); + expect(raw.byteLength).toBe(104); + }); +}); + +describe("RSA with SHA-3 hash", () => { + it("RSA-PSS with SHA3-256: generate, sign, verify, JWK export", async () => { + const { publicKey, privateKey } = await crypto.subtle.generateKey( + { + name: "RSA-PSS", + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: "SHA3-256", + }, + true, + ["sign", "verify"], + ); + const data = new TextEncoder().encode("hello"); + const sig = await crypto.subtle.sign({ name: "RSA-PSS", saltLength: 32 }, privateKey, data); + expect(await crypto.subtle.verify({ name: "RSA-PSS", saltLength: 32 }, publicKey, sig, data)).toBe(true); + + const jwk = await crypto.subtle.exportKey("jwk", publicKey); + expect(jwk.kty).toBe("RSA"); + expect(jwk.alg).toBeUndefined(); + + const reimported = await crypto.subtle.importKey("jwk", jwk, { name: "RSA-PSS", hash: "SHA3-256" }, true, [ + "verify", + ]); + expect(await crypto.subtle.verify({ name: "RSA-PSS", saltLength: 32 }, reimported, sig, data)).toBe(true); + }); +}); + +describe("node:crypto SHA-3", () => { + it("createHash sha3-256", () => { + expect(createHash("sha3-256").update("abc").digest("hex")).toBe( + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + ); + }); + + it("createHash sha3-384", () => { + expect(createHash("sha3-384").update("abc").digest("hex")).toBe( + "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", + ); + }); + + it("createHmac sha3-512", () => { + expect(createHmac("sha3-512", Buffer.from("key")).update("data").digest("hex")).toBe( + "752bf49d54115aaa670ea62bdf79eb95e6df787938bec5fabdfc4745cf49f7fe11b7c2f73989ad2e568f06ced3a2d99536b05a121f43647b98ea43f818f38b33", + ); + }); + + it("getHashes includes sha3", () => { + const hashes = getHashes(); + expect(hashes).toContain("sha3-256"); + expect(hashes).toContain("sha3-384"); + expect(hashes).toContain("sha3-512"); + }); + + it("pbkdf2Sync sha3-256", () => { + expect(pbkdf2Sync("pw", "salt", 1000, 32, "sha3-256").toString("hex")).toBe( + "53b1bc246a311cbf8e2c907d96bcb209ddf95cd9f0a74fdcbab033b6ea82e30a", + ); + }); +});