From f3ffdbb0cde9a11259ca64b47410824820f941e4 Mon Sep 17 00:00:00 2001 From: Amos Treiber Date: Mon, 9 Oct 2023 14:06:01 +0200 Subject: [PATCH] Support eFrodoKEM in TLS 1.3 (also as hybrid PQ/T) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: René Meusel --- src/lib/tls/tls13_pqc/hybrid_public_key.cpp | 36 ++++++++++ src/lib/tls/tls_algos.cpp | 76 ++++++++++++++++++++- src/lib/tls/tls_algos.h | 46 +++++++++++-- src/lib/tls/tls_callbacks.cpp | 16 +++++ src/scripts/test_cli.py | 14 ++++ 5 files changed, 183 insertions(+), 5 deletions(-) diff --git a/src/lib/tls/tls13_pqc/hybrid_public_key.cpp b/src/lib/tls/tls13_pqc/hybrid_public_key.cpp index e9e46b1c3bf..dc503c0b25a 100644 --- a/src/lib/tls/tls13_pqc/hybrid_public_key.cpp +++ b/src/lib/tls/tls13_pqc/hybrid_public_key.cpp @@ -30,15 +30,33 @@ std::vector> algorithm_specs_for_group(Group return {{"Curve25519", "Curve25519"}, {"Kyber", "Kyber-512-r3"}}; case Group_Params::HYBRID_X25519_KYBER_768_R3_OQS: return {{"Curve25519", "Curve25519"}, {"Kyber", "Kyber-768-r3"}}; + case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: + return {{"Curve25519", "Curve25519"}, {"FrodoKEM", "eFrodoKEM-640-SHAKE"}}; + case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: + return {{"Curve25519", "Curve25519"}, {"FrodoKEM", "eFrodoKEM-640-AES"}}; case Group_Params::HYBRID_SECP256R1_KYBER_512_R3_OQS: return {{"ECDH", "secp256r1"}, {"Kyber", "Kyber-512-r3"}}; case Group_Params::HYBRID_SECP256R1_KYBER_768_R3_OQS: return {{"ECDH", "secp256r1"}, {"Kyber", "Kyber-768-r3"}}; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: + return {{"ECDH", "secp256r1"}, {"FrodoKEM", "eFrodoKEM-640-SHAKE"}}; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: + return {{"ECDH", "secp256r1"}, {"FrodoKEM", "eFrodoKEM-640-AES"}}; + case Group_Params::HYBRID_SECP384R1_KYBER_768_R3_OQS: return {{"ECDH", "secp384r1"}, {"Kyber", "Kyber-768-r3"}}; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: + return {{"ECDH", "secp384r1"}, {"FrodoKEM", "eFrodoKEM-976-SHAKE"}}; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: + return {{"ECDH", "secp384r1"}, {"FrodoKEM", "eFrodoKEM-976-AES"}}; + case Group_Params::HYBRID_SECP521R1_KYBER_1024_R3_OQS: return {{"ECDH", "secp521r1"}, {"Kyber", "Kyber-1024-r3"}}; + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: + return {{"ECDH", "secp521r1"}, {"FrodoKEM", "eFrodoKEM-1344-SHAKE"}}; + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: + return {{"ECDH", "secp521r1"}, {"FrodoKEM", "eFrodoKEM-1344-AES"}}; default: return {}; @@ -79,13 +97,31 @@ std::vector public_value_lengths_for_group(Group_Params group) { return {32, 800}; case Group_Params::HYBRID_X25519_KYBER_768_R3_OQS: return {32, 1184}; + case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: + return {32, 9616}; + case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: + return {32, 9616}; case Group_Params::HYBRID_SECP256R1_KYBER_512_R3_OQS: return {32, 800}; case Group_Params::HYBRID_SECP256R1_KYBER_768_R3_OQS: return {32, 1184}; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: + return {32, 9616}; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: + return {32, 9616}; + case Group_Params::HYBRID_SECP384R1_KYBER_768_R3_OQS: return {48, 1184}; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: + return {48, 15632}; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: + return {48, 15632}; + + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: + return {66, 21520}; + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: + return {66, 21520}; case Group_Params::HYBRID_SECP521R1_KYBER_1024_R3_OQS: return {66, 1568}; diff --git a/src/lib/tls/tls_algos.cpp b/src/lib/tls/tls_algos.cpp index 41958402c0a..8623fe35777 100644 --- a/src/lib/tls/tls_algos.cpp +++ b/src/lib/tls/tls_algos.cpp @@ -183,16 +183,40 @@ std::optional Group_Params::from_string(std::string_view group_nam return Group_Params::KYBER_1024_R3_OQS; } + if(group_name == "eFrodoKEM-640-SHAKE") { + return Group_Params::eFRODOKEM_640_SHAKE_OQS; + } + if(group_name == "eFrodoKEM-976-SHAKE") { + return Group_Params::eFRODOKEM_976_SHAKE_OQS; + } + if(group_name == "eFrodoKEM-1344-SHAKE") { + return Group_Params::eFRODOKEM_1344_SHAKE_OQS; + } + if(group_name == "eFrodoKEM-640-AES") { + return Group_Params::eFRODOKEM_640_AES_OQS; + } + if(group_name == "eFrodoKEM-976-AES") { + return Group_Params::eFRODOKEM_976_AES_OQS; + } + if(group_name == "eFrodoKEM-1344-AES") { + return Group_Params::eFRODOKEM_1344_AES_OQS; + } + if(group_name == "x25519/Kyber-512-r3/cloudflare") { return Group_Params::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE; } - if(group_name == "x25519/Kyber-512-r3") { return Group_Params::HYBRID_X25519_KYBER_512_R3_OQS; } if(group_name == "x25519/Kyber-768-r3") { return Group_Params::HYBRID_X25519_KYBER_768_R3_OQS; } + if(group_name == "x25519/eFrodoKEM-640-SHAKE") { + return Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS; + } + if(group_name == "x25519/eFrodoKEM-640-AES") { + return Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS; + } if(group_name == "secp256r1/Kyber-512-r3") { return Group_Params::HYBRID_SECP256R1_KYBER_512_R3_OQS; @@ -200,12 +224,32 @@ std::optional Group_Params::from_string(std::string_view group_nam if(group_name == "secp256r1/Kyber-768-r3") { return Group_Params::HYBRID_SECP256R1_KYBER_768_R3_OQS; } + if(group_name == "secp256r1/eFrodoKEM-640-SHAKE") { + return Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS; + } + if(group_name == "secp256r1/eFrodoKEM-640-AES") { + return Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS; + } + if(group_name == "secp384r1/Kyber-768-r3") { return Group_Params::HYBRID_SECP384R1_KYBER_768_R3_OQS; } + if(group_name == "secp384r1/eFrodoKEM-976-SHAKE") { + return Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS; + } + if(group_name == "secp384r1/eFrodoKEM-976-AES") { + return Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS; + } + if(group_name == "secp521r1/Kyber-1024-r3") { return Group_Params::HYBRID_SECP521R1_KYBER_1024_R3_OQS; } + if(group_name == "secp521r1/eFrodoKEM-1344-SHAKE") { + return Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS; + } + if(group_name == "secp521r1/eFrodoKEM-1344-AES") { + return Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS; + } return std::nullopt; } @@ -245,6 +289,36 @@ std::optional Group_Params::to_string() const { case Group_Params::KYBER_1024_R3_OQS: return "Kyber-1024-r3"; + case Group_Params::eFRODOKEM_640_SHAKE_OQS: + return "eFrodoKEM-640-SHAKE"; + case Group_Params::eFRODOKEM_976_SHAKE_OQS: + return "eFrodoKEM-976-SHAKE"; + case Group_Params::eFRODOKEM_1344_SHAKE_OQS: + return "eFrodoKEM-1344-SHAKE"; + case Group_Params::eFRODOKEM_640_AES_OQS: + return "eFrodoKEM-640-AES"; + case Group_Params::eFRODOKEM_976_AES_OQS: + return "eFrodoKEM-976-AES"; + case Group_Params::eFRODOKEM_1344_AES_OQS: + return "eFrodoKEM-1344-AES"; + + case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: + return "x25519/eFrodoKEM-640-SHAKE"; + case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: + return "x25519/eFrodoKEM-640-AES"; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: + return "secp256r1/eFrodoKEM-640-SHAKE"; + case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: + return "secp256r1/eFrodoKEM-640-AES"; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: + return "secp384r1/eFrodoKEM-976-SHAKE"; + case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: + return "secp384r1/eFrodoKEM-976-AES"; + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: + return "secp521r1/eFrodoKEM-1344-SHAKE"; + case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: + return "secp521r1/eFrodoKEM-1344-AES"; + case Group_Params::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE: return "x25519/Kyber-512-r3/cloudflare"; diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h index a13bf9bca08..3d52f420f66 100644 --- a/src/lib/tls/tls_algos.h +++ b/src/lib/tls/tls_algos.h @@ -103,6 +103,13 @@ enum class Group_Params_Code : uint16_t { KYBER_768_R3_OQS = 0x023C, KYBER_1024_R3_OQS = 0x023D, + eFRODOKEM_640_SHAKE_OQS = 0x0201, + eFRODOKEM_976_SHAKE_OQS = 0x0203, + eFRODOKEM_1344_SHAKE_OQS = 0x0205, + eFRODOKEM_640_AES_OQS = 0x0200, + eFRODOKEM_976_AES_OQS = 0x0202, + eFRODOKEM_1344_AES_OQS = 0x0204, + // Cloudflare code points for hybrid PQC // https://blog.cloudflare.com/post-quantum-for-all/ HYBRID_X25519_KYBER_512_R3_CLOUDFLARE = 0xFE30, @@ -117,8 +124,22 @@ enum class Group_Params_Code : uint16_t { HYBRID_SECP256R1_KYBER_512_R3_OQS = 0x2F3A, HYBRID_SECP256R1_KYBER_768_R3_OQS = 0x639A, + HYBRID_SECP384R1_KYBER_768_R3_OQS = 0x2F3C, + HYBRID_SECP521R1_KYBER_1024_R3_OQS = 0x2F3D, + + HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS = 0x2F81, + HYBRID_X25519_eFRODOKEM_640_AES_OQS = 0x2F80, + + HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS = 0x2F01, + HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS = 0x2F00, + + HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS = 0x2F03, + HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS = 0x2F02, + + HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS = 0x2F05, + HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS = 0x2F04, }; class BOTAN_PUBLIC_API(3, 2) Group_Params final { @@ -170,21 +191,38 @@ class BOTAN_PUBLIC_API(3, 2) Group_Params final { m_code == Group_Params_Code::KYBER_1024_R3_OQS; } + constexpr bool is_pure_frodokem() const { + return m_code == Group_Params_Code::eFRODOKEM_640_SHAKE_OQS || + m_code == Group_Params_Code::eFRODOKEM_976_SHAKE_OQS || + m_code == Group_Params_Code::eFRODOKEM_1344_SHAKE_OQS || + m_code == Group_Params_Code::eFRODOKEM_640_AES_OQS || + m_code == Group_Params_Code::eFRODOKEM_976_AES_OQS || + m_code == Group_Params_Code::eFRODOKEM_1344_AES_OQS; + } + constexpr bool is_pure_ecc_group() const { return is_x25519() || is_ecdh_named_curve(); } - constexpr bool is_post_quantum() const { return is_pure_kyber() || is_pqc_hybrid(); } + constexpr bool is_post_quantum() const { return is_pure_kyber() || is_pure_frodokem() || is_pqc_hybrid(); } constexpr bool is_pqc_hybrid() const { - return m_code == Group_Params::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE || + return m_code == Group_Params_Code::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE || m_code == Group_Params_Code::HYBRID_X25519_KYBER_512_R3_OQS || m_code == Group_Params_Code::HYBRID_X25519_KYBER_768_R3_OQS || + m_code == Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS || + m_code == Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_AES_OQS || m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_512_R3_OQS || m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_768_R3_OQS || + m_code == Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS || + m_code == Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS || m_code == Group_Params_Code::HYBRID_SECP384R1_KYBER_768_R3_OQS || - m_code == Group_Params_Code::HYBRID_SECP521R1_KYBER_1024_R3_OQS; + m_code == Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS || + m_code == Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS || + m_code == Group_Params_Code::HYBRID_SECP521R1_KYBER_1024_R3_OQS || + m_code == Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS || + m_code == Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS; } - constexpr bool is_kem() const { return is_pure_kyber() || is_pqc_hybrid(); } + constexpr bool is_kem() const { return is_pure_kyber() || is_pure_frodokem() || is_pqc_hybrid(); } // Returns std::nullopt if the param has no known name std::optional to_string() const; diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp index b6c4769e82d..0f89e70894d 100644 --- a/src/lib/tls/tls_callbacks.cpp +++ b/src/lib/tls/tls_callbacks.cpp @@ -30,6 +30,10 @@ #include #endif +#if defined(BOTAN_HAS_FRODOKEM) + #include +#endif + #if defined(BOTAN_HAS_TLS_13_PQC) #include #endif @@ -157,6 +161,12 @@ std::unique_ptr TLS::Callbacks::tls_kem_generate_key(TLS::Group_Par } #endif +#if defined(BOTAN_HAS_FRODOKEM) + if(group.is_pure_frodokem()) { + return std::make_unique(rng, FrodoKEMMode(group.to_string().value())); + } +#endif + #if defined(BOTAN_HAS_TLS_13_PQC) if(group.is_pqc_hybrid()) { return Hybrid_KEM_PrivateKey::generate_from_group(group, rng); @@ -185,6 +195,12 @@ KEM_Encapsulation TLS::Callbacks::tls_kem_encapsulate(TLS::Group_Params group, } #endif +#if defined(BOTAN_HAS_FRODOKEM) + if(group.is_pure_frodokem()) { + return std::make_unique(encoded_public_key, FrodoKEMMode(group.to_string().value())); + } +#endif + throw TLS_Exception(Alert::IllegalParameter, "KEM is not supported"); }(); diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index 62e0641deeb..0f807ccfebe 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -1214,6 +1214,20 @@ def get_oqs_ports(): TestConfig("test.openquantumsafe.org", "Kyber-512-r3", port=oqsp['kyber512'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "Kyber-768-r3", port=oqsp['kyber768'], ca=oqs_test_ca), TestConfig("test.openquantumsafe.org", "Kyber-1024-r3", port=oqsp['kyber1024'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-SHAKE", port=oqsp['frodo640shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-976-SHAKE", port=oqsp['frodo976shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-1344-SHAKE", port=oqsp['frodo1344shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-640-AES", port=oqsp['frodo640aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-976-AES", port=oqsp['frodo976aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "eFrodoKEM-1344-AES", port=oqsp['frodo1344aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "x25519/eFrodoKEM-640-SHAKE", port=oqsp['x25519_frodo640shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "x25519/eFrodoKEM-640-AES", port=oqsp['x25519_frodo640aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp256r1/eFrodoKEM-640-SHAKE", port=oqsp['p256_frodo640shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp256r1/eFrodoKEM-640-AES", port=oqsp['p256_frodo640aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp384r1/eFrodoKEM-976-SHAKE", port=oqsp['p384_frodo976shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp384r1/eFrodoKEM-976-AES", port=oqsp['p384_frodo976aes'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp521r1/eFrodoKEM-1344-SHAKE", port=oqsp['p521_frodo1344shake'], ca=oqs_test_ca), + TestConfig("test.openquantumsafe.org", "secp521r1/eFrodoKEM-1344-AES", port=oqsp['p521_frodo1344aes'], ca=oqs_test_ca), ] else: logging.info("failed to pull OQS port assignment, skipping OQS...")