libcrux_rsa/
impl_hacl.rs

1use crate::Error;
2
3/// An RSA Public Key that is `LEN` bytes long.
4#[derive(Debug, Clone)]
5pub struct PublicKey<const LEN: usize> {
6    n: [u8; LEN],
7}
8
9/// An RSA Private Key that is `LEN` bytes long.
10pub struct PrivateKey<const LEN: usize> {
11    pk: PublicKey<LEN>,
12    d: [u8; LEN],
13}
14
15impl<const LEN: usize> alloc::fmt::Debug for PrivateKey<LEN> {
16    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17        f.debug_struct("PrivateKey")
18            .field("pk", &self.pk)
19            .field("d", &"****")
20            .finish()
21    }
22}
23
24/// An RSA Public Key backed by a slice. Use if the length is not known at compile time.
25#[derive(Debug, Clone)]
26pub struct VarLenPublicKey<'a> {
27    n: &'a [u8],
28}
29
30impl<'a> TryFrom<&'a [u8]> for VarLenPublicKey<'a> {
31    type Error = Error;
32
33    fn try_from(n: &'a [u8]) -> Result<Self, Error> {
34        match n.len() {
35            256 | 384 | 512 | 768 | 1024 => Ok(Self { n }),
36            _ => Err(Error::InvalidKeyLength),
37        }
38    }
39}
40
41impl VarLenPublicKey<'_> {
42    /// Returns the length of the key
43    pub fn key_len(&self) -> usize {
44        self.n.len()
45    }
46
47    /// Returns the modulus
48    pub fn n(&self) -> &[u8] {
49        self.n
50    }
51}
52impl alloc::fmt::Debug for VarLenPrivateKey<'_> {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        f.debug_struct("PrivateKey")
55            .field("pk", &self.pk)
56            .field("d", &"****")
57            .finish()
58    }
59}
60
61/// An RSA Private Key backed by slices. Use if the length is not known at compile time.
62pub struct VarLenPrivateKey<'a> {
63    pk: VarLenPublicKey<'a>,
64    d: &'a [u8],
65}
66
67impl<'a> VarLenPrivateKey<'a> {
68    /// Constructor for the private key based on `n` and `d`.
69    pub fn from_components(n: &'a [u8], d: &'a [u8]) -> Result<Self, Error> {
70        if n.len() != d.len() {
71            return Err(Error::KeyLengthMismatch);
72        }
73
74        Ok(Self {
75            pk: n.try_into()?,
76            d,
77        })
78    }
79
80    /// Returns the public key of the private key.
81    pub fn pk(&self) -> &VarLenPublicKey {
82        &self.pk
83    }
84
85    /// Returns the length of the keys
86    pub fn key_len(&self) -> usize {
87        self.d.len()
88    }
89
90    /// Returns the private exponent of the keys
91    pub fn d(&self) -> &[u8] {
92        self.d
93    }
94}
95
96const E_BITS: u32 = 17;
97const E: [u8; 3] = [1, 0, 1];
98
99fn hacl_hash_alg(alg: crate::DigestAlgorithm) -> libcrux_hacl_rs::streaming_types::hash_alg {
100    match alg {
101        crate::DigestAlgorithm::Sha2_256 => libcrux_hacl_rs::streaming_types::hash_alg::SHA2_256,
102        crate::DigestAlgorithm::Sha2_384 => libcrux_hacl_rs::streaming_types::hash_alg::SHA2_384,
103        crate::DigestAlgorithm::Sha2_512 => libcrux_hacl_rs::streaming_types::hash_alg::SHA2_512,
104    }
105}
106
107// next up: generate these in macros
108
109macro_rules! impl_rsapss {
110    ($sign_fn:ident, $verify_fn:ident, $bits:literal, $bytes:literal) => {
111        impl From<[u8; $bytes]> for PublicKey<$bytes> {
112            fn from(n: [u8; $bytes]) -> Self {
113                Self { n }
114            }
115        }
116
117        impl PublicKey<$bytes> {
118            /// Returns the slice-based public-key
119            pub fn as_var_len(&self) -> VarLenPublicKey<'_> {
120                VarLenPublicKey { n: &self.n }
121            }
122
123            /// Returns the modulus as bytes.
124            pub fn n(&self) -> &[u8; $bytes] {
125                &self.n
126            }
127        }
128
129        impl PrivateKey<$bytes> {
130            /// Constructor for the private key based on `n` and `d`.
131            pub fn from_components(n: [u8; $bytes], d: [u8; $bytes]) -> Self {
132                Self { pk: n.into(), d }
133            }
134
135            /// Returns the public key of the private key.
136            pub fn pk(&self) -> &PublicKey<$bytes> {
137                &self.pk
138            }
139
140            /// Returns the slice-based private key
141            pub fn as_var_len(&self) -> VarLenPrivateKey<'_> {
142                VarLenPrivateKey {
143                    pk: self.pk.as_var_len(),
144                    d: &self.d,
145                }
146            }
147
148            /// Returns the private exponent as bytes.
149            pub fn d(&self) -> &[u8; $bytes] {
150                &self.d
151            }
152        }
153
154        impl<'a> From<&'a PublicKey<$bytes>> for VarLenPublicKey<'a> {
155            fn from(value: &'a PublicKey<$bytes>) -> Self {
156                value.as_var_len()
157            }
158        }
159
160        impl<'a> From<&'a PrivateKey<$bytes>> for VarLenPrivateKey<'a> {
161            fn from(value: &'a PrivateKey<$bytes>) -> Self {
162                value.as_var_len()
163            }
164        }
165
166        /// Computes a signature over `msg` using `sk` and writes it to `sig`.
167        /// Returns `Ok(())` on success.
168        ///
169        /// Returns an error in any of the following cases:
170        /// - the secret key is invalid
171        /// - the length of `msg` exceeds `u32::MAX`
172        /// - `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
173        pub fn $sign_fn(
174            alg: crate::DigestAlgorithm,
175            sk: &PrivateKey<$bytes>,
176            msg: &[u8],
177            salt: &[u8],
178            sig: &mut [u8; $bytes],
179        ) -> Result<(), Error> {
180            sign_varlen(alg, &sk.into(), msg, salt, sig)
181        }
182
183        /// Returns `Ok(())` if the provided signature is valid.
184        ///
185        /// Returns an error in any of the following cases:
186        /// - the public key is invalid
187        /// - the signature verification fails
188        /// - the length of `msg` exceeds `u32::MAX`
189        /// - `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
190        pub fn $verify_fn(
191            alg: crate::DigestAlgorithm,
192            pk: &PublicKey<$bytes>,
193            msg: &[u8],
194            salt_len: u32,
195            sig: &[u8; $bytes],
196        ) -> Result<(), Error> {
197            verify_varlen(alg, &pk.into(), msg, salt_len, sig)
198        }
199    };
200}
201
202impl_rsapss!(sign_2048, verify_2048, 2048, 256);
203impl_rsapss!(sign_3072, verify_3072, 3072, 384);
204impl_rsapss!(sign_4096, verify_4096, 4096, 512);
205impl_rsapss!(sign_6144, verify_6144, 6144, 768);
206impl_rsapss!(sign_8192, verify_8192, 8192, 1024);
207
208/// Computes a signature over `msg` using `sk` and writes it to `sig`.
209/// Returns `Ok(())` on success.
210///
211/// Returns an error in any of the following cases:
212/// - the secret key is invalid
213/// - the length of `msg` exceeds `u32::MAX`
214/// - `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
215/// - the length of `sig` does not match the length of `sk`
216pub fn sign(
217    alg: crate::DigestAlgorithm,
218    sk: &VarLenPrivateKey<'_>,
219    msg: &[u8],
220    salt: &[u8],
221    sig: &mut [u8],
222) -> Result<(), Error> {
223    if sig.len() != sk.key_len() {
224        return Err(Error::InvalidSignatureLength);
225    }
226
227    sign_varlen(alg, sk, msg, salt, sig)
228}
229
230/// Returns `Ok(())` if the provided signature is valid.
231///
232/// Returns an error in any of the following cases:
233/// - the public key is invalid
234/// - the signature verification fails
235/// - the length of `msg` exceeds `u32::MAX`
236/// - `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
237pub fn verify(
238    alg: crate::DigestAlgorithm,
239    pk: &VarLenPublicKey<'_>,
240    msg: &[u8],
241    salt_len: u32,
242    sig: &[u8],
243) -> Result<(), Error> {
244    if pk.key_len() != sig.len() {
245        return Err(Error::InvalidSignatureLength);
246    }
247
248    verify_varlen(alg, pk, msg, salt_len, sig)
249}
250
251/// Computes a signature over `msg` using `sk` and writes it to `sig`.
252/// Returns `Ok(())` on success.
253///
254/// Returns an error in any of the following cases:
255/// - `Error::SigningFailed`: the secret key is invalid
256/// - `Error::MessageTooLarge`: the length of `msg` exceeds `u32::MAX`
257/// - `Error::SaltTooLarge`: `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
258///
259/// Ensures that the preconditions to hacl functions hold:
260///
261/// - `sLen + Hash.hash_length a + 8 <= max_size_t`
262///   - checked explicitly
263/// - `(sLen + Hash.hash_length a + 8) less_than_max_input_length a`
264///   - `max_input_length` is at least `2^62 - 1`, can't be reached since everyting
265///     is less than `u32::MAX`.
266/// - `sLen + Hash.hash_length a + 2 <= blocks (modBits - 1) 8`
267///   - `blocks a b = (a - 1) / b + 1`, i.e. `ceil(div(a, b))`
268///   - checked explicitly
269/// - `msgLen `less_than_max_input_length` a`
270///   - follows from the check that messages are shorter than `u32::MAX`.
271pub fn sign_varlen(
272    alg: crate::DigestAlgorithm,
273    sk: &VarLenPrivateKey<'_>,
274    msg: &[u8],
275    salt: &[u8],
276    sig: &mut [u8],
277) -> Result<(), Error> {
278    let salt_len = salt.len().try_into().map_err(|_| Error::SaltTooLarge)?;
279    let msg_len = msg.len().try_into().map_err(|_| Error::MessageTooLarge)?;
280
281    // required by precondition to verify, see
282    // https://github.com/hacl-star/hacl-star/blob/efbf82f29190e2aecdac8899e4f42c8cb9defc98/code/rsapss/Hacl.Spec.RSAPSS.fst#L162
283    // all operands are at most u32, so coercing to u64 and then adding is safe.
284    if (salt_len as u64) + alg.hash_len() as u64 + 8 > u32::MAX as u64 {
285        return Err(Error::SaltTooLarge);
286    }
287
288    let a = hacl_hash_alg(alg);
289    let mod_bits = sk.pk.n.len() as u32 * 8;
290    let e_bits = E_BITS;
291    let d_bits = sk.d.len() as u32 * 8;
292
293    // required by precondition to verify, see
294    // https://github.com/hacl-star/hacl-star/blob/efbf82f29190e2aecdac8899e4f42c8cb9defc98/code/rsapss/Hacl.Spec.RSAPSS.fst#L164
295    // all operands are at most u32, so coercing to u64 and then adding is safe.
296    if salt_len as u64 + alg.hash_len() as u64 + 2 > (mod_bits as u64 - 1) / 8 + 1 {
297        return Err(Error::SaltTooLarge);
298    }
299
300    match crate::hacl::rsapss::rsapss_skey_sign(
301        a, mod_bits, e_bits, d_bits, sk.pk.n, &E, sk.d, salt_len, salt, msg_len, msg, sig,
302    ) {
303        true => Ok(()),
304        false => Err(Error::SigningFailed),
305    }
306}
307
308/// Returns `Ok(())` if the provided signature is valid.
309///
310/// Returns an error in any of the following cases:
311/// - the public key is invalid
312/// - the signature verification fails
313/// - the length of `msg` exceeds `u32::MAX`
314/// - `salt_len` exceeds `u32::MAX - alg.hash_len() - 8`
315///
316/// Ensures that the preconditions to hacl functions hold:
317///
318/// - `sLen + Hash.hash_length a + 8 <= max_size_t`
319///   - checked explicitly
320/// - `(sLen + Hash.hash_length a + 8) less_than_max_input_length a`
321///   - `max_input_length` is at least `2^62 - 1`, can't be reached since everyting
322///     is less than `u32::MAX`.
323/// - `msgLen less_than_max_input_length a`
324///   - follows from the check that messages are shorter than `u32::MAX`.
325pub fn verify_varlen(
326    alg: crate::DigestAlgorithm,
327    pk: &VarLenPublicKey<'_>,
328    msg: &[u8],
329    salt_len: u32,
330    sig: &[u8],
331) -> Result<(), Error> {
332    // required by precondition to verify, see
333    // https://github.com/hacl-star/hacl-star/blob/efbf82f29190e2aecdac8899e4f42c8cb9defc98/code/rsapss/Hacl.Spec.RSAPSS.fst#L236
334    // all operands are at most u32, so coercing to u64 and then adding is safe.
335    if (salt_len as u64) + alg.hash_len() as u64 + 8 > u32::MAX as u64 {
336        return Err(Error::SaltTooLarge);
337    }
338
339    let msg_len = msg.len().try_into().map_err(|_| Error::MessageTooLarge)?;
340
341    let a = hacl_hash_alg(alg);
342    let mod_bits = pk.n.len() as u32 * 8;
343    let e_bits = E_BITS;
344
345    match crate::hacl::rsapss::rsapss_pkey_verify(
346        a,
347        mod_bits,
348        e_bits,
349        pk.n,
350        &E,
351        salt_len,
352        sig.len() as u32, /*signature length*/
353        sig,
354        msg_len,
355        msg,
356    ) {
357        true => Ok(()),
358        false => Err(Error::VerificationFailed),
359    }
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366    const MODULUS: [u8; 256] = [
367        0xd2, 0x78, 0x16, 0xcb, 0x72, 0xbb, 0x6e, 0x27, 0xdb, 0x10, 0x1a, 0x6f, 0x3e, 0x64, 0x62,
368        0x93, 0xd9, 0xec, 0xa7, 0xb3, 0x98, 0xe3, 0x36, 0x6c, 0x9e, 0x69, 0x31, 0xc4, 0x5d, 0xd7,
369        0x24, 0xd3, 0xf8, 0x90, 0xb0, 0xd0, 0x57, 0x78, 0x3e, 0xdd, 0xee, 0xf0, 0xc9, 0x0e, 0x98,
370        0x6d, 0xad, 0xe9, 0x46, 0x47, 0xc5, 0xcb, 0x4d, 0xa4, 0xc6, 0x9c, 0x83, 0x1a, 0x13, 0x9f,
371        0xb7, 0x8d, 0xe7, 0xe3, 0x79, 0x97, 0xf2, 0x9e, 0x36, 0x5c, 0x96, 0xaa, 0xf6, 0x29, 0xfe,
372        0x6e, 0x3c, 0x0d, 0xb0, 0xcb, 0x04, 0x7d, 0x35, 0xd3, 0xeb, 0xf7, 0xee, 0x36, 0x59, 0xda,
373        0xb5, 0xb2, 0x34, 0x08, 0x86, 0x87, 0x27, 0x02, 0x4b, 0x49, 0xb3, 0x85, 0x33, 0x9b, 0x63,
374        0x8f, 0x28, 0x3b, 0x27, 0x83, 0x65, 0xf9, 0x62, 0x23, 0xe0, 0x8b, 0x15, 0x1d, 0xd3, 0x00,
375        0xb1, 0xd6, 0x37, 0x3e, 0x7b, 0xa7, 0x1d, 0xc7, 0x63, 0x79, 0xe2, 0xa2, 0xca, 0x2d, 0xa4,
376        0xb6, 0xcd, 0xef, 0x8d, 0x73, 0xec, 0x56, 0xfc, 0x0b, 0xac, 0xcb, 0x80, 0x53, 0xcf, 0x34,
377        0x2f, 0x29, 0xb0, 0xe7, 0xf0, 0xb9, 0x24, 0xf4, 0xe4, 0x99, 0xb2, 0x58, 0xc0, 0x9e, 0x1f,
378        0xf5, 0x43, 0x6e, 0xca, 0xc6, 0xeb, 0x65, 0xd0, 0x5f, 0xdb, 0x13, 0x4c, 0x8c, 0xca, 0x82,
379        0xd9, 0xad, 0xc1, 0xfd, 0x7a, 0xd9, 0x78, 0xc7, 0xed, 0xdf, 0xc9, 0x70, 0x54, 0xd3, 0x80,
380        0x5f, 0x06, 0x48, 0x11, 0x6e, 0xfb, 0x9b, 0x46, 0xfa, 0x02, 0x65, 0xde, 0xcc, 0xe9, 0x6e,
381        0x91, 0x98, 0x93, 0x3d, 0x3d, 0x6d, 0xb1, 0x99, 0xa4, 0x73, 0xc1, 0x2c, 0xa2, 0x16, 0x55,
382        0x97, 0xf3, 0x0f, 0x67, 0xf7, 0x9a, 0x78, 0x74, 0x15, 0x66, 0xb1, 0xd4, 0xdc, 0x98, 0x47,
383        0x8a, 0x50, 0xb6, 0x2d, 0x63, 0xf9, 0xce, 0xa2, 0x76, 0x70, 0x91, 0xa8, 0x3b, 0x00, 0x28,
384        0x01,
385    ];
386
387    const PRIVATE_EXPONENT: [u8; 256] = [
388        0x5a, 0x90, 0x21, 0xfe, 0xd9, 0x17, 0x9d, 0x86, 0xb8, 0xd4, 0x6d, 0x0b, 0x81, 0x25, 0x60,
389        0xe5, 0x8d, 0xd8, 0x2f, 0x31, 0x30, 0x90, 0x54, 0x52, 0xd8, 0xb7, 0x1b, 0x1b, 0x0b, 0xe6,
390        0x0f, 0x8a, 0xc6, 0x62, 0x3c, 0x32, 0xe9, 0xf0, 0x6b, 0xdc, 0xc3, 0x7c, 0x08, 0x87, 0xa7,
391        0x3f, 0x4a, 0x9e, 0x1e, 0x07, 0xb4, 0x2c, 0x8e, 0xf4, 0x60, 0x21, 0xe8, 0xa7, 0xc7, 0xd9,
392        0xe9, 0xf9, 0xbd, 0xd6, 0x3b, 0xf4, 0x0e, 0x09, 0xd6, 0x0a, 0x71, 0x2a, 0x8f, 0x51, 0xf2,
393        0x91, 0x2c, 0x76, 0x17, 0xa4, 0xc4, 0x01, 0xbc, 0xaf, 0xbb, 0xd1, 0xab, 0x46, 0xe7, 0xd3,
394        0x1c, 0x6b, 0xd9, 0xc7, 0xf1, 0x5b, 0x26, 0x85, 0xee, 0x2f, 0x80, 0x77, 0xc8, 0x85, 0x0c,
395        0x8a, 0x05, 0x1d, 0xaf, 0x1a, 0xf3, 0x3e, 0x23, 0xe4, 0x9c, 0x32, 0x3c, 0x9b, 0xe0, 0xb7,
396        0x63, 0xce, 0x71, 0x67, 0x09, 0x7e, 0x17, 0x69, 0x74, 0x9a, 0xec, 0x2a, 0x71, 0xf4, 0xeb,
397        0xe2, 0x84, 0x23, 0x8b, 0xa8, 0x27, 0x69, 0x19, 0x53, 0x52, 0x8f, 0xc3, 0x62, 0xd5, 0x2a,
398        0x43, 0xb0, 0x78, 0x90, 0x54, 0x98, 0x22, 0x12, 0x2d, 0x32, 0x28, 0xcf, 0xf9, 0x04, 0x1c,
399        0x4f, 0x28, 0xb7, 0xad, 0x98, 0x1a, 0xdf, 0x2e, 0xdb, 0x94, 0xd5, 0x3d, 0xe2, 0xa9, 0x29,
400        0x3c, 0x3e, 0xaa, 0x81, 0x2a, 0x61, 0x8d, 0x4b, 0x41, 0x2f, 0xda, 0x99, 0x8b, 0x78, 0x7a,
401        0xd5, 0xec, 0x93, 0x53, 0x5a, 0x84, 0x43, 0x47, 0x1a, 0xaf, 0x68, 0xa7, 0x5f, 0x4e, 0x62,
402        0xe5, 0xcf, 0x07, 0xc9, 0x2b, 0x67, 0x34, 0x82, 0x27, 0xf6, 0xe0, 0x6d, 0x51, 0xca, 0x21,
403        0xea, 0xfa, 0x32, 0xf0, 0x9f, 0x84, 0xb4, 0xfb, 0xaf, 0x25, 0x1e, 0x91, 0x08, 0x94, 0x5e,
404        0x83, 0x7f, 0x0f, 0x6a, 0x86, 0x98, 0x77, 0xb8, 0xb0, 0xca, 0xd0, 0x34, 0x10, 0x69, 0x59,
405        0x21,
406    ];
407
408    #[test]
409    fn self_test_rsa_pss() {
410        let pk = PublicKey { n: MODULUS };
411        let sk = PrivateKey {
412            pk: pk.clone(),
413            d: PRIVATE_EXPONENT,
414        };
415        let salt = [1, 2, 3, 4, 5];
416        let msg = [7, 8, 9, 10];
417        let mut signature = [0u8; 256];
418        sign_2048(
419            crate::DigestAlgorithm::Sha2_256,
420            &sk,
421            &msg,
422            &salt,
423            &mut signature,
424        )
425        .unwrap();
426        verify_2048(
427            crate::DigestAlgorithm::Sha2_256,
428            &pk,
429            &msg,
430            salt.len() as u32,
431            &signature,
432        )
433        .expect("Error verifying signature");
434    }
435}