Algorithm::encryption_key_from_decryption_key` skips both of the validations its siblings perform
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 58/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- rust
- Ambito
- cryptography, security
Direzione di ricerca
Inizia in frodo-kem/src/lib.rs, in corrispondenza di inner_encryption_key_from_decryption_key, e confrontalo con inner_encapsulate_with_rng e inner_decapsulate; esamina DecryptionKeyRef::from_slice e public_key in frodo-kem/src/hazmat/models.rs. Il lavoro è completo quando gli algoritmi non corrispondenti e le lunghezze delle chiavi non valide restituiscono errori invece di produrre una chiave o causare un panic, mentre l’implementazione From non interessata rimane coerente.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Present in published 0.1.0 (src/lib.rs:995) and unchanged on master at 1d535a11 (src/lib.rs:1031, crate version 0.2.0, unreleased). Line numbers below are from master.
Raising it now partly because the fix wants a breaking signature change: 0.2.0 has not shipped, so if that signature is going to move, this is the cheapest moment for it to move.
CONFORMANCE.md lines 52-53 state, under Algorithm review:
Dynamic API boundaries reject keys and ciphertexts tagged for another parameter set and reject invalid message, salt, key, and ciphertext lengths.
This method does neither. It has two visible faces, and one fix closes both.
The code
frodo-kem/src/lib.rs:1027-1036:
fn inner_encryption_key_from_decryption_key<B: Params>(
&self,
secret_key: &DecryptionKey,
) -> EncryptionKey {
let sk = DecryptionKeyRef::<B>(secret_key.value.as_slice(), PhantomData);
EncryptionKey {
algorithm: *self,
value: sk.public_key().to_vec(),
}
}
Compared with inner_encapsulate_with_rng, eleven lines further down at frodo-kem/src/lib.rs:1558-1565:
fn inner_encapsulate_with_rng<K: Kem, R: CryptoRng + ?Sized>(
&self,
encryption_key: &EncryptionKey,
rng: &mut R,
) -> FrodoResult<(Ciphertext, SharedSecret)> {
if encryption_key.algorithm != *self {
return Err(Error::AlgorithmMismatch);
}
let pk = EncryptionKeyRef::from_slice(encryption_key.value.as_slice())?;
The sibling checks the tag and goes through the length-checked from_slice. The method above constructs DecryptionKeyRef::<B> by direct tuple construction, which bypasses from_slice entirely, and never compares secret_key.algorithm to *self. inner_decapsulate (frodo-kem/src/lib.rs:1643) checks both tags too.
Face 1 — panic
DecryptionKeyRef::public_key (frodo-kem/src/hazmat/models.rs:454-455) slices using the receiving parameter set's lengths over the supplied key's bytes:
pub fn public_key(&self) -> &[u8] {
&self.0[P::SHARED_SECRET_LENGTH..P::SHARED_SECRET_LENGTH + P::PUBLIC_KEY_LENGTH]
}
When the receiving set's PUBLIC_KEY_LENGTH exceeds the supplied key's length, that range is out of bounds. A correctly-tagged 19888-byte FrodoKEM-640-AES decryption key handed to any of the four 1344 algorithms panics with range end index 21552 out of range for slice of length 19888 — measured on 96 of 96 right-length buffers at all four 1344 parameter sets.
let dk = Algorithm::FrodoKem640Aes.decryption_key_from_bytes(&bytes_19888)?;
let _ = Algorithm::FrodoKem1344Shake.encryption_key_from_decryption_key(&dk); // panics
Face 2 — silent wrong answer
When the supplied key is long enough for the receiving set's public-key window, there is no panic. The method returns a well-formed, correct-length EncryptionKey tagged with the receiving algorithm, built from bytes that are not that algorithm's public key. Ciphertexts produced under it decapsulate to nothing anyone can use.
The signature is -> EncryptionKey, not -> FrodoResult<EncryptionKey>, so the method has no way to report the mismatch even if it detected one.
Suggested fix
Matching the siblings:
if secret_key.algorithm != *self {
return Err(Error::AlgorithmMismatch);
}
let sk = DecryptionKeyRef::<B>::from_slice(secret_key.value.as_slice())?;
That needs the public method's return type to become FrodoResult<EncryptionKey>, which is a breaking change — hence raising it as an issue rather than sending a patch, since the shape of the fix is your call.
The infallible impl From<&DecryptionKey> for EncryptionKey (frodo-kem/src/lib.rs:311-317) is unaffected: it reads secret_key.algorithm, so it is always self-consistent and cannot reach either face.
On reachability, stated carefully
Neither face is reachable through the idiomatic From path. Both require an application that pairs a DecryptionKey obtained from one source with an Algorithm value obtained from another.
That shape is plausible rather than observed: the serde impl (frodo-kem/src/lib.rs:130-242, applied at frodo-kem/src/lib.rs:386) puts the algorithm tag on the wire as the first byte of the binary form, and the deserialiser routes on it, so an application that deserialises a DecryptionKey and then calls the method with its own configured Algorithm would be crashable by whoever supplied those bytes. I did not find such an application; I am describing an API shape that permits one.
Worth noting separately: 19888 all-zero bytes are accepted as a FrodoKEM-640-AES decryption key, so no genuine key material is needed to reach the panic.
This is an API-contract and availability issue, not a vulnerability — no key recovery, no plaintext recovery, and the panic is a Rust bounds check doing its job rather than an out-of-bounds read. I am not filing it as a security advisory.
Method
Found with our own PQC test harness, and this issue was drafted with AI assistance — both stated up front as a matter of policy. Every claim above was reproduced by execution, then re-checked independently by three reviewers who did not share harness code. Two of the three tempered the original reachability wording, and this text reflects the tempered version.
- Lingua principale
- Rust
- Stelle
- 111
- Fork
- 53
- Merge medio
- 5g 29m
- PR unite (30g)
- 2
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di RustCrypto/KEMs
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
RustCrypto/KEMs#346 · 2 commenti ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
RustCrypto/KEMs#328 ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 30/100
RustCrypto/KEMs#325 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
RustCrypto/KEMs#308 ·
-
Incremental ML-KEM Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
RustCrypto/KEMs#264 · 2 commenti ·
Tutte le issue di RustCrypto/KEMs
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
-
bug core
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
-
JIT-compiled number -> Decimal conversion silently overflows instead of raising DECIMAL_OVERFLOW Apertafuzz
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
ClickHouse/ClickHouse#122114 ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
linebender/vello_svg#90 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100