dcrypt_sign/dilithium/
mod.rs1use crate::error::Error as SignError;
28use core::marker::PhantomData;
29use dcrypt_api::{Result as ApiResult, Signature as SignatureTrait};
30use rand::{CryptoRng, RngCore};
31use zeroize::{Zeroize, ZeroizeOnDrop};
32
33mod arithmetic;
35mod encoding;
36mod polyvec;
37mod sampling;
38mod sign;
39
40use arithmetic::power2round_polyvec;
42use polyvec::{expand_matrix_a, matrix_polyvecl_mul};
43
44use encoding::{unpack_public_key, unpack_secret_key, unpack_signature};
46
47use dcrypt_params::pqc::dilithium::{
51 Dilithium2Params, Dilithium3Params, Dilithium5Params, DilithiumSchemeParams,
52};
53
54#[derive(Clone, Debug, Zeroize)]
65pub struct DilithiumPublicKey(pub(crate) Vec<u8>);
66
67#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
72pub struct DilithiumSecretKey(Vec<u8>);
73
74#[derive(Clone, Debug)]
81pub struct DilithiumSignatureData(pub(crate) Vec<u8>);
82
83impl AsRef<[u8]> for DilithiumPublicKey {
85 fn as_ref(&self) -> &[u8] {
86 &self.0
87 }
88}
89impl AsMut<[u8]> for DilithiumPublicKey {
90 fn as_mut(&mut self) -> &mut [u8] {
91 &mut self.0
92 }
93}
94impl AsRef<[u8]> for DilithiumSignatureData {
95 fn as_ref(&self) -> &[u8] {
96 &self.0
97 }
98}
99impl AsMut<[u8]> for DilithiumSignatureData {
100 fn as_mut(&mut self) -> &mut [u8] {
101 &mut self.0
102 }
103}
104
105impl AsRef<[u8]> for DilithiumSecretKey {
108 fn as_ref(&self) -> &[u8] {
109 &self.0
110 }
111}
112
113impl DilithiumSecretKey {
117 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignError> {
122 match bytes.len() {
124 2560 => {} 4032 => {} 4896 => {} _ => {
128 return Err(SignError::Deserialization(format!(
129 "Invalid FIPS 204 secret key size: {} bytes",
130 bytes.len()
131 )))
132 }
133 };
134
135 match bytes.len() {
137 2560 => {
138 let _ = unpack_secret_key::<Dilithium2Params>(bytes)?;
139 }
140 4032 => {
141 let _ = unpack_secret_key::<Dilithium3Params>(bytes)?;
142 }
143 4896 => {
144 let _ = unpack_secret_key::<Dilithium5Params>(bytes)?;
145 }
146 _ => unreachable!(),
147 }
148
149 Ok(Self(bytes.to_vec()))
150 }
151
152 pub fn to_bytes(&self) -> &[u8] {
154 &self.0
155 }
156
157 pub fn public_key(&self) -> Result<DilithiumPublicKey, SignError> {
159 match self.0.len() {
160 2560 => {
161 let (rho, _, _, s1, s2, _) = unpack_secret_key::<Dilithium2Params>(&self.0)?;
162 let pk_bytes = reconstruct_public_key::<Dilithium2Params>(&rho, &s1, &s2)?;
163 Ok(DilithiumPublicKey(pk_bytes))
164 }
165 4032 => {
166 let (rho, _, _, s1, s2, _) = unpack_secret_key::<Dilithium3Params>(&self.0)?;
167 let pk_bytes = reconstruct_public_key::<Dilithium3Params>(&rho, &s1, &s2)?;
168 Ok(DilithiumPublicKey(pk_bytes))
169 }
170 4896 => {
171 let (rho, _, _, s1, s2, _) = unpack_secret_key::<Dilithium5Params>(&self.0)?;
172 let pk_bytes = reconstruct_public_key::<Dilithium5Params>(&rho, &s1, &s2)?;
173 Ok(DilithiumPublicKey(pk_bytes))
174 }
175 _ => unreachable!(),
176 }
177 }
178}
179
180fn reconstruct_public_key<P: DilithiumSchemeParams>(
182 rho: &[u8; 32],
183 s1: &polyvec::PolyVecL<P>,
184 s2: &polyvec::PolyVecK<P>,
185) -> Result<Vec<u8>, SignError> {
186 let matrix_a = expand_matrix_a::<P>(rho)?;
188
189 let mut matrix_a_hat = Vec::with_capacity(P::K_DIM);
191 for row in matrix_a {
192 let mut row_ntt = row;
193 row_ntt.ntt_inplace().map_err(SignError::from_algo)?;
194 matrix_a_hat.push(row_ntt);
195 }
196
197 let mut s1_hat = s1.clone();
198 s1_hat.ntt_inplace().map_err(SignError::from_algo)?;
199
200 let mut s2_hat = s2.clone();
201 s2_hat.ntt_inplace().map_err(SignError::from_algo)?;
202
203 let mut t_hat = matrix_polyvecl_mul(&matrix_a_hat, &s1_hat);
205 t_hat = t_hat.add(&s2_hat);
206
207 let mut t = t_hat;
209 t.inv_ntt_inplace().map_err(SignError::from_algo)?;
210
211 let (_, t1) = power2round_polyvec(&t, P::D_PARAM);
213
214 encoding::pack_public_key::<P>(rho, &t1)
216}
217
218impl DilithiumPublicKey {
221 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignError> {
223 match bytes.len() {
225 n if n == Dilithium2Params::PUBLIC_KEY_BYTES => {
226 let _ = unpack_public_key::<Dilithium2Params>(bytes)?;
227 }
228 n if n == Dilithium3Params::PUBLIC_KEY_BYTES => {
229 let _ = unpack_public_key::<Dilithium3Params>(bytes)?;
230 }
231 n if n == Dilithium5Params::PUBLIC_KEY_BYTES => {
232 let _ = unpack_public_key::<Dilithium5Params>(bytes)?;
233 }
234 _ => {
235 return Err(SignError::Deserialization(format!(
236 "Invalid public key size: {} bytes",
237 bytes.len()
238 )))
239 }
240 }
241
242 Ok(Self(bytes.to_vec()))
243 }
244
245 pub fn to_bytes(&self) -> &[u8] {
247 &self.0
248 }
249}
250
251impl DilithiumSignatureData {
252 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignError> {
254 match bytes.len() {
256 n if n == Dilithium2Params::SIGNATURE_SIZE => {
257 let _ = unpack_signature::<Dilithium2Params>(bytes)?;
258 }
259 n if n == Dilithium3Params::SIGNATURE_SIZE => {
260 let _ = unpack_signature::<Dilithium3Params>(bytes)?;
261 }
262 n if n == Dilithium5Params::SIGNATURE_SIZE => {
263 let _ = unpack_signature::<Dilithium5Params>(bytes)?;
264 }
265 _ => {
266 return Err(SignError::Deserialization(format!(
267 "Invalid signature size: {} bytes",
268 bytes.len()
269 )))
270 }
271 }
272
273 Ok(Self(bytes.to_vec()))
274 }
275
276 pub fn to_bytes(&self) -> &[u8] {
278 &self.0
279 }
280}
281
282pub struct Dilithium<P: DilithiumSchemeParams + 'static> {
284 _params: PhantomData<P>,
285}
286
287impl<P: DilithiumSchemeParams + Send + Sync + 'static> SignatureTrait for Dilithium<P> {
289 type PublicKey = DilithiumPublicKey;
290 type SecretKey = DilithiumSecretKey;
291 type SignatureData = DilithiumSignatureData;
292 type KeyPair = (Self::PublicKey, Self::SecretKey);
293
294 fn name() -> &'static str {
295 P::NAME
296 }
297
298 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
299 let (pk_bytes, sk_bytes) =
300 sign::keypair_internal::<P, R>(rng).map_err(dcrypt_api::Error::from)?;
301 let sk = DilithiumSecretKey::from_bytes(&sk_bytes).map_err(dcrypt_api::Error::from)?;
302 Ok((DilithiumPublicKey(pk_bytes), sk))
303 }
304
305 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
306 keypair.0.clone()
307 }
308 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
309 keypair.1.clone()
310 }
311
312 fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
313 let mut rng = rand::rngs::OsRng;
314 let sig_bytes = sign::sign_internal::<P, _>(message, &secret_key.0, &mut rng)
315 .map_err(dcrypt_api::Error::from)?;
316 Ok(DilithiumSignatureData(sig_bytes))
317 }
318
319 fn verify(
320 message: &[u8],
321 signature: &Self::SignatureData,
322 public_key: &Self::PublicKey,
323 ) -> ApiResult<()> {
324 sign::verify_internal::<P>(message, &signature.0, &public_key.0)
325 .map_err(dcrypt_api::Error::from)
326 }
327}
328
329pub type Dilithium2 = Dilithium<Dilithium2Params>;
331pub type Dilithium3 = Dilithium<Dilithium3Params>;
332pub type Dilithium5 = Dilithium<Dilithium5Params>;
333
334#[cfg(test)]
335mod tests;