-
Notifications
You must be signed in to change notification settings - Fork 31
Open
Description
I've found a discrepancy between this implementation of ML-KEM and the Go one. There's a repro below for a case which is rejected by Go but accepted by Rust. Seems the issue is that public keys loaded from bytes are not validated, but they could be invalid.
//! Reproduction case: ml-kem accepts invalid encapsulation keys
//!
//! ML-KEM encapsulation keys contain polynomial coefficients encoded as 12-bit
//! values. Per FIPS 203, each coefficient must be in the range [0, 3329) where
//! 3329 is the modulus q. The ml-kem crate does not validate this, accepting
//! arbitrary bytes as valid encapsulation keys.
//!
//! Go's crypto/mlkem implementation validates coefficients and rejects invalid keys.
use ml_kem::{EncodedSizeUser, MlKem768Params};
use ml_kem::kem::EncapsulationKey;
fn main() {
// Create an invalid key: all bytes set to 0xFF
// When decoded as 12-bit coefficients, this produces values of 0xFFF = 4095 > 3329
let invalid_key = [0xFFu8; 1184];
// ml-kem accepts this without error
let ek = EncapsulationKey::<MlKem768Params>::from_bytes(&invalid_key.into());
println!("ml-kem accepted invalid key with coefficients >= 3329");
println!("Encapsulation key bytes: {:?}...", &ek.as_bytes()[..16]);
// Demonstrate the issue: the first 3 bytes [0xFF, 0xFF, 0xFF] decode as:
// - coeff1 = 0xFF | ((0xFF & 0x0F) << 8) = 0xFF | 0xF00 = 0xFFF = 4095
// - coeff2 = (0xFF >> 4) | (0xFF << 4) = 0x0F | 0xFF0 = 0xFFF = 4095
// Both exceed q=3329, making this an invalid ML-KEM key
let bytes: [u8; 3] = [0xFF, 0xFF, 0xFF];
let coeff1 = u16::from(bytes[0]) | ((u16::from(bytes[1]) & 0x0F) << 8);
let coeff2 = (u16::from(bytes[1]) >> 4) | (u16::from(bytes[2]) << 4);
println!("\nDecoded coefficients from [0xFF, 0xFF, 0xFF]:");
println!(" coeff1 = {} (max valid: 3328)", coeff1);
println!(" coeff2 = {} (max valid: 3328)", coeff2);
println!("\nBoth exceed q=3329, but ml-kem accepts this key.");
}ml-kem accepted invalid key with coefficients >= 3329
Encapsulation key bytes: [254, 226, 47, 254, 226, 47, 254, 226, 47, 254, 226, 47, 254, 226, 47, 254]...
Decoded coefficients from [0xFF, 0xFF, 0xFF]:
coeff1 = 4095 (max valid: 3328)
coeff2 = 4095 (max valid: 3328)
tarcieri
Metadata
Metadata
Assignees
Labels
No labels