|
1 | | -:100644 100644 dd8a39197a 0000000000 M crypto/ml_kem/ml_kem.c |
| 1 | +From 2209874540108f88ed2d61f2bf14cce3c538be58 Mon Sep 17 00:00:00 2001 |
| 2 | +From: nkraetzschmar <9020053+nkraetzschmar@users.noreply.github.com> |
| 3 | +Date: Fri, 17 Jul 2026 15:09:20 +0000 |
| 4 | +Subject: [PATCH] FIPS: ML-KEM zeroize full key allocation in key_reset |
| 5 | + |
| 6 | +ossl_ml_kem_key_reset failed to wipe the public vector |t| and matrix |
| 7 | +|m| (only the private |s|/|z|/|d| tail was cleansed), so on freeing a |
| 8 | +public key those SSPs survived; the prior fix attempt cleansed |m| with |
| 9 | +sizeof(pointer) (8 bytes) *after* OPENSSL_free(key->t), a use-after-free. |
| 10 | + |
| 11 | +|t|, |m|, |s|, |z|/|d| are consecutive slices of the single allocation |
| 12 | +starting at key->t (add_storage()), so cleanse the whole allocation |
| 13 | +(puballoc/prvalloc) before freeing it. |rho|/|pkhash| alias the in-struct |
| 14 | +|seedbuf|, cleansed separately. |
| 15 | +--- |
| 16 | + crypto/ml_kem/ml_kem.c | 23 ++++++++++++++++------- |
| 17 | + 1 file changed, 16 insertions(+), 7 deletions(-) |
2 | 18 |
|
3 | 19 | diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c |
4 | | -index dd8a39197a..5c5446435d 100644 |
| 20 | +index f29912581a..fc528317bb 100644 |
5 | 21 | --- a/crypto/ml_kem/ml_kem.c |
6 | 22 | +++ b/crypto/ml_kem/ml_kem.c |
7 | | -@@ -1897,2 +1900,8 @@ void ossl_ml_kem_key_reset(ML_KEM_KEY *key) |
| 23 | +@@ -1909,16 +1909,25 @@ void ossl_ml_kem_key_reset(ML_KEM_KEY *key) |
| 24 | + if (key->t == NULL) |
| 25 | + return; |
| 26 | + /*- |
| 27 | +- * Cleanse any sensitive data: |
| 28 | +- * - The private vector |s| is immediately followed by the FO failure |
| 29 | +- * secret |z|, and seed |d|, we can cleanse all three in one call. |
| 30 | ++ * Cleanse all key material before releasing it. |
| 31 | + * |
| 32 | +- * - Otherwise, when key->d is set, cleanse the stashed seed. |
| 33 | ++ * |t|, |m|, |s| and |z|/|d| are consecutive slices of the single |
| 34 | ++ * allocation that begins at |key->t| (see add_storage()), so a single |
| 35 | ++ * cleanse over the whole allocation covers the public vector |t| and |
| 36 | ++ * matrix |m| as well as the private vector |s| and the FO failure secret |
| 37 | ++ * |z| (plus the optional seed |d|). This must happen *before* the |
| 38 | ++ * OPENSSL_free() below, while the memory is still ours. |
| 39 | + */ |
| 40 | +- if (ossl_ml_kem_have_prvkey(key)) |
| 41 | +- OPENSSL_cleanse(key->s, |
| 42 | +- key->vinfo->rank * sizeof(scalar) + 2 * ML_KEM_RANDOM_BYTES); |
| 43 | ++ OPENSSL_cleanse(key->t, ossl_ml_kem_have_prvkey(key) |
| 44 | ++ ? key->vinfo->prvalloc |
| 45 | ++ : key->vinfo->puballoc); |
8 | 46 | OPENSSL_free(key->t); |
9 | | -+ |
10 | | -+ if (key->rho != NULL) |
11 | | -+ OPENSSL_cleanse(key->rho, sizeof(key->rho)); |
12 | | -+ OPENSSL_cleanse((void *)key->pkhash, ML_KEM_PKHASH_BYTES); |
13 | | -+ OPENSSL_cleanse(key->m, sizeof(key->m)); |
14 | | -+ OPENSSL_cleanse(key->seedbuf, ML_KEM_SEED_BYTES); |
| 47 | ++ /* |
| 48 | ++ * |rho| and |pkhash| are aliases into the in-struct |seedbuf|, so wiping |
| 49 | ++ * |seedbuf| clears both. |seedbuf| is not part of the freed allocation |
| 50 | ++ * above, so the order here is immaterial. |
| 51 | ++ */ |
| 52 | ++ OPENSSL_cleanse(key->seedbuf, sizeof(key->seedbuf)); |
15 | 53 | key->d = key->z = (uint8_t *)(key->s = key->m = key->t = NULL); |
| 54 | + } |
| 55 | + |
| 56 | +-- |
| 57 | +2.47.3 |
| 58 | + |
0 commit comments