Skip to content

Commit 61e4d76

Browse files
authored
Add support for hybrid PQ cipher suites (#456)
1 parent ed47ac6 commit 61e4d76

File tree

8 files changed

+85
-45
lines changed

8 files changed

+85
-45
lines changed

include/mls/crypto.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct CipherSuite
5252
P521_AES256GCM_SHA512_P521 = 0x0005,
5353
X448_CHACHA20POLY1305_SHA512_Ed448 = 0x0006,
5454
P384_AES256GCM_SHA384_P384 = 0x0007,
55+
MLKEM768X25519_AES256GCM_SHA384_Ed25519 = 0x0008,
56+
MLKEM768P256_AES256GCM_SHA384_P256 = 0x0009,
57+
MLKEM1024P384_AES256GCM_SHA384_P384 = 0x000a,
5558

5659
// GREASE values, included here mainly so that debugger output looks nice
5760
GREASE_0 = 0x0A0A,
@@ -133,12 +136,23 @@ struct CipherSuite
133136
static const bytes& reference_label();
134137
};
135138

136-
#if WITH_BORINGSSL
137-
extern const std::array<CipherSuite::ID, 5> all_supported_suites;
139+
#if defined(WITH_BORINGSSL)
140+
static constexpr size_t n_supported_x448_suites = 0;
138141
#else
139-
extern const std::array<CipherSuite::ID, 7> all_supported_suites;
142+
static constexpr size_t n_supported_x448_suites = 2;
140143
#endif
141144

145+
#if defined(WITH_PQ)
146+
static constexpr size_t n_supported_pq_suites = 3;
147+
#else
148+
static constexpr size_t n_supported_pq_suites = 0;
149+
#endif
150+
151+
static constexpr size_t n_supported_suites =
152+
5 + n_supported_x448_suites + n_supported_pq_suites;
153+
extern const std::array<CipherSuite::ID, n_supported_suites>
154+
all_supported_cipher_suites;
155+
142156
// Utilities
143157
using MLS_NAMESPACE::hpke::random_bytes;
144158

src/core_types.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "mls/core_types.h"
2+
#include "mls/crypto.h"
23
#include "mls/messages.h"
34
#include <namespace.h>
45

@@ -37,15 +38,6 @@ const std::array<ProtocolVersion, 1> all_supported_versions = {
3738
ProtocolVersion::mls10
3839
};
3940

40-
const std::array<CipherSuite::ID, 6> all_supported_ciphersuites = {
41-
CipherSuite::ID::X25519_AES128GCM_SHA256_Ed25519,
42-
CipherSuite::ID::P256_AES128GCM_SHA256_P256,
43-
CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519,
44-
CipherSuite::ID::X448_AES256GCM_SHA512_Ed448,
45-
CipherSuite::ID::P521_AES256GCM_SHA512_P521,
46-
CipherSuite::ID::X448_CHACHA20POLY1305_SHA512_Ed448,
47-
};
48-
4941
const std::array<CredentialType, 4> all_supported_credentials = {
5042
CredentialType::basic,
5143
CredentialType::x509,
@@ -58,7 +50,7 @@ Capabilities::create_default()
5850
{
5951
return {
6052
{ all_supported_versions.begin(), all_supported_versions.end() },
61-
{ all_supported_ciphersuites.begin(), all_supported_ciphersuites.end() },
53+
{ all_supported_cipher_suites.begin(), all_supported_cipher_suites.end() },
6254
{ /* No non-default extensions */ },
6355
{ /* No non-default proposals */ },
6456
{ all_supported_credentials.begin(), all_supported_credentials.end() },

src/crypto.cpp

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,32 @@ CipherSuite::get() const
130130
Digest::get<Digest::ID::SHA512>(),
131131
Signature::get<Signature::ID::Ed448>(),
132132
};
133-
#endif
133+
#endif // !defined(WITH_BORINGSSL)
134+
135+
#if defined(WITH_PQ)
136+
static const auto ciphers_MLKEM768X25519_AES256GCM_SHA384_Ed25519 =
137+
CipherSuite::Ciphers{
138+
HPKE(
139+
KEM::ID::MLKEM768_X25519, KDF::ID::HKDF_SHA384, AEAD::ID::AES_256_GCM),
140+
Digest::get<Digest::ID::SHA384>(),
141+
Signature::get<Signature::ID::Ed25519>(),
142+
};
143+
144+
static const auto ciphers_MLKEM768P256_AES256GCM_SHA384_P256 =
145+
CipherSuite::Ciphers{
146+
HPKE(KEM::ID::MLKEM768_P256, KDF::ID::HKDF_SHA384, AEAD::ID::AES_256_GCM),
147+
Digest::get<Digest::ID::SHA384>(),
148+
Signature::get<Signature::ID::P256_SHA256>(),
149+
};
150+
151+
static const auto ciphers_MLKEM1024P384_AES256GCM_SHA384_P384 =
152+
CipherSuite::Ciphers{
153+
HPKE(
154+
KEM::ID::MLKEM1024_P384, KDF::ID::HKDF_SHA384, AEAD::ID::AES_256_GCM),
155+
Digest::get<Digest::ID::SHA384>(),
156+
Signature::get<Signature::ID::P384_SHA384>(),
157+
};
158+
#endif // defined(WITH_PQ)
134159

135160
switch (id) {
136161
case ID::unknown:
@@ -159,6 +184,17 @@ CipherSuite::get() const
159184
return ciphers_X448_CHACHA20POLY1305_SHA512_Ed448;
160185
#endif
161186

187+
#if !defined(P256_SHA256)
188+
case ID::MLKEM768X25519_AES256GCM_SHA384_Ed25519:
189+
return ciphers_MLKEM768X25519_AES256GCM_SHA384_Ed25519;
190+
191+
case ID::MLKEM768P256_AES256GCM_SHA384_P256:
192+
return ciphers_MLKEM768P256_AES256GCM_SHA384_P256;
193+
194+
case ID::MLKEM1024P384_AES256GCM_SHA384_P384:
195+
return ciphers_MLKEM1024P384_AES256GCM_SHA384_P384;
196+
#endif
197+
162198
default:
163199
throw InvalidParameterError("Unsupported ciphersuite");
164200
}
@@ -200,25 +236,23 @@ CipherSuite::derive_tree_secret(const bytes& secret,
200236
return expand_with_label(secret, label, tls::marshal(generation), length);
201237
}
202238

203-
#if WITH_BORINGSSL
204-
const std::array<CipherSuite::ID, 5> all_supported_suites = {
205-
CipherSuite::ID::X25519_AES128GCM_SHA256_Ed25519,
206-
CipherSuite::ID::P256_AES128GCM_SHA256_P256,
207-
CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519,
208-
CipherSuite::ID::P521_AES256GCM_SHA512_P521,
209-
CipherSuite::ID::P384_AES256GCM_SHA384_P384,
210-
};
211-
#else
212-
const std::array<CipherSuite::ID, 7> all_supported_suites = {
213-
CipherSuite::ID::X25519_AES128GCM_SHA256_Ed25519,
214-
CipherSuite::ID::P256_AES128GCM_SHA256_P256,
215-
CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519,
216-
CipherSuite::ID::P521_AES256GCM_SHA512_P521,
217-
CipherSuite::ID::P384_AES256GCM_SHA384_P384,
218-
CipherSuite::ID::X448_CHACHA20POLY1305_SHA512_Ed448,
219-
CipherSuite::ID::X448_AES256GCM_SHA512_Ed448,
220-
};
239+
const std::array<CipherSuite::ID, n_supported_suites>
240+
all_supported_cipher_suites = {
241+
CipherSuite::ID::X25519_AES128GCM_SHA256_Ed25519,
242+
CipherSuite::ID::P256_AES128GCM_SHA256_P256,
243+
CipherSuite::ID::X25519_CHACHA20POLY1305_SHA256_Ed25519,
244+
CipherSuite::ID::P521_AES256GCM_SHA512_P521,
245+
CipherSuite::ID::P384_AES256GCM_SHA384_P384,
246+
#if !defined(WITH_BORINGSSL)
247+
CipherSuite::ID::X448_CHACHA20POLY1305_SHA512_Ed448,
248+
CipherSuite::ID::X448_AES256GCM_SHA512_Ed448,
221249
#endif
250+
#if defined(WITH_PQ)
251+
CipherSuite::ID::MLKEM768X25519_AES256GCM_SHA384_Ed25519,
252+
CipherSuite::ID::MLKEM768P256_AES256GCM_SHA384_P256,
253+
CipherSuite::ID::MLKEM1024P384_AES256GCM_SHA384_P384,
254+
#endif
255+
};
222256

223257
// MakeKeyPackageRef(value) = KDF.expand(
224258
// KDF.extract("", value), "MLS 1.0 KeyPackage Reference", 16)

src/state.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,8 +1847,8 @@ State::valid(const ReInit& reinit)
18471847
{
18481848
// Check that the version and CipherSuite are ones we support
18491849
auto supported_version = (reinit.version == ProtocolVersion::mls10);
1850-
auto supported_suite =
1851-
stdx::contains(all_supported_suites, reinit.cipher_suite.cipher_suite());
1850+
auto supported_suite = stdx::contains(all_supported_cipher_suites,
1851+
reinit.cipher_suite.cipher_suite());
18521852

18531853
return supported_version && supported_suite;
18541854
}

test/crypto.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ TEST_CASE("Basic HPKE")
1212
auto context = random_bytes(100);
1313
auto original = random_bytes(100);
1414

15-
for (auto suite_id : all_supported_suites) {
15+
for (auto suite_id : all_supported_cipher_suites) {
1616
auto suite = CipherSuite{ suite_id };
1717
auto s = bytes{ 0, 1, 2, 3 };
1818

@@ -38,7 +38,7 @@ TEST_CASE("Basic HPKE")
3838

3939
TEST_CASE("HPKE Key Serialization")
4040
{
41-
for (auto suite_id : all_supported_suites) {
41+
for (auto suite_id : all_supported_cipher_suites) {
4242
auto suite = CipherSuite{ suite_id };
4343
auto x = HPKEPrivateKey::derive(suite, { 0, 1, 2, 3 });
4444
auto gX = x.public_key;
@@ -54,7 +54,7 @@ TEST_CASE("HPKE Key Serialization")
5454

5555
TEST_CASE("Basic Signature")
5656
{
57-
for (auto suite_id : all_supported_suites) {
57+
for (auto suite_id : all_supported_cipher_suites) {
5858
auto suite = CipherSuite{ suite_id };
5959
auto a = SignaturePrivateKey::generate(suite);
6060
auto b = SignaturePrivateKey::generate(suite);
@@ -77,7 +77,7 @@ TEST_CASE("Basic Signature")
7777

7878
TEST_CASE("Signature Key Serializion")
7979
{
80-
for (auto suite_id : all_supported_suites) {
80+
for (auto suite_id : all_supported_cipher_suites) {
8181
auto suite = CipherSuite{ suite_id };
8282
auto x = SignaturePrivateKey::generate(suite);
8383
auto gX = x.public_key;
@@ -92,7 +92,7 @@ TEST_CASE("Signature Key Serializion")
9292

9393
TEST_CASE("Signature Key JWK Import/Export")
9494
{
95-
for (auto suite_id : all_supported_suites) {
95+
for (auto suite_id : all_supported_cipher_suites) {
9696
const auto suite = CipherSuite{ suite_id };
9797
const auto priv = SignaturePrivateKey::generate(suite);
9898
const auto pub = priv.public_key;
@@ -128,7 +128,7 @@ TEST_CASE("Signature Key JWK Import/Export")
128128

129129
TEST_CASE("Crypto Interop")
130130
{
131-
for (auto suite : all_supported_suites) {
131+
for (auto suite : all_supported_cipher_suites) {
132132
auto tv = CryptoBasicsTestVector{ suite };
133133
REQUIRE(tv.verify() == std::nullopt);
134134
}

test/key_schedule.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@ using namespace mls_vectors;
77

88
TEST_CASE("Secret Tree Interop")
99
{
10-
for (auto suite : all_supported_suites) {
10+
for (auto suite : all_supported_cipher_suites) {
1111
const auto tv = SecretTreeTestVector{ suite, 15, { 1, 10 } };
1212
REQUIRE(tv.verify() == std::nullopt);
1313
}
1414
}
1515

1616
TEST_CASE("PSK Secret Interop")
1717
{
18-
for (auto suite : all_supported_suites) {
18+
for (auto suite : all_supported_cipher_suites) {
1919
const auto tv = PSKSecretTestVector{ suite, 5 };
2020
REQUIRE(tv.verify() == std::nullopt);
2121
}
2222
}
2323

2424
TEST_CASE("Key Schedule Interop")
2525
{
26-
for (auto suite : all_supported_suites) {
26+
for (auto suite : all_supported_cipher_suites) {
2727
auto tv = KeyScheduleTestVector{ suite, 15 };
2828
REQUIRE(tv.verify() == std::nullopt);
2929
}

test/messages.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ TEST_CASE("Messages Interop")
162162

163163
TEST_CASE("Message Protection Interop")
164164
{
165-
for (auto suite : all_supported_suites) {
165+
for (auto suite : all_supported_cipher_suites) {
166166
auto tv = MessageProtectionTestVector{ suite };
167167
REQUIRE(tv.verify() == std::nullopt);
168168
}

test/treekem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ TEST_CASE_METHOD(TreeKEMTest, "TreeKEM encap/decap")
299299

300300
TEST_CASE("TreeKEM Interop", "[.][all]")
301301
{
302-
for (auto suite : all_supported_suites) {
302+
for (auto suite : all_supported_cipher_suites) {
303303
for (auto structure : treekem_test_tree_structures) {
304304
auto tv = TreeKEMTestVector{ suite, structure };
305305
REQUIRE(tv.verify() == std::nullopt);

0 commit comments

Comments
 (0)