From cb68be58712cc11a1b1df2852b87a55458c809e5 Mon Sep 17 00:00:00 2001 From: Rene Meusel Date: Tue, 28 Jul 2026 18:42:58 +0200 Subject: [PATCH] Support ECDSA with brainpool in TLS 1.3 ... as described in RFC 8734. Closes #3811 --- src/lib/tls/tls_signature_scheme.cpp | 64 +++++++++++++++++++++---- src/lib/tls/tls_signature_scheme.h | 5 ++ src/tests/test_tls_signature_scheme.cpp | 9 ++++ 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/lib/tls/tls_signature_scheme.cpp b/src/lib/tls/tls_signature_scheme.cpp index a1c110138c0..7cc882701de 100644 --- a/src/lib/tls/tls_signature_scheme.cpp +++ b/src/lib/tls/tls_signature_scheme.cpp @@ -39,6 +39,10 @@ const std::vector& Signature_Scheme::all_available_schemes() { RSA_PSS_SHA256, RSA_PSS_SHA512, + ECDSA_BRAINPOOL384R1_TLS13_SHA384, + ECDSA_BRAINPOOL256R1_TLS13_SHA256, + ECDSA_BRAINPOOL512R1_TLS13_SHA512, + RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, RSA_PKCS1_SHA256, @@ -84,6 +88,16 @@ Signature_Scheme Signature_Scheme::from_string(std::string_view str) { return RSA_PSS_SHA512; } + if(str == "ECDSA_BRAINPOOL256R1_TLS13_SHA256") { + return ECDSA_BRAINPOOL256R1_TLS13_SHA256; + } + if(str == "ECDSA_BRAINPOOL384R1_TLS13_SHA384") { + return ECDSA_BRAINPOOL384R1_TLS13_SHA384; + } + if(str == "ECDSA_BRAINPOOL512R1_TLS13_SHA512") { + return ECDSA_BRAINPOOL512R1_TLS13_SHA512; + } + // Parse signature schemes passed as hexadecimal code points (e.g. "0x081A") if(str.size() == 6 && str.starts_with("0x")) { try { @@ -112,6 +126,9 @@ bool Signature_Scheme::is_available() const noexcept { case RSA_PKCS1_SHA384: case RSA_PKCS1_SHA512: case RSA_PKCS1_SHA256: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: case ECDSA_SHA384: case ECDSA_SHA512: case ECDSA_SHA256: @@ -145,6 +162,13 @@ std::string Signature_Scheme::to_string() const { case ECDSA_SHA512: return "ECDSA_SHA512"; + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: + return "ECDSA_BRAINPOOL256R1_TLS13_SHA256"; + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: + return "ECDSA_BRAINPOOL384R1_TLS13_SHA384"; + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: + return "ECDSA_BRAINPOOL512R1_TLS13_SHA512"; + case RSA_PSS_SHA256: return "RSA_PSS_SHA256"; case RSA_PSS_SHA384: @@ -171,16 +195,19 @@ std::string Signature_Scheme::hash_function_name() const { case ECDSA_SHA256: case RSA_PKCS1_SHA256: case RSA_PSS_SHA256: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: return "SHA-256"; case ECDSA_SHA384: case RSA_PKCS1_SHA384: case RSA_PSS_SHA384: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: return "SHA-384"; case ECDSA_SHA512: case RSA_PKCS1_SHA512: case RSA_PSS_SHA512: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: return "SHA-512"; case EDDSA_25519: @@ -206,10 +233,13 @@ std::string Signature_Scheme::padding_string() const { case ECDSA_SHA1: return "SHA-1"; case ECDSA_SHA256: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: return "SHA-256"; case ECDSA_SHA384: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: return "SHA-384"; case ECDSA_SHA512: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: return "SHA-512"; case RSA_PSS_SHA256: @@ -243,6 +273,9 @@ std::string Signature_Scheme::algorithm_name() const { case ECDSA_SHA256: case ECDSA_SHA384: case ECDSA_SHA512: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: return "ECDSA"; case EDDSA_25519: @@ -275,6 +308,13 @@ AlgorithmIdentifier Signature_Scheme::key_algorithm_identifier() const { case ECDSA_SHA512: return {"ECDSA", der_encode_oid("secp521r1")}; + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: + return {"ECDSA", der_encode_oid("brainpool256r1")}; + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: + return {"ECDSA", der_encode_oid("brainpool384r1")}; + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: + return {"ECDSA", der_encode_oid("brainpool512r1")}; + case EDDSA_25519: return {"Ed25519", AlgorithmIdentifier::USE_EMPTY_PARAM}; case EDDSA_448: @@ -308,10 +348,13 @@ AlgorithmIdentifier Signature_Scheme::algorithm_identifier() const { case ECDSA_SHA1: return AlgorithmIdentifier(OID::from_string("ECDSA/SHA-1"), AlgorithmIdentifier::USE_EMPTY_PARAM); case ECDSA_SHA256: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: return AlgorithmIdentifier(OID::from_string("ECDSA/SHA-256"), AlgorithmIdentifier::USE_EMPTY_PARAM); case ECDSA_SHA384: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: return AlgorithmIdentifier(OID::from_string("ECDSA/SHA-384"), AlgorithmIdentifier::USE_EMPTY_PARAM); case ECDSA_SHA512: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: return AlgorithmIdentifier(OID::from_string("ECDSA/SHA-512"), AlgorithmIdentifier::USE_EMPTY_PARAM); case RSA_PSS_SHA256: @@ -344,6 +387,9 @@ std::optional Signature_Scheme::format() const noexcept { case ECDSA_SHA256: case ECDSA_SHA384: case ECDSA_SHA512: + case ECDSA_BRAINPOOL256R1_TLS13_SHA256: + case ECDSA_BRAINPOOL384R1_TLS13_SHA384: + case ECDSA_BRAINPOOL512R1_TLS13_SHA512: return Signature_Format::DerSequence; default: @@ -385,16 +431,18 @@ bool Signature_Scheme::is_suitable_for(const Private_Key& private_key) const { return false; } - if(m_code == ECDSA_SHA256 && !(keylen >= 250 && keylen <= 350)) { - return false; - } + if(algorithm_name() == "ECDSA") { + if(hash_function_name() == "SHA-256" && !(keylen >= 250 && keylen <= 350)) { + return false; + } - if(m_code == ECDSA_SHA384 && !(keylen >= 350 && keylen <= 450)) { - return false; - } + if(hash_function_name() == "SHA-384" && !(keylen >= 350 && keylen <= 450)) { + return false; + } - if(m_code == ECDSA_SHA512 && !(keylen >= 450 && keylen <= 550)) { - return false; + if(hash_function_name() == "SHA-512" && !(keylen >= 450 && keylen <= 550)) { + return false; + } } return true; diff --git a/src/lib/tls/tls_signature_scheme.h b/src/lib/tls/tls_signature_scheme.h index 0e13fb6ec9d..eaf8a4078ec 100644 --- a/src/lib/tls/tls_signature_scheme.h +++ b/src/lib/tls/tls_signature_scheme.h @@ -52,6 +52,11 @@ class BOTAN_PUBLIC_API(3, 0) Signature_Scheme final { RSA_PSS_SHA384 = 0x0805, RSA_PSS_SHA512 = 0x0806, + // RFC 8734 + ECDSA_BRAINPOOL256R1_TLS13_SHA256 = 0x081A, + ECDSA_BRAINPOOL384R1_TLS13_SHA384 = 0x081B, + ECDSA_BRAINPOOL512R1_TLS13_SHA512 = 0x081C, + EDDSA_25519 = 0x0807, EDDSA_448 = 0x0808, }; diff --git a/src/tests/test_tls_signature_scheme.cpp b/src/tests/test_tls_signature_scheme.cpp index 9ca42a7b67c..7e5dd8d67fa 100644 --- a/src/tests/test_tls_signature_scheme.cpp +++ b/src/tests/test_tls_signature_scheme.cpp @@ -70,6 +70,15 @@ std::vector test_signature_scheme() { result.test_u16_eq("RSA_PSS_SHA256", Sig::from_string("RSA_PSS_SHA256").wire_code(), Sig::RSA_PSS_SHA256); result.test_u16_eq("RSA_PSS_SHA384", Sig::from_string("RSA_PSS_SHA384").wire_code(), Sig::RSA_PSS_SHA384); result.test_u16_eq("RSA_PSS_SHA512", Sig::from_string("RSA_PSS_SHA512").wire_code(), Sig::RSA_PSS_SHA512); + result.test_u16_eq("ECDSA_BRAINPOOL256R1_TLS13_SHA256", + Sig::from_string("ECDSA_BRAINPOOL256R1_TLS13_SHA256").wire_code(), + Sig::ECDSA_BRAINPOOL256R1_TLS13_SHA256); + result.test_u16_eq("ECDSA_BRAINPOOL384R1_TLS13_SHA384", + Sig::from_string("ECDSA_BRAINPOOL384R1_TLS13_SHA384").wire_code(), + Sig::ECDSA_BRAINPOOL384R1_TLS13_SHA384); + result.test_u16_eq("ECDSA_BRAINPOOL512R1_TLS13_SHA512", + Sig::from_string("ECDSA_BRAINPOOL512R1_TLS13_SHA512").wire_code(), + Sig::ECDSA_BRAINPOOL512R1_TLS13_SHA512); result.test_u16_eq("custom code point", Sig::from_string("0xFE42").wire_code(), Sig(0xFE42).wire_code());