From e09c8dd51254abaa50c9bba8b428fd4ffef5f0bd Mon Sep 17 00:00:00 2001 From: rcombs Date: Wed, 22 May 2024 17:01:24 -0700 Subject: [PATCH 1/7] Support custom signing routines in SignaturePrivateKey This enables support for signing with non-extractable private keys (e.g. TPM-bound). --- include/mls/crypto.h | 9 ++++++++- src/crypto.cpp | 24 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/mls/crypto.h b/include/mls/crypto.h index 16aefaab..d266f9b9 100644 --- a/include/mls/crypto.h +++ b/include/mls/crypto.h @@ -240,11 +240,14 @@ struct PublicJWK struct SignaturePrivateKey { + using SignerFunc = std::function&)>; + static SignaturePrivateKey generate(CipherSuite suite); static SignaturePrivateKey parse(CipherSuite suite, const bytes& data); static SignaturePrivateKey derive(CipherSuite suite, const bytes& secret); static SignaturePrivateKey from_jwk(CipherSuite suite, const std::string& json_str); + static SignaturePrivateKey from_func(SignerFunc func, bytes pub_data); SignaturePrivateKey() = default; @@ -261,7 +264,11 @@ struct SignaturePrivateKey TLS_SERIALIZABLE(data) private: - SignaturePrivateKey(bytes priv_data, bytes pub_data); + SignerFunc _sign_func; + + SignaturePrivateKey(bytes priv_data, + bytes pub_data, + SignerFunc func = SignerFunc()); }; } // namespace MLS_NAMESPACE diff --git a/src/crypto.cpp b/src/crypto.cpp index 8d859c0e..fcaf471a 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -461,19 +461,31 @@ SignaturePrivateKey::sign(const CipherSuite& suite, { auto label_plus = mls_1_0_plus(label); const auto content = tls::marshal(SignContent{ label_plus, message }); + + if (_sign_func) { + return _sign_func(content); + } + const auto priv = suite.sig().deserialize_private(data); return suite.sig().sign(content, *priv); } -SignaturePrivateKey::SignaturePrivateKey(bytes priv_data, bytes pub_data) +SignaturePrivateKey::SignaturePrivateKey(bytes priv_data, + bytes pub_data, + SignerFunc func) : data(std::move(priv_data)) , public_key{ std::move(pub_data) } + , _sign_func{ std::move(func) } { } void SignaturePrivateKey::set_public_key(CipherSuite suite) { + if (_sign_func) { + throw std::runtime_error("not implemented"); + } + const auto priv = suite.sig().deserialize_private(data); auto pub = priv->public_key(); public_key.data = suite.sig().serialize(*pub); @@ -489,9 +501,19 @@ SignaturePrivateKey::from_jwk(CipherSuite suite, const std::string& json_str) return { priv_data, pub_data }; } +SignaturePrivateKey +SignaturePrivateKey::from_func(SignerFunc func, bytes pub_data) +{ + return { bytes(), std::move(pub_data), std::move(func) }; +} + std::string SignaturePrivateKey::to_jwk(CipherSuite suite) const { + if (_sign_func) { + throw std::runtime_error("not implemented"); + } + const auto priv = suite.sig().deserialize_private(data); return suite.sig().export_jwk_private(*priv); } From 58dc8c7a1066d7608fa5ebbb0716d248d9029235 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 12:45:18 -0400 Subject: [PATCH 2/7] Add tests for custom signing functions - Add test wrapping an HPKE-layer private key with SignerFunc - Add macOS Keychain test using Security.framework (conditional) - Link Security.framework on macOS for Keychain tests Co-Authored-By: Claude Opus 4.5 --- test/CMakeLists.txt | 10 +++ test/crypto.cpp | 156 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 06ffbd4c..094b7b20 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,5 +8,15 @@ add_dependencies(${TEST_APP_NAME} ${LIB_NAME} bytes tls_syntax mls_vectors) target_include_directories(${TEST_APP_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/src) target_link_libraries(${TEST_APP_NAME} PRIVATE mls_vectors Catch2::Catch2WithMain) +# Link Security.framework on macOS for Keychain tests +if(APPLE) + find_library(SECURITY_FRAMEWORK Security) + find_library(COREFOUNDATION_FRAMEWORK CoreFoundation) + if(SECURITY_FRAMEWORK AND COREFOUNDATION_FRAMEWORK) + target_link_libraries(${TEST_APP_NAME} PRIVATE ${SECURITY_FRAMEWORK} ${COREFOUNDATION_FRAMEWORK}) + target_compile_definitions(${TEST_APP_NAME} PRIVATE HAVE_SECURITY_FRAMEWORK) + endif() +endif() + # Enable CTest catch_discover_tests(${TEST_APP_NAME}) diff --git a/test/crypto.cpp b/test/crypto.cpp index 5666a6ea..cf550db8 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -3,6 +3,11 @@ #include #include +#if defined(HAVE_SECURITY_FRAMEWORK) +#include +#include +#endif + using namespace MLS_NAMESPACE; using namespace mls_vectors; @@ -133,3 +138,154 @@ TEST_CASE("Crypto Interop") REQUIRE(tv.verify() == std::nullopt); } } + +TEST_CASE("External Signer - OpenSSL Wrapper") +{ + // This test creates a signing callback that wraps an HPKE-layer private key. + // It demonstrates how an application could wrap any external signing + // mechanism. + + for (auto suite_id : all_supported_suites) { + auto suite = CipherSuite{ suite_id }; + + // Create a key at the HPKE layer (simulating an external key store) + const auto& sig = suite.sig(); + auto backend_priv = sig.generate_key_pair(); + auto backend_pub = backend_priv->public_key(); + auto backend_pub_data = sig.serialize(*backend_pub); + + // Create an external signer that delegates to the backend key + auto external_key = SignaturePrivateKey::from_func( + [&](const std::vector& data) { + return sig.sign(data, *backend_priv); + }, + backend_pub_data); + + // Verify the external key has correct public key + REQUIRE(external_key.public_key.data == backend_pub_data); + + // Verify signing works + const auto label = "test_label"s; + auto message = from_hex("deadbeef"); + auto signature = external_key.sign(suite, label, message); + REQUIRE(external_key.public_key.verify(suite, label, message, signature)); + + // Verify to_jwk throws for external signers + REQUIRE_THROWS(external_key.to_jwk(suite)); + } +} + +#if defined(HAVE_SECURITY_FRAMEWORK) + +// Helper RAII wrapper for CFRelease +template +struct CFReleaser +{ + T ref; + explicit CFReleaser(T r) + : ref(r) + { + } + ~CFReleaser() + { + if (ref) { + CFRelease(ref); + } + } + CFReleaser(const CFReleaser&) = delete; + CFReleaser& operator=(const CFReleaser&) = delete; + operator T() const { return ref; } + T get() const { return ref; } +}; + +TEST_CASE("External Signer - macOS Keychain") +{ + // This test creates a P-256 key using macOS Security.framework and + // uses it for signing via SignaturePrivateKey::from_func(). + + auto suite = CipherSuite{ CipherSuite::ID::P256_AES128GCM_SHA256_P256 }; + + // Generate a temporary key using Security.framework + CFReleaser attributes( + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + REQUIRE(attributes.get() != nullptr); + + CFDictionarySetValue( + attributes, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom); + int keySize = 256; + CFReleaser keySizeNum( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize)); + CFDictionarySetValue(attributes, kSecAttrKeySizeInBits, keySizeNum); + + // Create a temporary key (not persisted to Keychain for test isolation) + CFErrorRef error = nullptr; + CFReleaser privateKey(SecKeyCreateRandomKey(attributes, &error)); + if (error) { + CFReleaser desc(CFErrorCopyDescription(error)); + char buf[256]; + CFStringGetCString(desc, buf, sizeof(buf), kCFStringEncodingUTF8); + CFRelease(error); + FAIL("Failed to create SecKey: " << buf); + } + REQUIRE(privateKey.get() != nullptr); + + // Get public key + CFReleaser publicKey(SecKeyCopyPublicKey(privateKey)); + REQUIRE(publicKey.get() != nullptr); + + // Export public key to get bytes for MLS + CFReleaser pubKeyData( + SecKeyCopyExternalRepresentation(publicKey, &error)); + if (error) { + CFRelease(error); + FAIL("Failed to export public key"); + } + + // The exported format is ANSI X9.63 (04 || x || y), which is what MLS expects + bytes pub_bytes(CFDataGetLength(pubKeyData)); + memcpy(pub_bytes.data(), CFDataGetBytePtr(pubKeyData), pub_bytes.size()); + + // Create external signer using SecKey + SecKeyRef privKeyRef = privateKey.get(); + auto external_key = SignaturePrivateKey::from_func( + [privKeyRef](const std::vector& data) -> bytes { + CFReleaser dataRef( + CFDataCreate(kCFAllocatorDefault, data.data(), data.size())); + + CFErrorRef signError = nullptr; + CFReleaser signature( + SecKeyCreateSignature(privKeyRef, + kSecKeyAlgorithmECDSASignatureMessageX962SHA256, + dataRef, + &signError)); + + if (signError || !signature.get()) { + if (signError) { + CFRelease(signError); + } + throw std::runtime_error("SecKey signing failed"); + } + + bytes sig(CFDataGetLength(signature)); + memcpy(sig.data(), CFDataGetBytePtr(signature), sig.size()); + return sig; + }, + pub_bytes); + + // Verify the external key has correct public key + REQUIRE(external_key.public_key.data == pub_bytes); + + // Verify signing works + const auto label = "keychain_test"s; + auto message = from_hex("cafebabe"); + auto signature = external_key.sign(suite, label, message); + REQUIRE(external_key.public_key.verify(suite, label, message, signature)); + + // Verify to_jwk throws for external signers + REQUIRE_THROWS(external_key.to_jwk(suite)); +} + +#endif // HAVE_SECURITY_FRAMEWORK From 22fe1613fdc7ec99f93d8fca6a9021c42ce811b0 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 14:08:55 -0400 Subject: [PATCH 3/7] Add throwing serialization for non-exportable keys - Replace TLS_SERIALIZABLE with custom operators that throw on non-exportable keys - Add exportable() method to check if key can be serialized - Add tests verifying serialization throws for external signers - Add tests verifying normal keys serialize/deserialize correctly Co-Authored-By: Claude Opus 4.5 --- include/mls/crypto.h | 32 +++++++++++++++++++++++++++++++- test/crypto.cpp | 14 ++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/include/mls/crypto.h b/include/mls/crypto.h index d266f9b9..9c05d0e0 100644 --- a/include/mls/crypto.h +++ b/include/mls/crypto.h @@ -261,7 +261,37 @@ struct SignaturePrivateKey void set_public_key(CipherSuite suite); std::string to_jwk(CipherSuite suite) const; - TLS_SERIALIZABLE(data) + /// Returns true if this key can be serialized/exported + bool exportable() const { return !_sign_func; } + + /// TLS serialization - throws if key is not exportable + friend tls::ostream& operator<<(tls::ostream& str, + const SignaturePrivateKey& obj) + { + if (!obj.exportable()) { + throw std::runtime_error( + "Cannot serialize non-exportable SignaturePrivateKey"); + } + return str << obj.data; + } + + friend tls::istream& operator>>(tls::istream& str, SignaturePrivateKey& obj) + { + obj._sign_func = nullptr; + return str >> obj.data; + } + + friend bool operator==(const SignaturePrivateKey& lhs, + const SignaturePrivateKey& rhs) + { + return lhs.data == rhs.data; + } + + friend bool operator!=(const SignaturePrivateKey& lhs, + const SignaturePrivateKey& rhs) + { + return !(lhs == rhs); + } private: SignerFunc _sign_func; diff --git a/test/crypto.cpp b/test/crypto.cpp index cf550db8..bd734e85 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -164,6 +164,9 @@ TEST_CASE("External Signer - OpenSSL Wrapper") // Verify the external key has correct public key REQUIRE(external_key.public_key.data == backend_pub_data); + // Verify it's not exportable + REQUIRE_FALSE(external_key.exportable()); + // Verify signing works const auto label = "test_label"s; auto message = from_hex("deadbeef"); @@ -172,6 +175,17 @@ TEST_CASE("External Signer - OpenSSL Wrapper") // Verify to_jwk throws for external signers REQUIRE_THROWS(external_key.to_jwk(suite)); + + // Verify serialization throws for non-exportable keys + REQUIRE_THROWS(tls::marshal(external_key)); + + // Verify a normal key is exportable and serializable + auto normal_key = SignaturePrivateKey::generate(suite); + REQUIRE(normal_key.exportable()); + auto serialized = tls::marshal(normal_key); + auto deserialized = tls::get(serialized); + REQUIRE(deserialized.exportable()); + REQUIRE(deserialized.data == normal_key.data); } } From ee170ade55fcac479e2ebacf259e9e23fe2f0d9b Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 14:25:34 -0400 Subject: [PATCH 4/7] Add missing #include Required for std::function used by SignerFunc. Co-Authored-By: Claude Opus 4.5 --- include/mls/crypto.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mls/crypto.h b/include/mls/crypto.h index 9c05d0e0..9eb77583 100644 --- a/include/mls/crypto.h +++ b/include/mls/crypto.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include From 44dd2c6fc0442ec438f9d530cad679b71cf88216 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 14:43:31 -0400 Subject: [PATCH 5/7] Fix test to use all_supported_cipher_suites After merging main, update test to use the renamed array. Co-Authored-By: Claude Opus 4.5 --- test/crypto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index 6a60ff83..5266c8fb 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -145,7 +145,7 @@ TEST_CASE("External Signer - OpenSSL Wrapper") // It demonstrates how an application could wrap any external signing // mechanism. - for (auto suite_id : all_supported_suites) { + for (auto suite_id : all_supported_cipher_suites) { auto suite = CipherSuite{ suite_id }; // Create a key at the HPKE layer (simulating an external key store) From d537b2d94821d1fb78a57640f08eb95268e2fbf9 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 14:46:32 -0400 Subject: [PATCH 6/7] Fix formatting issues from merge Apply clang-format to fix code style after merging main. Co-Authored-By: Claude Opus 4.5 --- lib/hpke/src/signature.cpp | 11 +- lib/hpke/src/userinfo_vc.cpp | 17 +- lib/hpke/test/common.cpp | 3 +- lib/hpke/test/hpke.cpp | 12 +- lib/hpke/test/kem.cpp | 19 ++- lib/hpke/test/signature.cpp | 230 +++++++++++++-------------- lib/mls_vectors/test/mls_vectors.cpp | 13 +- 7 files changed, 147 insertions(+), 158 deletions(-) diff --git a/lib/hpke/src/signature.cpp b/lib/hpke/src/signature.cpp index 7475802b..77822e1f 100644 --- a/lib/hpke/src/signature.cpp +++ b/lib/hpke/src/signature.cpp @@ -299,14 +299,13 @@ static const Signature& sig_from_jwk(const std::string& jwk_json) { using KeyTypeAndCurve = std::tuple; - static const auto alg_sig_map = std::map - { + static const auto alg_sig_map = std::map{ { { "EC", "P-256" }, Signature::get() }, - { { "EC", "P-384" }, Signature::get() }, - { { "EC", "P-512" }, Signature::get() }, - { { "OKP", "Ed25519" }, Signature::get() }, + { { "EC", "P-384" }, Signature::get() }, + { { "EC", "P-512" }, Signature::get() }, + { { "OKP", "Ed25519" }, Signature::get() }, #if !defined(WITH_BORINGSSL) - { { "OKP", "Ed448" }, Signature::get() }, + { { "OKP", "Ed448" }, Signature::get() }, #endif // TODO(RLB): RSA }; diff --git a/lib/hpke/src/userinfo_vc.cpp b/lib/hpke/src/userinfo_vc.cpp index 60912af4..cb5881c6 100644 --- a/lib/hpke/src/userinfo_vc.cpp +++ b/lib/hpke/src/userinfo_vc.cpp @@ -52,18 +52,17 @@ get_optional(const json& json_object, const std::string& field_name) static const Signature& signature_from_alg(const std::string& alg) { - static const auto alg_sig_map = std::map - { + static const auto alg_sig_map = std::map{ { "ES256", Signature::get() }, - { "ES384", Signature::get() }, - { "ES512", Signature::get() }, - { "Ed25519", Signature::get() }, + { "ES384", Signature::get() }, + { "ES512", Signature::get() }, + { "Ed25519", Signature::get() }, #if !defined(WITH_BORINGSSL) - { "Ed448", Signature::get() }, + { "Ed448", Signature::get() }, #endif - { "RS256", Signature::get() }, - { "RS384", Signature::get() }, - { "RS512", Signature::get() }, + { "RS256", Signature::get() }, + { "RS384", Signature::get() }, + { "RS512", Signature::get() }, }; return alg_sig_map.at(alg); diff --git a/lib/hpke/test/common.cpp b/lib/hpke/test/common.cpp index 5c7e8a5b..0c83fd20 100644 --- a/lib/hpke/test/common.cpp +++ b/lib/hpke/test/common.cpp @@ -49,8 +49,7 @@ fips_disable(AEAD::ID id) bool fips_disable(Signature::ID id) { - static const auto disabled = std::set - { + static const auto disabled = std::set{ #if !defined(WITH_BORINGSSL) Signature::ID::Ed448, #endif diff --git a/lib/hpke/test/hpke.cpp b/lib/hpke/test/hpke.cpp index d435a444..3a5c5e15 100644 --- a/lib/hpke/test/hpke.cpp +++ b/lib/hpke/test/hpke.cpp @@ -212,16 +212,16 @@ TEST_CASE("HPKE Round-Trip") { ensure_fips_if_required(); - const std::vector kems - { + const std::vector kems{ KEM::ID::DHKEM_P256_SHA256, KEM::ID::DHKEM_P384_SHA384, - KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512, + KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512, #if !defined(WITH_BORINGSSL) - KEM::ID::DHKEM_X448_SHA512, + KEM::ID::DHKEM_X448_SHA512, #endif #if defined(WITH_PQ) - KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024, - KEM::ID::MLKEM768_P256, KEM::ID::MLKEM768_X25519, KEM::ID::MLKEM1024_P384, + KEM::ID::MLKEM512, KEM::ID::MLKEM768, + KEM::ID::MLKEM1024, KEM::ID::MLKEM768_P256, + KEM::ID::MLKEM768_X25519, KEM::ID::MLKEM1024_P384, #endif }; const std::vector kdfs{ KDF::ID::HKDF_SHA256, diff --git a/lib/hpke/test/kem.cpp b/lib/hpke/test/kem.cpp index 727711d9..01b73fee 100644 --- a/lib/hpke/test/kem.cpp +++ b/lib/hpke/test/kem.cpp @@ -3,16 +3,16 @@ #include "common.h" -static const auto ids = std::vector -{ +static const auto ids = std::vector{ KEM::ID::DHKEM_P256_SHA256, KEM::ID::DHKEM_P384_SHA384, - KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512, + KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512, #if !defined(WITH_BORINGSSL) - KEM::ID::DHKEM_X448_SHA512, + KEM::ID::DHKEM_X448_SHA512, #endif #if defined(WITH_PQ) - KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024, - KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519, + KEM::ID::MLKEM512, KEM::ID::MLKEM768, + KEM::ID::MLKEM1024, KEM::ID::MLKEM768_P256, + KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519, #endif }; @@ -52,11 +52,10 @@ TEST_CASE("AuthKEM round-trip") { ensure_fips_if_required(); - static const auto no_auth = std::vector - { + static const auto no_auth = std::vector{ #if defined(WITH_PQ) - KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024, - KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519 + KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024, + KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519 #endif }; diff --git a/lib/hpke/test/signature.cpp b/lib/hpke/test/signature.cpp index ba4199e2..49f74c59 100644 --- a/lib/hpke/test/signature.cpp +++ b/lib/hpke/test/signature.cpp @@ -22,8 +22,7 @@ TEST_CASE("Signature Known-Answer") bytes signature; }; - const std::vector cases - { + const std::vector cases{ // TODO(RLB): Add ECDSA known-answer tests { // https://tools.ietf.org/html/rfc8032#section-7.1 @@ -43,31 +42,31 @@ TEST_CASE("Signature Known-Answer") "3dca179c138ac17ad9bef1177331a704"), }, #if !defined(WITH_BORINGSSL) - { - // https://tools.ietf.org/html/rfc8032#section-7.2 - Signature::ID::Ed448, - true, - from_hex("d65df341ad13e008567688baedda8e9d" - "cdc17dc024974ea5b4227b6530e339bf" - "f21f99e68ca6968f3cca6dfe0fb9f4fa" - "b4fa135d5542ea3f01"), - from_hex("df9705f58edbab802c7f8363cfe5560a" - "b1c6132c20a9f1dd163483a26f8ac53a" - "39d6808bf4a1dfbd261b099bb03b3fb5" - "0906cb28bd8a081f00"), - from_hex("bd0f6a3747cd561bdddf4640a332461a" - "4a30a12a434cd0bf40d766d9c6d458e5" - "512204a30c17d1f50b5079631f64eb31" - "12182da3005835461113718d1a5ef944"), - from_hex("554bc2480860b49eab8532d2a533b7d5" - "78ef473eeb58c98bb2d0e1ce488a98b1" - "8dfde9b9b90775e67f47d4a1c3482058" - "efc9f40d2ca033a0801b63d45b3b722e" - "f552bad3b4ccb667da350192b61c508c" - "f7b6b5adadc2c8d9a446ef003fb05cba" - "5f30e88e36ec2703b349ca229c267083" - "3900"), - }, + { + // https://tools.ietf.org/html/rfc8032#section-7.2 + Signature::ID::Ed448, + true, + from_hex("d65df341ad13e008567688baedda8e9d" + "cdc17dc024974ea5b4227b6530e339bf" + "f21f99e68ca6968f3cca6dfe0fb9f4fa" + "b4fa135d5542ea3f01"), + from_hex("df9705f58edbab802c7f8363cfe5560a" + "b1c6132c20a9f1dd163483a26f8ac53a" + "39d6808bf4a1dfbd261b099bb03b3fb5" + "0906cb28bd8a081f00"), + from_hex("bd0f6a3747cd561bdddf4640a332461a" + "4a30a12a434cd0bf40d766d9c6d458e5" + "512204a30c17d1f50b5079631f64eb31" + "12182da3005835461113718d1a5ef944"), + from_hex("554bc2480860b49eab8532d2a533b7d5" + "78ef473eeb58c98bb2d0e1ce488a98b1" + "8dfde9b9b90775e67f47d4a1c3482058" + "efc9f40d2ca033a0801b63d45b3b722e" + "f552bad3b4ccb667da350192b61c508c" + "f7b6b5adadc2c8d9a446ef003fb05cba" + "5f30e88e36ec2703b349ca229c267083" + "3900"), + }, #endif }; @@ -104,8 +103,7 @@ TEST_CASE("Signature Verify Known-Answer") bytes signature; }; - const std::vector cases - { + const std::vector cases{ { Signature::ID::P256_SHA256, from_hex( @@ -120,86 +118,84 @@ TEST_CASE("Signature Verify Known-Answer") "86f94b88c02203b6a5e140d2d13ebec80636bfb1e32d17fe3d7f2983a53104e" "101e766830453a"), }, - { - Signature::ID::P384_SHA384, - from_hex("04d5a2abcb844865a479af773f9db66f5b8994710e2617e8b3c7ab4555f02" - "3f8e71a42" - "291416cdf9ea288874c5cc9f38a49b6e7cc96a3a65f60a42a05e233af26c9" - "4e0cc23c8" - "ee60177f1e1e3b52514a8de018addcc97245c2bef6bdd9ea7149da"), - from_hex("4be880bc0ccc92f79ed58b2c78268e28610719fb654b7d8b8aceae09e9e9e" - "c3115de63" - "3d5dbeb36762a67d48b0fd1c74cd499058557638372bb5d76f88a5ea00194" - "f9c0b1578" - "a9b5833d8d001ce847d4a55212601d514d6134f581f4c9a1f7bc5564ceaf2" - "8169c7fff" - "70fbc67087da868826913dab1f1dcfdf045d027e7460b7"), - from_hex( - "3064023036da67b80ca54e25cffd8c7992d406118de661c9ff40ed0468938b04d710" - "09" - "7a3f5a947d2cb5420a5af6ca9b7a8684cb023042950fa4859def74cee5066f974b7a" - "49" - "cd43899468831970b736b7bbb95338d1dd0c9e9034c9801f414982580fe9e590"), - }, - { - Signature::ID::P521_SHA512, - from_hex("0400a659dcddfafe88ebbba8c04155870e0315794c7bd5a0c53ed9b57bcfa" - "a36d79743" - "5b40a74d62ba4104d62e166538e6f88d832aa047b6ed3cd119a477000f336" - "2df01855f" - "4e61ed4be7e81ed5f566ef6455a4fb588db6e6e44f57dc4271ac3d22cdba1" - "6d361db47" - "8fa4fb233fd71179633e722615c33cfbd1d556cc29a839121c37b982"), - from_hex("6abe2712353e03ef03571a9679a3f1e889937d5ffc0df431fab44a408ce8c" - "c37449f94" - "28aae783a2ce200bb7ed546a1a92ea3555b45552844d15d6d86b662778e33" - "124304691" - "16615523990495dd3352b374792d591384123c3c7ca81ad42b9f6e856426a" - "82dddd284" - "d2f447df243067af6fe7f73cc4a368cb7cd53240af21d6"), - from_hex("3081880242015a033045a1bf86b3e1017826dd226604d78d129dcfca84f40" - "20063beec" - "03e0b4bedbedacbf1b0d1285ddbd0c7107078ac200be9876577025ffdd898" - "e97f648f7" - "80024201afcf701a73ab224ea5a0b6399fc231da0e7f1a8649df17ef2d517" - "1fc4dc278" - "6923727c2edc4f0ad9e98825750596be312d0109d47888ab6481c688a287b" - "0aac6b0"), - }, - { - Signature::ID::Ed25519, - from_hex( - "923654bbdbacc72ab6c568208719c7cb866c3f89c366914ae90d604ef360c5c8"), - from_hex("dab12589702ff146b4e83b808da4007ff4ea4a358af2f7baa6861f08fb11e" - "d71e338b2" - "fa01c7a68f86daaed5c1f00683bd5a2e511f773ac3e664222692297d7b469" - "fcae561e6" - "1a8127bef87978449ec640883c0ba17d4f1741ed4ec94443b0fa0db1a139a" - "d219ff7a4" - "ac34ced9c7d74e4bf608a1d8f792c0bf28eedbf2536af7"), - from_hex("460396e559547d5faa532503b9a15bdd4d9b7415f3e71327adb1dd1cc21eb" - "905dd9654" - "136772745f5cc9d9ffdf6bed05b9b17491a2ae8309e847bc1c7f4d6e0c"), - }, + { + Signature::ID::P384_SHA384, + from_hex("04d5a2abcb844865a479af773f9db66f5b8994710e2617e8b3c7ab4555f02" + "3f8e71a42" + "291416cdf9ea288874c5cc9f38a49b6e7cc96a3a65f60a42a05e233af26c9" + "4e0cc23c8" + "ee60177f1e1e3b52514a8de018addcc97245c2bef6bdd9ea7149da"), + from_hex("4be880bc0ccc92f79ed58b2c78268e28610719fb654b7d8b8aceae09e9e9e" + "c3115de63" + "3d5dbeb36762a67d48b0fd1c74cd499058557638372bb5d76f88a5ea00194" + "f9c0b1578" + "a9b5833d8d001ce847d4a55212601d514d6134f581f4c9a1f7bc5564ceaf2" + "8169c7fff" + "70fbc67087da868826913dab1f1dcfdf045d027e7460b7"), + from_hex( + "3064023036da67b80ca54e25cffd8c7992d406118de661c9ff40ed0468938b04d710" + "09" + "7a3f5a947d2cb5420a5af6ca9b7a8684cb023042950fa4859def74cee5066f974b7a" + "49" + "cd43899468831970b736b7bbb95338d1dd0c9e9034c9801f414982580fe9e590"), + }, + { + Signature::ID::P521_SHA512, + from_hex("0400a659dcddfafe88ebbba8c04155870e0315794c7bd5a0c53ed9b57bcfa" + "a36d79743" + "5b40a74d62ba4104d62e166538e6f88d832aa047b6ed3cd119a477000f336" + "2df01855f" + "4e61ed4be7e81ed5f566ef6455a4fb588db6e6e44f57dc4271ac3d22cdba1" + "6d361db47" + "8fa4fb233fd71179633e722615c33cfbd1d556cc29a839121c37b982"), + from_hex("6abe2712353e03ef03571a9679a3f1e889937d5ffc0df431fab44a408ce8c" + "c37449f94" + "28aae783a2ce200bb7ed546a1a92ea3555b45552844d15d6d86b662778e33" + "124304691" + "16615523990495dd3352b374792d591384123c3c7ca81ad42b9f6e856426a" + "82dddd284" + "d2f447df243067af6fe7f73cc4a368cb7cd53240af21d6"), + from_hex("3081880242015a033045a1bf86b3e1017826dd226604d78d129dcfca84f40" + "20063beec" + "03e0b4bedbedacbf1b0d1285ddbd0c7107078ac200be9876577025ffdd898" + "e97f648f7" + "80024201afcf701a73ab224ea5a0b6399fc231da0e7f1a8649df17ef2d517" + "1fc4dc278" + "6923727c2edc4f0ad9e98825750596be312d0109d47888ab6481c688a287b" + "0aac6b0"), + }, + { + Signature::ID::Ed25519, + from_hex( + "923654bbdbacc72ab6c568208719c7cb866c3f89c366914ae90d604ef360c5c8"), + from_hex("dab12589702ff146b4e83b808da4007ff4ea4a358af2f7baa6861f08fb11e" + "d71e338b2" + "fa01c7a68f86daaed5c1f00683bd5a2e511f773ac3e664222692297d7b469" + "fcae561e6" + "1a8127bef87978449ec640883c0ba17d4f1741ed4ec94443b0fa0db1a139a" + "d219ff7a4" + "ac34ced9c7d74e4bf608a1d8f792c0bf28eedbf2536af7"), + from_hex("460396e559547d5faa532503b9a15bdd4d9b7415f3e71327adb1dd1cc21eb" + "905dd9654" + "136772745f5cc9d9ffdf6bed05b9b17491a2ae8309e847bc1c7f4d6e0c"), + }, #if !defined(WITH_BORINGSSL) - { - Signature::ID::Ed448, - from_hex( - "7d60a1da10701ca4579de441643a545e334fddf18f6159ad2e8d2d914877a82" - "ea95f0b1bdac911dfb2499d3ccf814ebe69b09f9914c6aca000"), - from_hex("074f95d4f746a270af113b5650da98dcb247ef9839e480e99961a2cc99805" - "8e2b98be3" - "f949ceb7b000973127c0f79e54644f3b750763c2e904ac2179aa0a7e03da4" - "e6d848f50" - "8323ff81e4a6d20b4eb89fed06a9117383daa50e13d25e6e1c74069102137" - "9005d140a" - "8e2157744cf7717f95a503d8e3740a081efa27146974c6"), - from_hex( - "902aa0a168a9e7a547a1736fb52b491f857fe8984b9a5a5b2ae50b3c2c3b232" - "894ae055013b256218cea79c4b4055719de3a6fbb2b0be0470062bc9e76f89e" - "4ffc4c08cbd8ce50de80bae8029b78ced07cce09bc75c9b2eedcf402ed0e74c" - "8078326f8ab69960d8062d2294ad1ff63901b00"), - }, + { + Signature::ID::Ed448, + from_hex("7d60a1da10701ca4579de441643a545e334fddf18f6159ad2e8d2d914877a82" + "ea95f0b1bdac911dfb2499d3ccf814ebe69b09f9914c6aca000"), + from_hex("074f95d4f746a270af113b5650da98dcb247ef9839e480e99961a2cc99805" + "8e2b98be3" + "f949ceb7b000973127c0f79e54644f3b750763c2e904ac2179aa0a7e03da4" + "e6d848f50" + "8323ff81e4a6d20b4eb89fed06a9117383daa50e13d25e6e1c74069102137" + "9005d140a" + "8e2157744cf7717f95a503d8e3740a081efa27146974c6"), + from_hex("902aa0a168a9e7a547a1736fb52b491f857fe8984b9a5a5b2ae50b3c2c3b232" + "894ae055013b256218cea79c4b4055719de3a6fbb2b0be0470062bc9e76f89e" + "4ffc4c08cbd8ce50de80bae8029b78ced07cce09bc75c9b2eedcf402ed0e74c" + "8078326f8ab69960d8062d2294ad1ff63901b00"), + }, #endif }; @@ -222,15 +218,14 @@ TEST_CASE("Signature Round-Trip") { ensure_fips_if_required(); - const std::vector ids - { + const std::vector ids{ Signature::ID::P256_SHA256, Signature::ID::P384_SHA384, - Signature::ID::P521_SHA512, Signature::ID::Ed25519, + Signature::ID::P521_SHA512, Signature::ID::Ed25519, #if !defined(WITH_BORINGSSL) - Signature::ID::Ed448, + Signature::ID::Ed448, #endif - Signature::ID::RSA_SHA256, Signature::ID::RSA_SHA384, - Signature::ID::RSA_SHA512, + Signature::ID::RSA_SHA256, Signature::ID::RSA_SHA384, + Signature::ID::RSA_SHA512, }; const auto data = from_hex("00010203"); @@ -273,12 +268,11 @@ TEST_CASE("Signature Round-Trip") TEST_CASE("Signature Key JWK Round-Trip") { ensure_fips_if_required(); - const std::vector ids - { + const std::vector ids{ Signature::ID::P256_SHA256, Signature::ID::P384_SHA384, - Signature::ID::P521_SHA512, Signature::ID::Ed25519, + Signature::ID::P521_SHA512, Signature::ID::Ed25519, #if !defined(WITH_BORINGSSL) - Signature::ID::Ed448, + Signature::ID::Ed448, #endif }; diff --git a/lib/mls_vectors/test/mls_vectors.cpp b/lib/mls_vectors/test/mls_vectors.cpp index df2f48ee..6be2b5b2 100644 --- a/lib/mls_vectors/test/mls_vectors.cpp +++ b/lib/mls_vectors/test/mls_vectors.cpp @@ -4,15 +4,14 @@ using namespace mls_vectors; -static const std::vector supported_suites -{ +static const std::vector supported_suites{ { MLS_NAMESPACE::CipherSuite::ID::X25519_AES128GCM_SHA256_Ed25519 }, - { MLS_NAMESPACE::CipherSuite::ID::P256_AES128GCM_SHA256_P256 }, - { MLS_NAMESPACE::CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519 }, - { MLS_NAMESPACE::CipherSuite::ID::P521_AES256GCM_SHA512_P521 }, + { MLS_NAMESPACE::CipherSuite::ID::P256_AES128GCM_SHA256_P256 }, + { MLS_NAMESPACE::CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519 }, + { MLS_NAMESPACE::CipherSuite::ID::P521_AES256GCM_SHA512_P521 }, #if !defined(WITH_BORINGSSL) - { MLS_NAMESPACE::CipherSuite::ID::X448_AES256GCM_SHA512_Ed448 }, - { MLS_NAMESPACE::CipherSuite::ID::X448_CHACHA20POLY1305_SHA512_Ed448 }, + { MLS_NAMESPACE::CipherSuite::ID::X448_AES256GCM_SHA512_Ed448 }, + { MLS_NAMESPACE::CipherSuite::ID::X448_CHACHA20POLY1305_SHA512_Ed448 }, #endif }; From 98d9f5ac98a91efd3009f2f9bc306946ddef6205 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 10 Apr 2026 14:51:44 -0400 Subject: [PATCH 7/7] Update CI to use clang-format 19 Upgrade from clang-format 16 to 19 to match modern formatting. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/main_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index e99917d8..f557573c 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -62,7 +62,7 @@ jobs: - name: Run clang-format style check for C/C++ programs uses: jidicula/clang-format-action@v4.11.0 with: - clang-format-version: 16 + clang-format-version: 19 include-regex: '^\./(src|include|test|cmd|lib)/.*\.(cpp|h)$' fallback-style: 'Mozilla'