cypheron_core/kem/
ml_kem_768.rs1use crate::kem::sizes;
16use crate::kem::{Kem, KemVariant};
17
18use secrecy::{ExposeSecret, SecretBox};
19use thiserror::Error;
20use zeroize::Zeroize;
21
22#[cfg(not(rust_analyzer))]
23#[allow(non_camel_case_types)]
24#[allow(non_snake_case)]
25#[allow(non_upper_case_globals)]
26mod bindings {
27 include!(concat!(env!("OUT_DIR"), "/ml_kem_768_bindings.rs"));
28}
29use bindings::*;
30
31pub struct MlKemSecretKey(pub SecretBox<[u8; sizes::ML_KEM_768_SECRET]>);
32
33#[deprecated(
34 since = "0.2.0",
35 note = "Use MlKemSecretKey instead for NIST FIPS 203 compliance"
36)]
37pub type KyberSecretKey = MlKemSecretKey;
38
39#[derive(Clone)]
40pub struct MlKemPublicKey(pub [u8; sizes::ML_KEM_768_PUBLIC]);
41
42#[deprecated(
43 since = "0.2.0",
44 note = "Use MlKemPublicKey instead for NIST FIPS 203 compliance"
45)]
46pub type KyberPublicKey = MlKemPublicKey;
47
48#[derive(Error, Debug)]
49pub enum MlKemError {
50 #[error("Key generation failed - entropy failure (ERROR-KEM-001)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-001")]
51 KeyGenerationEntropyFailure,
52 #[error("Key generation failed - internal error (ERROR-KEM-004)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-004")]
53 KeyGenerationInternalError,
54 #[error("Encapsulation failed - invalid public key (ERROR-KEM-003)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-003")]
55 EncapsulationInvalidKey,
56 #[error("Encapsulation failed - internal error (ERROR-KEM-005)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-005")]
57 EncapsulationInternalError,
58 #[error("Decapsulation failed - invalid ciphertext (ERROR-KEM-002)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-002")]
59 DecapsulationInvalidCiphertext,
60 #[error("Decapsulation failed - internal error (ERROR-KEM-006)\nSee: https://docs.rs/cypheron-core/troubleshooting/errors.html#error-kem-006")]
61 DecapsulationInternalError,
62 #[error("Invalid ciphertext length: expected {expected}, got {actual}")]
63 InvalidCiphertextLength { expected: usize, actual: usize },
64 #[error("Invalid public key length: expected {expected}, got {actual}")]
65 InvalidPublicKeyLength { expected: usize, actual: usize },
66 #[error("Invalid secret key length: expected {expected}, got {actual}")]
67 InvalidSecretKeyLength { expected: usize, actual: usize },
68 #[error("ML-KEM C library returned error code: {code}")]
69 CLibraryError { code: i32 },
70}
71
72#[deprecated(
73 since = "0.2.0",
74 note = "Use MlKemError instead for NIST FIPS 203 compliance"
75)]
76pub type KyberError = MlKemError;
77
78impl MlKemError {
79 pub fn from_c_code(code: i32, operation: &str) -> Self {
80 match code {
81 0 => panic!("Should not map success code 0 to error"),
82 -1 => match operation {
83 "keypair" => MlKemError::KeyGenerationInternalError,
84 "encapsulate" => MlKemError::EncapsulationInternalError,
85 "decapsulate" => MlKemError::DecapsulationInternalError,
86 _ => MlKemError::CLibraryError { code },
87 },
88 -2 => match operation {
89 "keypair" => MlKemError::KeyGenerationEntropyFailure,
90 "encapsulate" => MlKemError::EncapsulationInvalidKey,
91 "decapsulate" => MlKemError::DecapsulationInvalidCiphertext,
92 _ => MlKemError::CLibraryError { code },
93 },
94 _ => MlKemError::CLibraryError { code },
95 }
96 }
97}
98
99pub struct MlKem768;
100
101#[deprecated(
102 since = "0.2.0",
103 note = "Use MlKem768 instead for NIST FIPS 203 compliance"
104)]
105pub type Kyber768 = MlKem768;
106
107impl MlKem768 {
108 pub fn variant() -> KemVariant {
109 KemVariant::MlKem768
110 }
111
112 #[deprecated(
113 since = "0.2.0",
114 note = "Use variant() instead for NIST FIPS 203 compliance"
115 )]
116 pub fn legacy_variant() -> KemVariant {
117 #[allow(deprecated)]
118 KemVariant::Kyber768
119 }
120
121 pub fn expose_shared(secret: &SecretBox<[u8; sizes::ML_KEM_768_SHARED]>) -> &[u8] {
122 secret.expose_secret()
123 }
124}
125
126impl Kem for MlKem768 {
127 type PublicKey = MlKemPublicKey;
128 type SecretKey = MlKemSecretKey;
129 type Ciphertext = Vec<u8>;
130 type SharedSecret = SecretBox<[u8; sizes::ML_KEM_768_SHARED]>;
131 type Error = MlKemError;
132
133 fn keypair() -> Result<(Self::PublicKey, Self::SecretKey), Self::Error> {
134 let mut pk = [0u8; sizes::ML_KEM_768_PUBLIC];
135 let mut sk = [0u8; sizes::ML_KEM_768_SECRET];
136
137 let result = unsafe { pqcrystals_kyber768_ref_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
138
139 if result != 0 {
140 pk.zeroize();
141 sk.zeroize();
142 return Err(MlKemError::from_c_code(result, "keypair"));
143 }
144
145 Ok((
146 MlKemPublicKey(pk),
147 MlKemSecretKey(SecretBox::new(Box::new(sk))),
148 ))
149 }
150
151 fn encapsulate(
152 pk: &Self::PublicKey,
153 ) -> Result<(Self::Ciphertext, Self::SharedSecret), Self::Error> {
154 if pk.0.len() != sizes::ML_KEM_768_PUBLIC {
155 return Err(MlKemError::InvalidPublicKeyLength {
156 expected: sizes::ML_KEM_768_PUBLIC,
157 actual: pk.0.len(),
158 });
159 }
160
161 let mut ct = vec![0u8; sizes::ML_KEM_768_CIPHERTEXT];
162 let mut ss = [0u8; sizes::ML_KEM_768_SHARED];
163
164 let result =
165 unsafe { pqcrystals_kyber768_ref_enc(ct.as_mut_ptr(), ss.as_mut_ptr(), pk.0.as_ptr()) };
166
167 if result != 0 {
168 ss.zeroize();
169 return Err(MlKemError::from_c_code(result, "encapsulate"));
170 }
171
172 Ok((ct, SecretBox::new(ss.into())))
173 }
174
175 fn decapsulate(
176 ct: &Self::Ciphertext,
177 sk: &Self::SecretKey,
178 ) -> Result<Self::SharedSecret, Self::Error> {
179 if ct.len() != sizes::ML_KEM_768_CIPHERTEXT {
180 return Err(MlKemError::InvalidCiphertextLength {
181 expected: sizes::ML_KEM_768_CIPHERTEXT,
182 actual: ct.len(),
183 });
184 }
185 if sk.0.expose_secret().len() != sizes::ML_KEM_768_SECRET {
186 return Err(MlKemError::InvalidSecretKeyLength {
187 expected: sizes::ML_KEM_768_SECRET,
188 actual: sk.0.expose_secret().len(),
189 });
190 }
191
192 let mut ss = [0u8; sizes::ML_KEM_768_SHARED];
193
194 let result = unsafe {
195 pqcrystals_kyber768_ref_dec(ss.as_mut_ptr(), ct.as_ptr(), sk.0.expose_secret().as_ptr())
196 };
197
198 if result != 0 {
199 ss.zeroize();
200 return Err(MlKemError::from_c_code(result, "decapsulate"));
201 }
202
203 Ok(SecretBox::new(ss.into()))
204 }
205}