Skip to content

Commit d5a5b22

Browse files
committed
Enable hybrid ML-KEMs in FIPS mode
1 parent f815dce commit d5a5b22

File tree

4 files changed

+104
-47
lines changed

4 files changed

+104
-47
lines changed

kex-names.c

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,30 @@ static const struct kexalg gss_kexalgs[] = {
112112
{ NULL, 0, -1, -1},
113113
};
114114

115+
/*
116+
* 0 - unavailable
117+
* 1 - available in non-FIPS mode
118+
* 2 - available in FIPS mode
119+
*/
115120
static int is_mlkem768_available()
116121
{
117122
static int is_fetched = -1;
118123

119124
if (is_fetched == -1) {
120-
EVP_KEM *mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL);
121-
is_fetched = mlkem768 != NULL ? 1 : 0;
125+
EVP_KEM *mlkem768 = NULL;
126+
127+
if (FIPS_mode() == 1) {
128+
mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL);
129+
is_fetched = mlkem768 != NULL ? 2 : 0;
130+
131+
if (is_fetched == 0) {
132+
mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", "provider=default,-fips");
133+
is_fetched = mlkem768 != NULL ? 1 : 0;
134+
}
135+
} else {
136+
mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL);
137+
is_fetched = mlkem768 != NULL ? 1 : 0;
138+
}
122139
EVP_KEM_free(mlkem768);
123140
}
124141

@@ -131,13 +148,32 @@ kex_alg_list_internal(char sep, const struct kexalg *algs)
131148
char *ret = NULL, *tmp;
132149
size_t nlen, rlen = 0;
133150
const struct kexalg *k;
151+
int x25519mlkem_available = 0, nistmlkem_available = 0;
152+
153+
/*
154+
* FIPS provider can provide ML-KEMs and then all hybrids are available
155+
* Otherwise only NIST hybrids are available
156+
* */
157+
if (FIPS_mode()) {
158+
if (is_mlkem768_available() == 2) {
159+
x25519mlkem_available = 1;
160+
nistmlkem_available = 1;
161+
} else if (is_mlkem768_available() == 1) {
162+
nistmlkem_available = 1;
163+
}
164+
} else {
165+
if (is_mlkem768_available() > 0) {
166+
x25519mlkem_available = 1;
167+
nistmlkem_available = 1;
168+
}
169+
}
134170

135171
for (k = algs; k->name != NULL; k++) {
136-
if ( (strcmp(k->name, KEX_MLKEM768X25519_SHA256) == 0
137-
|| strcmp(k->name, KEX_MLKEM768NISTP256_SHA256) == 0
138-
|| strcmp(k->name, KEX_MLKEM1024NISTP384_SHA384) == 0)
139-
&& !is_mlkem768_available())
172+
if ( (strcmp(k->name, KEX_MLKEM768X25519_SHA256) == 0 && x25519mlkem_available == 0)
173+
|| (strcmp(k->name, KEX_MLKEM768NISTP256_SHA256) == 0 && nistmlkem_available == 0)
174+
|| (strcmp(k->name, KEX_MLKEM1024NISTP384_SHA384) == 0 && nistmlkem_available == 0))
140175
continue;
176+
141177
if (ret != NULL)
142178
ret[rlen++] = sep;
143179
nlen = strlen(k->name);
@@ -168,12 +204,30 @@ static const struct kexalg *
168204
kex_alg_by_name(const char *name)
169205
{
170206
const struct kexalg *k;
207+
int x25519mlkem_available = 0, nistmlkem_available = 0;
171208

172-
if ( (strcmp(name, KEX_MLKEM768X25519_SHA256) == 0
173-
|| strcmp(name, KEX_MLKEM768NISTP256_SHA256) == 0
174-
|| strcmp(name, KEX_MLKEM1024NISTP384_SHA384) == 0)
175-
&& !is_mlkem768_available())
176-
return NULL;
209+
/*
210+
* FIPS provider can provide ML-KEMs and then all hybrids are available
211+
* Otherwise only NIST hybrids are available
212+
* */
213+
if (FIPS_mode()) {
214+
if (is_mlkem768_available() == 2) {
215+
x25519mlkem_available = 1;
216+
nistmlkem_available = 1;
217+
} else if (is_mlkem768_available() == 1) {
218+
nistmlkem_available = 1;
219+
}
220+
} else {
221+
if (is_mlkem768_available() > 0) {
222+
x25519mlkem_available = 1;
223+
nistmlkem_available = 1;
224+
}
225+
}
226+
227+
if ( (strcmp(name, KEX_MLKEM768X25519_SHA256) == 0 && x25519mlkem_available == 0)
228+
|| (strcmp(name, KEX_MLKEM768NISTP256_SHA256) == 0 && nistmlkem_available == 0)
229+
|| (strcmp(name, KEX_MLKEM1024NISTP384_SHA384) == 0 && nistmlkem_available == 0))
230+
return NULL;
177231

178232
for (k = kexalgs; k->name != NULL; k++) {
179233
if (strcmp(k->name, name) == 0)

kexgen.c

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,23 @@ kex_gen_client(struct ssh *ssh)
133133
break;
134134
case KEX_KEM_MLKEM768X25519_SHA256:
135135
if (FIPS_mode()) {
136-
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
137-
r = SSH_ERR_INVALID_ARGUMENT;
136+
EVP_KEM *mlkem = EVP_KEM_fetch(NULL, "mlkem768", NULL);
137+
if (mlkem == NULL) {
138+
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
139+
r = SSH_ERR_INVALID_ARGUMENT;
140+
} else {
141+
EVP_KEM_free(mlkem);
142+
r = kex_kem_mlkem768x25519_keypair(kex);
143+
}
138144
} else {
139145
r = kex_kem_mlkem768x25519_keypair(kex);
140146
}
141147
break;
142148
case KEX_KEM_MLKEM768NISTP256_SHA256:
143-
if (FIPS_mode()) {
144-
logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
145-
r = SSH_ERR_INVALID_ARGUMENT;
146-
} else {
147149
r = kex_kem_mlkem768nistp256_keypair(kex);
148-
}
149150
break;
150151
case KEX_KEM_MLKEM1024NISTP384_SHA384:
151-
if (FIPS_mode()) {
152-
logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
153-
r = SSH_ERR_INVALID_ARGUMENT;
154-
} else {
155152
r = kex_kem_mlkem1024nistp384_keypair(kex);
156-
}
157153
break;
158154
default:
159155
r = SSH_ERR_INVALID_ARGUMENT;
@@ -239,30 +235,27 @@ input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh)
239235
break;
240236
case KEX_KEM_MLKEM768X25519_SHA256:
241237
if (FIPS_mode()) {
242-
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
243-
r = SSH_ERR_INVALID_ARGUMENT;
238+
EVP_KEM *mlkem = EVP_KEM_fetch(NULL, "mlkem768", NULL);
239+
if (mlkem == NULL) {
240+
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
241+
r = SSH_ERR_INVALID_ARGUMENT;
242+
} else {
243+
EVP_KEM_free(mlkem);
244+
r = kex_kem_mlkem768x25519_dec(kex, server_blob,
245+
&shared_secret);
246+
}
244247
} else {
245248
r = kex_kem_mlkem768x25519_dec(kex, server_blob,
246249
&shared_secret);
247250
}
248251
break;
249252
case KEX_KEM_MLKEM768NISTP256_SHA256:
250-
if (FIPS_mode()) {
251-
logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
252-
r = SSH_ERR_INVALID_ARGUMENT;
253-
} else {
254253
r = kex_kem_mlkem768nistp256_dec(kex, server_blob,
255254
&shared_secret);
256-
}
257255
break;
258256
case KEX_KEM_MLKEM1024NISTP384_SHA384:
259-
if (FIPS_mode()) {
260-
logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
261-
r = SSH_ERR_INVALID_ARGUMENT;
262-
} else {
263257
r = kex_kem_mlkem1024nistp384_dec(kex, server_blob,
264258
&shared_secret);
265-
}
266259
break;
267260
default:
268261
r = SSH_ERR_INVALID_ARGUMENT;
@@ -398,30 +391,27 @@ input_kex_gen_init(int type, u_int32_t seq, struct ssh *ssh)
398391
break;
399392
case KEX_KEM_MLKEM768X25519_SHA256:
400393
if (FIPS_mode()) {
401-
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
402-
r = SSH_ERR_INVALID_ARGUMENT;
394+
EVP_KEM *mlkem = EVP_KEM_fetch(NULL, "mlkem768", NULL);
395+
if (mlkem == NULL) {
396+
logit_f("Key exchange type mlkem768x25519 is not allowed in FIPS mode");
397+
r = SSH_ERR_INVALID_ARGUMENT;
398+
} else {
399+
EVP_KEM_free(mlkem);
400+
r = kex_kem_mlkem768x25519_enc(kex, client_pubkey,
401+
&server_pubkey, &shared_secret);
402+
}
403403
} else {
404404
r = kex_kem_mlkem768x25519_enc(kex, client_pubkey,
405405
&server_pubkey, &shared_secret);
406406
}
407407
break;
408408
case KEX_KEM_MLKEM768NISTP256_SHA256:
409-
if (FIPS_mode()) {
410-
logit_f("Key exchange type mlkem768nistp256 is not allowed in FIPS mode");
411-
r = SSH_ERR_INVALID_ARGUMENT;
412-
} else {
413409
r = kex_kem_mlkem768nistp256_enc(kex, client_pubkey,
414410
&server_pubkey, &shared_secret);
415-
}
416411
break;
417412
case KEX_KEM_MLKEM1024NISTP384_SHA384:
418-
if (FIPS_mode()) {
419-
logit_f("Key exchange type mlkem1024nistp384 is not allowed in FIPS mode");
420-
r = SSH_ERR_INVALID_ARGUMENT;
421-
} else {
422413
r = kex_kem_mlkem1024nistp384_enc(kex, client_pubkey,
423414
&server_pubkey, &shared_secret);
424-
}
425415
break;
426416
default:
427417
r = SSH_ERR_INVALID_ARGUMENT;

kexmlkem768x25519.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "libcrux_mlkem768_sha3.h"
5151
#include <openssl/err.h>
5252
#include <openssl/evp.h>
53+
#include <openssl/fips.h>
5354
#include <stdio.h>
5455

5556
static int
@@ -62,6 +63,14 @@ mlkem_keypair_gen(const char *algname, unsigned char *pubkeybuf, size_t pubkey_s
6263
size_t got_pub_size = pubkey_size, got_priv_size = privkey_size;
6364

6465
ctx = EVP_PKEY_CTX_new_from_name(NULL, algname, NULL);
66+
67+
if (ctx == NULL && FIPS_mode()) {
68+
/* We have filtered x25519 + ML-KEM in FIPS mode earlier
69+
* so if we are in FIPS mode and ML-KEM is not available with default propq,
70+
* we can fetch it from the default provider */
71+
ctx = EVP_PKEY_CTX_new_from_name(NULL, algname, "provider=default,-fips");
72+
}
73+
6574
if (ctx == NULL) {
6675
ret = SSH_ERR_LIBCRYPTO_ERROR;
6776
goto err;

myproposal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#define KEX_SERVER_KEX \
2828
"mlkem768x25519-sha256," \
29+
"mlkem768nistp256-sha256," \
30+
"mlkem1024nistp384-sha384," \
2931
"sntrup761x25519-sha512," \
3032
3133
"curve25519-sha256," \
@@ -97,6 +99,8 @@
9799
"aes192-cbc,aes256-cbc,[email protected]," \
98100
99101
#define KEX_DEFAULT_KEX_FIPS \
102+
"mlkem768nistp256-sha256," \
103+
"mlkem1024nistp384-sha384," \
100104
"ecdh-sha2-nistp256," \
101105
"ecdh-sha2-nistp384," \
102106
"ecdh-sha2-nistp521," \

0 commit comments

Comments
 (0)