Skip to main content

noxtls_crypto/pkc/primitive/
rsa.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18use crate::drbg::HmacDrbgSha256;
19use crate::hash::{noxtls_sha1, noxtls_sha256, noxtls_sha384, noxtls_sha512};
20use crate::internal_alloc::Vec;
21use noxtls_core::{Error, Result};
22
23use super::bignum::BigUint;
24
25const RSA_KEYGEN_MIN_BITS: usize = 1024;
26const RSA_KEYGEN_MAX_BITS: usize = 4096;
27const RSA_MIN_SECURE_BITS: usize = 2048;
28const RSA_RECOMMENDED_SECURE_BITS: usize = 3072;
29
30/// Represents an RSA private key with arbitrary-size modulus and exponent.
31#[derive(Debug, Clone)]
32pub struct RsaPrivateKey {
33    pub n: BigUint,
34    pub d: BigUint,
35    crt: Option<RsaPrivateCrtComponents>,
36}
37
38/// Represents an RSA public key with arbitrary-size modulus and exponent.
39#[derive(Debug, Clone)]
40pub struct RsaPublicKey {
41    pub n: BigUint,
42    pub e: BigUint,
43}
44
45/// Stores optional RSA CRT decomposition parameters for accelerated private operations.
46#[derive(Debug, Clone)]
47struct RsaPrivateCrtComponents {
48    p: BigUint,
49    q: BigUint,
50    dp: BigUint,
51    dq: BigUint,
52    qinv: BigUint,
53}
54
55/// Defines secure RSA key-size policy thresholds for safe key-generation entry points.
56#[derive(Debug, Copy, Clone, Eq, PartialEq)]
57pub enum RsaPssHashAlgorithm {
58    Sha1,
59    Sha256,
60    Sha384,
61    Sha512,
62}
63
64#[derive(Debug, Copy, Clone, Eq, PartialEq)]
65pub enum RsaKeySizePolicy {
66    /// Requires at least 2048-bit RSA modulus length.
67    Minimum2048,
68    /// Requires at least 3072-bit RSA modulus length.
69    Minimum3072,
70}
71
72impl RsaKeySizePolicy {
73    /// Returns the minimum RSA modulus size in bits for this policy.
74    ///
75    /// # Arguments
76    ///
77    /// * `self` — Selected policy variant.
78    ///
79    /// # Returns
80    ///
81    /// Minimum modulus bit length required for key generation.
82    ///
83    /// # Panics
84    ///
85    /// This function does not panic.
86    fn min_bits(self) -> usize {
87        match self {
88            Self::Minimum2048 => RSA_MIN_SECURE_BITS,
89            Self::Minimum3072 => RSA_RECOMMENDED_SECURE_BITS,
90        }
91    }
92}
93
94impl RsaPrivateKey {
95    /// Creates private key from big-endian modulus and private exponent bytes.
96    ///
97    /// # Arguments
98    /// * `n`: RSA modulus encoded as big-endian bytes.
99    /// * `d`: RSA private exponent encoded as big-endian bytes.
100    ///
101    /// # Returns
102    /// Parsed `RsaPrivateKey` when both fields are non-empty.
103    ///
104    /// # Errors
105    ///
106    /// Returns [`Error::InvalidLength`] when fields are empty, or when modulus size is below
107    /// 2048 bits in default-safe builds (legacy-compatible hazardous mode permits smaller imports),
108    /// or other RSA component validation errors from [`validate_private_components`].
109    pub fn from_be_bytes(n: &[u8], d: &[u8]) -> Result<Self> {
110        if n.is_empty() || d.is_empty() {
111            return Err(Error::InvalidLength(
112                "rsa private key fields must not be empty",
113            ));
114        }
115        let key = Self {
116            n: BigUint::from_be_bytes(n),
117            d: BigUint::from_be_bytes(d),
118            crt: None,
119        };
120        if !cfg!(feature = "hazardous-legacy-crypto") && key.n.bit_len() < RSA_MIN_SECURE_BITS {
121            return Err(Error::InvalidLength(
122                "rsa private key modulus must be at least 2048 bits",
123            ));
124        }
125        validate_private_components(&key.n, &key.d)?;
126        Ok(key)
127    }
128
129    /// Creates private key from small integers for compatibility tests.
130    ///
131    /// # Arguments
132    /// * `n`: RSA modulus value.
133    /// * `d`: RSA private exponent value.
134    ///
135    /// # Returns
136    /// `RsaPrivateKey` converted from the provided integer values.
137    #[must_use]
138    pub fn from_u128(n: u128, d: u128) -> Self {
139        Self {
140            n: BigUint::from_u128(n),
141            d: BigUint::from_u128(d),
142            crt: None,
143        }
144    }
145
146    /// Clears private key material to a zeroized placeholder state.
147    ///
148    /// # Notes
149    /// This mirrors explicit key free/reset lifecycle flows from the C surface.
150    pub fn clear(&mut self) {
151        self.n.clear();
152        self.d.clear();
153        if let Some(crt) = self.crt.as_mut() {
154            crt.p.clear();
155            crt.q.clear();
156            crt.dp.clear();
157            crt.dq.clear();
158            crt.qinv.clear();
159        }
160        self.crt = None;
161    }
162
163    /// Attaches RSA CRT decomposition components to this private key.
164    ///
165    /// # Arguments
166    /// * `p`: First RSA prime factor.
167    /// * `q`: Second RSA prime factor.
168    /// * `dp`: `d mod (p - 1)` CRT exponent.
169    /// * `dq`: `d mod (q - 1)` CRT exponent.
170    /// * `qinv`: `q^{-1} mod p` CRT coefficient.
171    ///
172    /// # Returns
173    /// Updated private key configured with CRT components.
174    pub fn with_crt_components(
175        mut self,
176        p: &[u8],
177        q: &[u8],
178        dp: &[u8],
179        dq: &[u8],
180        qinv: &[u8],
181    ) -> Result<Self> {
182        let crt = RsaPrivateCrtComponents {
183            p: BigUint::from_be_bytes(p),
184            q: BigUint::from_be_bytes(q),
185            dp: BigUint::from_be_bytes(dp),
186            dq: BigUint::from_be_bytes(dq),
187            qinv: BigUint::from_be_bytes(qinv),
188        };
189        validate_crt_components(&self.n, &crt)?;
190        self.crt = Some(crt);
191        Ok(self)
192    }
193
194    /// Signs a representative digest interpreted as big-endian integer modulo `n`.
195    ///
196    /// # Arguments
197    /// * `digest`: Digest bytes to convert into an RSA message representative.
198    ///
199    /// # Returns
200    /// Signature bytes padded to modulus length.
201    pub fn sign_digest(&self, digest: &[u8]) -> Result<Vec<u8>> {
202        if digest.is_empty() {
203            return Err(Error::InvalidLength("digest must not be empty"));
204        }
205        validate_private_components(&self.n, &self.d)?;
206        let m = BigUint::from_be_bytes(digest).modulo(&self.n);
207        let s = BigUint::mod_exp(&m, &self.d, &self.n);
208        s.to_be_bytes_padded(self.modulus_len())
209    }
210
211    /// Signs a message using RSASSA-PKCS1-v1_5 style encoding with SHA-256.
212    ///
213    /// # Arguments
214    /// * `msg`: Message bytes to hash and sign.
215    ///
216    /// # Returns
217    /// PKCS#1 v1.5 RSA signature bytes.
218    pub fn sign_pkcs1_v15_sha256(&self, msg: &[u8]) -> Result<Vec<u8>> {
219        validate_private_components(&self.n, &self.d)?;
220        let hash = noxtls_sha256(msg);
221        let em = emsa_pkcs1_v15_encode(
222            &hash,
223            PKCS1_V15_DIGESTINFO_SHA256_PREFIX,
224            self.modulus_len(),
225        )?;
226        let m = BigUint::from_be_bytes(&em);
227        let s = BigUint::mod_exp(&m, &self.d, &self.n);
228        s.to_be_bytes_padded(self.modulus_len())
229    }
230
231    /// Signs a message using RSASSA-PKCS1-v1_5 style encoding with SHA-1.
232    ///
233    /// # Arguments
234    /// * `msg`: Message bytes to hash and sign.
235    ///
236    /// # Returns
237    /// PKCS#1 v1.5 RSA signature bytes.
238    pub fn sign_pkcs1_v15_sha1(&self, msg: &[u8]) -> Result<Vec<u8>> {
239        validate_private_components(&self.n, &self.d)?;
240        let hash = noxtls_sha1(msg);
241        let em =
242            emsa_pkcs1_v15_encode(&hash, PKCS1_V15_DIGESTINFO_SHA1_PREFIX, self.modulus_len())?;
243        let m = BigUint::from_be_bytes(&em);
244        let s = BigUint::mod_exp(&m, &self.d, &self.n);
245        s.to_be_bytes_padded(self.modulus_len())
246    }
247
248    /// Signs a message using RSASSA-PKCS1-v1_5 style encoding with SHA-384.
249    ///
250    /// # Arguments
251    /// * `msg`: Message bytes to hash and sign.
252    ///
253    /// # Returns
254    /// PKCS#1 v1.5 RSA signature bytes.
255    pub fn sign_pkcs1_v15_sha384(&self, msg: &[u8]) -> Result<Vec<u8>> {
256        validate_private_components(&self.n, &self.d)?;
257        let hash = noxtls_sha384(msg);
258        let em = emsa_pkcs1_v15_encode(
259            &hash,
260            PKCS1_V15_DIGESTINFO_SHA384_PREFIX,
261            self.modulus_len(),
262        )?;
263        let m = BigUint::from_be_bytes(&em);
264        let s = BigUint::mod_exp(&m, &self.d, &self.n);
265        s.to_be_bytes_padded(self.modulus_len())
266    }
267
268    /// Signs a message using RSASSA-PKCS1-v1_5 style encoding with SHA-512.
269    ///
270    /// # Arguments
271    /// * `msg`: Message bytes to hash and sign.
272    ///
273    /// # Returns
274    /// PKCS#1 v1.5 RSA signature bytes.
275    pub fn sign_pkcs1_v15_sha512(&self, msg: &[u8]) -> Result<Vec<u8>> {
276        validate_private_components(&self.n, &self.d)?;
277        let hash = noxtls_sha512(msg);
278        let em = emsa_pkcs1_v15_encode(
279            &hash,
280            PKCS1_V15_DIGESTINFO_SHA512_PREFIX,
281            self.modulus_len(),
282        )?;
283        let m = BigUint::from_be_bytes(&em);
284        let s = BigUint::mod_exp(&m, &self.d, &self.n);
285        s.to_be_bytes_padded(self.modulus_len())
286    }
287
288    /// Signs a message using RSASSA-PSS with SHA-256 and caller-provided salt.
289    ///
290    /// # Arguments
291    /// * `msg`: Message bytes to hash and sign.
292    /// * `salt`: Caller-provided random salt used by PSS encoding.
293    ///
294    /// # Returns
295    /// RSASSA-PSS RSA signature bytes.
296    pub fn sign_pss_sha256(&self, msg: &[u8], salt: &[u8]) -> Result<Vec<u8>> {
297        validate_private_components(&self.n, &self.d)?;
298        let em_bits = self.n.bit_len().saturating_sub(1);
299        let em_len = em_bits.div_ceil(8);
300        let m_hash = noxtls_sha256(msg);
301        let em = emsa_pss_encode_sha256(&m_hash, salt, em_bits, em_len)?;
302        let s = BigUint::mod_exp(&BigUint::from_be_bytes(&em), &self.d, &self.n);
303        s.to_be_bytes_padded(self.modulus_len())
304    }
305
306    /// Signs a message using RSASSA-PSS with SHA-384 and caller-provided salt.
307    ///
308    /// # Arguments
309    /// * `msg`: Message bytes to hash and sign.
310    /// * `salt`: Caller-provided random salt used by PSS encoding.
311    ///
312    /// # Returns
313    /// RSASSA-PSS RSA signature bytes.
314    pub fn sign_pss_sha384(&self, msg: &[u8], salt: &[u8]) -> Result<Vec<u8>> {
315        validate_private_components(&self.n, &self.d)?;
316        let em_bits = self.n.bit_len().saturating_sub(1);
317        let em_len = em_bits.div_ceil(8);
318        let m_hash = noxtls_sha384(msg);
319        let em = emsa_pss_encode_sha384(&m_hash, salt, em_bits, em_len)?;
320        let s = BigUint::mod_exp(&BigUint::from_be_bytes(&em), &self.d, &self.n);
321        s.to_be_bytes_padded(self.modulus_len())
322    }
323
324    pub fn sign_pss_with_hashes(
325        &self,
326        msg: &[u8],
327        salt: &[u8],
328        message_hash: RsaPssHashAlgorithm,
329        mgf_hash: RsaPssHashAlgorithm,
330    ) -> Result<Vec<u8>> {
331        validate_private_components(&self.n, &self.d)?;
332        let em_bits = self.n.bit_len().saturating_sub(1);
333        let em_len = em_bits.div_ceil(8);
334        let m_hash = rsa_pss_hash(message_hash, msg);
335        let em = emsa_pss_encode(&m_hash, salt, em_bits, em_len, message_hash, mgf_hash)?;
336        let s = BigUint::mod_exp(&BigUint::from_be_bytes(&em), &self.d, &self.n);
337        s.to_be_bytes_padded(self.modulus_len())
338    }
339
340    /// Decrypts RSAES-PKCS1-v1_5 ciphertext with private exponent `d`.
341    ///
342    /// # Arguments
343    /// * `ciphertext`: Ciphertext bytes with length equal to modulus length.
344    ///
345    /// # Returns
346    /// Decrypted plaintext when PKCS#1 v1.5 structure is valid.
347    pub fn decrypt_pkcs1_v15(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
348        validate_private_components(&self.n, &self.d)?;
349        if ciphertext.len() != self.modulus_len() {
350            return Err(Error::CryptoFailure("rsa decryption failed"));
351        }
352        let em = BigUint::mod_exp(&BigUint::from_be_bytes(ciphertext), &self.d, &self.n)
353            .to_be_bytes_padded(self.modulus_len())?;
354        decode_pkcs1_v15_plaintext(&em)
355    }
356
357    /// Decrypts RSAES-PKCS1-v1_5 ciphertext using configured CRT components.
358    ///
359    /// # Arguments
360    /// * `ciphertext`: Ciphertext bytes with length equal to modulus length.
361    ///
362    /// # Returns
363    /// Decrypted plaintext when CRT components are configured and PKCS#1 v1.5 structure is valid.
364    pub fn decrypt_pkcs1_v15_crt_only(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
365        validate_private_components(&self.n, &self.d)?;
366        if ciphertext.len() != self.modulus_len() {
367            return Err(Error::CryptoFailure("rsa decryption failed"));
368        }
369        let crt = self
370            .crt
371            .as_ref()
372            .ok_or(Error::StateError("rsa crt parameters are not configured"))?;
373        let c = BigUint::from_be_bytes(ciphertext);
374        let m1 = BigUint::mod_exp(&c, &crt.dp, &crt.p);
375        let m2 = BigUint::mod_exp(&c, &crt.dq, &crt.q);
376        let diff = if m1.cmp(&m2).is_ge() {
377            m1.sub(&m2)
378        } else {
379            m1.add(&crt.p).sub(&m2)
380        };
381        let h = crt.qinv.mul(&diff).modulo(&crt.p);
382        let m = m2.add(&crt.q.mul(&h));
383        let em = m.to_be_bytes_padded(self.modulus_len())?;
384        decode_pkcs1_v15_plaintext(&em)
385    }
386
387    /// Decrypts RSAES-OAEP ciphertext with SHA-256 and caller-provided label.
388    ///
389    /// # Arguments
390    /// * `ciphertext`: Ciphertext bytes with length equal to modulus length.
391    /// * `label`: OAEP label bytes hashed into encoding parameters.
392    ///
393    /// # Returns
394    /// Decrypted plaintext when OAEP structure validates.
395    pub fn decrypt_oaep_sha256(&self, ciphertext: &[u8], label: &[u8]) -> Result<Vec<u8>> {
396        validate_private_components(&self.n, &self.d)?;
397        if ciphertext.len() != self.modulus_len() {
398            return Err(Error::CryptoFailure("rsa decryption failed"));
399        }
400        let em = BigUint::mod_exp(&BigUint::from_be_bytes(ciphertext), &self.d, &self.n)
401            .to_be_bytes_padded(self.modulus_len())?;
402        decode_oaep_sha256_plaintext(&em, label)
403    }
404
405    /// Decrypts RSAES-OAEP ciphertext using configured CRT components.
406    ///
407    /// # Arguments
408    /// * `ciphertext`: Ciphertext bytes with length equal to modulus length.
409    /// * `label`: OAEP label bytes hashed into encoding parameters.
410    ///
411    /// # Returns
412    /// Decrypted plaintext when CRT parameters are configured and OAEP structure validates.
413    pub fn decrypt_oaep_sha256_crt_only(&self, ciphertext: &[u8], label: &[u8]) -> Result<Vec<u8>> {
414        validate_private_components(&self.n, &self.d)?;
415        if ciphertext.len() != self.modulus_len() {
416            return Err(Error::CryptoFailure("rsa decryption failed"));
417        }
418        let crt = self
419            .crt
420            .as_ref()
421            .ok_or(Error::StateError("rsa crt parameters are not configured"))?;
422        let c = BigUint::from_be_bytes(ciphertext);
423        let m1 = BigUint::mod_exp(&c, &crt.dp, &crt.p);
424        let m2 = BigUint::mod_exp(&c, &crt.dq, &crt.q);
425        let diff = if m1.cmp(&m2).is_ge() {
426            m1.sub(&m2)
427        } else {
428            m1.add(&crt.p).sub(&m2)
429        };
430        let h = crt.qinv.mul(&diff).modulo(&crt.p);
431        let m = m2.add(&crt.q.mul(&h));
432        let em = m.to_be_bytes_padded(self.modulus_len())?;
433        decode_oaep_sha256_plaintext(&em, label)
434    }
435
436    /// Returns the RSA modulus length in bytes for PKCS encoding helpers.
437    ///
438    /// # Arguments
439    ///
440    /// * `self` — Private key whose modulus `n` defines the length.
441    ///
442    /// # Returns
443    ///
444    /// Byte length of the big-endian modulus encoding.
445    ///
446    /// # Panics
447    ///
448    /// This function does not panic.
449    fn modulus_len(&self) -> usize {
450        self.n.to_be_bytes().len()
451    }
452}
453
454impl Drop for RsaPrivateKey {
455    fn drop(&mut self) {
456        self.clear();
457    }
458}
459
460impl RsaPublicKey {
461    /// Creates public key from big-endian modulus and exponent bytes.
462    ///
463    /// # Arguments
464    /// * `n`: RSA modulus encoded as big-endian bytes.
465    /// * `e`: RSA public exponent encoded as big-endian bytes.
466    ///
467    /// # Returns
468    /// Parsed `RsaPublicKey` when both fields are non-empty.
469    ///
470    /// # Errors
471    ///
472    /// Returns [`Error::InvalidLength`] when fields are empty, or when modulus size is below
473    /// 2048 bits in default-safe builds (legacy-compatible hazardous mode permits smaller imports),
474    /// or other RSA component validation errors from [`validate_public_components`].
475    pub fn from_be_bytes(n: &[u8], e: &[u8]) -> Result<Self> {
476        if n.is_empty() || e.is_empty() {
477            return Err(Error::InvalidLength(
478                "rsa public key fields must not be empty",
479            ));
480        }
481        let key = Self {
482            n: BigUint::from_be_bytes(n),
483            e: BigUint::from_be_bytes(e),
484        };
485        if !cfg!(feature = "hazardous-legacy-crypto") && key.n.bit_len() < RSA_MIN_SECURE_BITS {
486            return Err(Error::InvalidLength(
487                "rsa public key modulus must be at least 2048 bits",
488            ));
489        }
490        validate_public_components(&key.n, &key.e)?;
491        Ok(key)
492    }
493
494    /// Creates public key from small integers for compatibility tests.
495    ///
496    /// # Arguments
497    /// * `n`: RSA modulus value.
498    /// * `e`: RSA public exponent value.
499    ///
500    /// # Returns
501    /// `RsaPublicKey` converted from the provided integer values.
502    #[must_use]
503    pub fn from_u128(n: u128, e: u128) -> Self {
504        Self {
505            n: BigUint::from_u128(n),
506            e: BigUint::from_u128(e),
507        }
508    }
509
510    /// Clears public key material to a zeroized placeholder state.
511    ///
512    /// # Notes
513    /// This mirrors explicit key free/reset lifecycle flows from the C surface.
514    pub fn clear(&mut self) {
515        self.n = BigUint::zero();
516        self.e = BigUint::zero();
517    }
518
519    /// Verifies a digest representative by recovering signature with exponent `e`.
520    ///
521    /// # Arguments
522    /// * `digest`: Expected digest representative bytes.
523    /// * `signature`: RSA signature to verify.
524    ///
525    /// # Returns
526    /// `Ok(())` when the recovered representative equals `digest mod n`.
527    pub fn verify_digest(&self, digest: &[u8], signature: &[u8]) -> Result<()> {
528        if digest.is_empty() {
529            return Err(Error::InvalidLength("digest must not be empty"));
530        }
531        validate_public_components(&self.n, &self.e)?;
532        let k = self.modulus_len();
533        let expected = BigUint::from_be_bytes(digest)
534            .modulo(&self.n)
535            .to_be_bytes_padded(k)?;
536        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
537            .to_be_bytes_padded(k)?;
538        if ct_bytes_eq(&recovered, &expected) {
539            Ok(())
540        } else {
541            Err(Error::CryptoFailure("RSA verification failed"))
542        }
543    }
544
545    /// Verifies RSASSA-PKCS1-v1_5 signature for SHA-256 hashed message.
546    ///
547    /// # Arguments
548    /// * `msg`: Original message bytes.
549    /// * `signature`: RSA signature expected to be PKCS#1 v1.5 encoded.
550    ///
551    /// # Returns
552    /// `Ok(())` when signature verification succeeds.
553    pub fn verify_pkcs1_v15_sha256(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
554        validate_public_components(&self.n, &self.e)?;
555        if signature.len() != self.modulus_len() {
556            return Err(Error::InvalidLength("rsa signature length mismatch"));
557        }
558        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
559            .to_be_bytes_padded(self.modulus_len())?;
560        let expected = emsa_pkcs1_v15_encode(
561            &noxtls_sha256(msg),
562            PKCS1_V15_DIGESTINFO_SHA256_PREFIX,
563            self.modulus_len(),
564        )?;
565        if ct_bytes_eq(&recovered, &expected) {
566            Ok(())
567        } else {
568            Err(Error::CryptoFailure("RSA verification failed"))
569        }
570    }
571
572    /// Verifies RSASSA-PKCS1-v1_5 signature for SHA-1 hashed message.
573    ///
574    /// # Arguments
575    /// * `msg`: Original message bytes.
576    /// * `signature`: RSA signature expected to be PKCS#1 v1.5 encoded.
577    ///
578    /// # Returns
579    /// `Ok(())` when signature verification succeeds.
580    pub fn verify_pkcs1_v15_sha1(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
581        validate_public_components(&self.n, &self.e)?;
582        if signature.len() != self.modulus_len() {
583            return Err(Error::InvalidLength("rsa signature length mismatch"));
584        }
585        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
586            .to_be_bytes_padded(self.modulus_len())?;
587        let expected = emsa_pkcs1_v15_encode(
588            &noxtls_sha1(msg),
589            PKCS1_V15_DIGESTINFO_SHA1_PREFIX,
590            self.modulus_len(),
591        )?;
592        if ct_bytes_eq(&recovered, &expected) {
593            Ok(())
594        } else {
595            Err(Error::CryptoFailure("RSA verification failed"))
596        }
597    }
598
599    /// Verifies RSASSA-PKCS1-v1_5 signature for SHA-384 hashed message.
600    ///
601    /// # Arguments
602    /// * `msg`: Original message bytes.
603    /// * `signature`: RSA signature expected to be PKCS#1 v1.5 encoded.
604    ///
605    /// # Returns
606    /// `Ok(())` when signature verification succeeds.
607    pub fn verify_pkcs1_v15_sha384(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
608        validate_public_components(&self.n, &self.e)?;
609        if signature.len() != self.modulus_len() {
610            return Err(Error::InvalidLength("rsa signature length mismatch"));
611        }
612        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
613            .to_be_bytes_padded(self.modulus_len())?;
614        let expected = emsa_pkcs1_v15_encode(
615            &noxtls_sha384(msg),
616            PKCS1_V15_DIGESTINFO_SHA384_PREFIX,
617            self.modulus_len(),
618        )?;
619        if ct_bytes_eq(&recovered, &expected) {
620            Ok(())
621        } else {
622            Err(Error::CryptoFailure("RSA verification failed"))
623        }
624    }
625
626    /// Verifies RSASSA-PKCS1-v1_5 signature for SHA-512 hashed message.
627    ///
628    /// # Arguments
629    /// * `msg`: Original message bytes.
630    /// * `signature`: RSA signature expected to be PKCS#1 v1.5 encoded.
631    ///
632    /// # Returns
633    /// `Ok(())` when signature verification succeeds.
634    pub fn verify_pkcs1_v15_sha512(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
635        validate_public_components(&self.n, &self.e)?;
636        if signature.len() != self.modulus_len() {
637            return Err(Error::InvalidLength("rsa signature length mismatch"));
638        }
639        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
640            .to_be_bytes_padded(self.modulus_len())?;
641        let expected = emsa_pkcs1_v15_encode(
642            &noxtls_sha512(msg),
643            PKCS1_V15_DIGESTINFO_SHA512_PREFIX,
644            self.modulus_len(),
645        )?;
646        if ct_bytes_eq(&recovered, &expected) {
647            Ok(())
648        } else {
649            Err(Error::CryptoFailure("RSA verification failed"))
650        }
651    }
652
653    /// Verifies RSASSA-PSS signature for SHA-256 hashed message.
654    ///
655    /// # Arguments
656    /// * `msg`: Original message bytes.
657    /// * `signature`: RSA signature expected to be PSS encoded.
658    /// * `salt_len`: Expected salt length used by signer.
659    ///
660    /// # Returns
661    /// `Ok(())` when signature verification succeeds.
662    pub fn verify_pss_sha256(&self, msg: &[u8], signature: &[u8], salt_len: usize) -> Result<()> {
663        validate_public_components(&self.n, &self.e)?;
664        if signature.len() != self.modulus_len() {
665            return Err(Error::InvalidLength("rsa signature length mismatch"));
666        }
667        let em_bits = self.n.bit_len().saturating_sub(1);
668        let em_len = em_bits.div_ceil(8);
669        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
670            .to_be_bytes_padded(self.modulus_len())?;
671        let em = &recovered[recovered.len() - em_len..];
672        emsa_pss_verify_sha256(&noxtls_sha256(msg), em, em_bits, salt_len)
673    }
674
675    /// Verifies RSASSA-PSS signature for SHA-384 hashed message.
676    ///
677    /// # Arguments
678    /// * `msg`: Original message bytes.
679    /// * `signature`: RSA signature expected to be PSS encoded.
680    /// * `salt_len`: Expected salt length used by signer.
681    ///
682    /// # Returns
683    /// `Ok(())` when signature verification succeeds.
684    pub fn verify_pss_sha384(&self, msg: &[u8], signature: &[u8], salt_len: usize) -> Result<()> {
685        validate_public_components(&self.n, &self.e)?;
686        if signature.len() != self.modulus_len() {
687            return Err(Error::InvalidLength("rsa signature length mismatch"));
688        }
689        let em_bits = self.n.bit_len().saturating_sub(1);
690        let em_len = em_bits.div_ceil(8);
691        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
692            .to_be_bytes_padded(self.modulus_len())?;
693        let em = &recovered[recovered.len() - em_len..];
694        emsa_pss_verify_sha384(&noxtls_sha384(msg), em, em_bits, salt_len)
695    }
696
697    pub fn verify_pss_with_hashes(
698        &self,
699        msg: &[u8],
700        signature: &[u8],
701        message_hash: RsaPssHashAlgorithm,
702        mgf_hash: RsaPssHashAlgorithm,
703        salt_len: usize,
704    ) -> Result<()> {
705        validate_public_components(&self.n, &self.e)?;
706        if signature.len() != self.modulus_len() {
707            return Err(Error::InvalidLength("rsa signature length mismatch"));
708        }
709        let em_bits = self.n.bit_len().saturating_sub(1);
710        let em_len = em_bits.div_ceil(8);
711        let recovered = BigUint::mod_exp(&BigUint::from_be_bytes(signature), &self.e, &self.n)
712            .to_be_bytes_padded(self.modulus_len())?;
713        let em = &recovered[recovered.len() - em_len..];
714        let digest = rsa_pss_hash(message_hash, msg);
715        emsa_pss_verify(&digest, em, em_bits, salt_len, message_hash, mgf_hash)
716    }
717
718    /// Encrypts plaintext using RSAES-PKCS1-v1_5 with DRBG-sourced non-zero padding.
719    ///
720    /// # Arguments
721    /// * `plaintext`: Plaintext bytes to encrypt.
722    /// * `drbg`: DRBG used to generate PKCS#1 v1.5 PS bytes.
723    ///
724    /// # Returns
725    /// Ciphertext bytes padded to modulus length.
726    pub fn encrypt_pkcs1_v15_auto(
727        &self,
728        plaintext: &[u8],
729        drbg: &mut HmacDrbgSha256,
730    ) -> Result<Vec<u8>> {
731        validate_public_components(&self.n, &self.e)?;
732        let k = self.modulus_len();
733        if plaintext.len() > k.saturating_sub(11) {
734            return Err(Error::InvalidLength(
735                "rsa plaintext too long for pkcs1 v1.5 encryption",
736            ));
737        }
738        let ps_len = k - plaintext.len() - 3;
739        let ps = drbg_nonzero_padding(drbg, ps_len)?;
740        let mut em = Vec::with_capacity(k);
741        em.push(0x00);
742        em.push(0x02);
743        em.extend_from_slice(&ps);
744        em.push(0x00);
745        em.extend_from_slice(plaintext);
746        let c = BigUint::mod_exp(&BigUint::from_be_bytes(&em), &self.e, &self.n);
747        c.to_be_bytes_padded(k)
748    }
749
750    /// Encrypts plaintext using RSAES-OAEP with SHA-256 and DRBG-derived seed.
751    ///
752    /// # Arguments
753    /// * `plaintext`: Plaintext bytes to encrypt.
754    /// * `label`: OAEP label bytes hashed into encoding parameters.
755    /// * `drbg`: DRBG used to generate OAEP seed bytes.
756    ///
757    /// # Returns
758    /// Ciphertext bytes padded to modulus length.
759    pub fn encrypt_oaep_sha256_auto(
760        &self,
761        plaintext: &[u8],
762        label: &[u8],
763        drbg: &mut HmacDrbgSha256,
764    ) -> Result<Vec<u8>> {
765        validate_public_components(&self.n, &self.e)?;
766        let k = self.modulus_len();
767        let seed = drbg.generate(32, b"rsa_oaep_sha256_seed")?;
768        let em = emea_oaep_encode_sha256(plaintext, label, &seed, k)?;
769        let c = BigUint::mod_exp(&BigUint::from_be_bytes(&em), &self.e, &self.n);
770        c.to_be_bytes_padded(k)
771    }
772
773    /// Returns the RSA modulus length in bytes for encryption and encoding helpers.
774    ///
775    /// # Arguments
776    ///
777    /// * `self` — Public key whose modulus `n` defines the length.
778    ///
779    /// # Returns
780    ///
781    /// Byte length of the big-endian modulus encoding.
782    ///
783    /// # Panics
784    ///
785    /// This function does not panic.
786    fn modulus_len(&self) -> usize {
787        self.n.to_be_bytes().len()
788    }
789}
790
791/// Generates an RSA keypair with a caller-provided public exponent using DRBG entropy.
792///
793/// # Arguments
794/// * `modulus_bits`: Target modulus size in bits (supported range: 1024..=4096).
795/// * `public_exponent`: Public exponent value (must be odd and >= 3).
796/// * `drbg`: DRBG source used to sample prime candidates.
797///
798/// # Returns
799/// Generated `(private_key, public_key)` pair including CRT parameters.
800#[cfg(feature = "hazardous-legacy-crypto")]
801pub fn noxtls_rsa_generate_keypair_with_exponent_auto(
802    modulus_bits: usize,
803    public_exponent: u32,
804    drbg: &mut HmacDrbgSha256,
805) -> Result<(RsaPrivateKey, RsaPublicKey)> {
806    rsa_generate_keypair_backend_auto(modulus_bits, public_exponent, drbg)
807}
808
809/// Generates RSA keypair material with backend-supported modulus range and exponent checks.
810///
811/// # Arguments
812///
813/// * `modulus_bits` — Target modulus size in bits (supported inclusive range enforced inside).
814/// * `public_exponent` — Desired public exponent (must be odd and at least 3).
815/// * `drbg` — DRBG used for prime sampling and auxiliary randomness.
816///
817/// # Returns
818///
819/// On success, a `(private_key, public_key)` pair including CRT parameters when applicable.
820///
821/// # Errors
822///
823/// Returns `noxtls_core::Error` when parameters are out of range, prime search fails, or internal invariants fail.
824///
825/// # Panics
826///
827/// This function does not panic.
828fn rsa_generate_keypair_backend_auto(
829    modulus_bits: usize,
830    public_exponent: u32,
831    drbg: &mut HmacDrbgSha256,
832) -> Result<(RsaPrivateKey, RsaPublicKey)> {
833    if !(RSA_KEYGEN_MIN_BITS..=RSA_KEYGEN_MAX_BITS).contains(&modulus_bits) {
834        return Err(Error::InvalidLength(
835            "rsa modulus bits must be in supported range 1024..=4096",
836        ));
837    }
838    if public_exponent < 3 || (public_exponent & 1) == 0 {
839        return Err(Error::CryptoFailure(
840            "rsa public exponent must be odd and at least 3",
841        ));
842    }
843    let e = BigUint::from_u128(u128::from(public_exponent));
844    let one = BigUint::one();
845    let p_bits = modulus_bits / 2;
846    let q_bits = modulus_bits - p_bits;
847    let mut attempts = 0_u32;
848    while attempts < 256 {
849        let mut p = generate_rsa_prime_candidate_auto(p_bits, &e, drbg)?;
850        let mut q = generate_rsa_prime_candidate_auto(q_bits, &e, drbg)?;
851        let mut distinct_attempts = 0_u32;
852        while p.cmp(&q).is_eq() {
853            if distinct_attempts >= 32 {
854                break;
855            }
856            q = generate_rsa_prime_candidate_auto(q_bits, &e, drbg)?;
857            distinct_attempts = distinct_attempts.saturating_add(1);
858        }
859        if p.cmp(&q).is_eq() {
860            attempts = attempts.saturating_add(1);
861            continue;
862        }
863        if p.cmp(&q).is_gt() {
864            core::mem::swap(&mut p, &mut q);
865        }
866        let n = p.mul(&q);
867        if n.bit_len() != modulus_bits {
868            attempts = attempts.saturating_add(1);
869            continue;
870        }
871        let pm1 = p.sub(&one);
872        let qm1 = q.sub(&one);
873        let phi = pm1.mul(&qm1);
874        if BigUint::gcd(&e, &phi).cmp(&one).is_ne() {
875            attempts = attempts.saturating_add(1);
876            continue;
877        }
878        let Some(d) = BigUint::mod_inverse(&e, &phi) else {
879            attempts = attempts.saturating_add(1);
880            continue;
881        };
882        let dp = d.modulo(&pm1);
883        let dq = d.modulo(&qm1);
884        let Some(qinv) = BigUint::mod_inverse(&q, &p) else {
885            attempts = attempts.saturating_add(1);
886            continue;
887        };
888        let private = RsaPrivateKey {
889            n: n.clone(),
890            d,
891            crt: Some(RsaPrivateCrtComponents { p, q, dp, dq, qinv }),
892        };
893        let public = RsaPublicKey { n, e };
894        validate_private_components(&private.n, &private.d)?;
895        validate_public_components(&public.n, &public.e)?;
896        validate_crt_components(&private.n, private.crt.as_ref().expect("crt must exist"))?;
897        return Ok((private, public));
898    }
899    Err(Error::StateError(
900        "rsa key generation exhausted attempt budget",
901    ))
902}
903
904/// Generates an RSA keypair with default public exponent `65537` using DRBG entropy.
905///
906/// # Arguments
907/// * `modulus_bits`: Target modulus size in bits (supported range: 1024..=4096).
908/// * `drbg`: DRBG source used to sample prime candidates.
909///
910/// # Returns
911/// Generated `(private_key, public_key)` pair including CRT parameters.
912#[cfg(feature = "hazardous-legacy-crypto")]
913pub fn noxtls_rsa_generate_keypair_auto(
914    modulus_bits: usize,
915    drbg: &mut HmacDrbgSha256,
916) -> Result<(RsaPrivateKey, RsaPublicKey)> {
917    rsa_generate_keypair_backend_auto(modulus_bits, 65_537, drbg)
918}
919
920/// Generates an RSA keypair under one secure minimum key-size policy.
921///
922/// # Arguments
923/// * `modulus_bits`: Target modulus size in bits for generated key material.
924/// * `public_exponent`: Public exponent value (must be odd and >= 3).
925/// * `policy`: Secure minimum modulus-size policy to enforce.
926/// * `drbg`: DRBG source used to sample prime candidates.
927///
928/// # Returns
929/// Generated `(private_key, public_key)` pair when key size satisfies policy and backend support.
930pub fn noxtls_rsa_generate_keypair_with_policy_auto(
931    modulus_bits: usize,
932    public_exponent: u32,
933    policy: RsaKeySizePolicy,
934    drbg: &mut HmacDrbgSha256,
935) -> Result<(RsaPrivateKey, RsaPublicKey)> {
936    if !(RSA_MIN_SECURE_BITS..=RSA_KEYGEN_MAX_BITS).contains(&modulus_bits) {
937        return Err(Error::InvalidLength(
938            "secure rsa modulus bits must be in supported range 2048..=4096",
939        ));
940    }
941    if modulus_bits < policy.min_bits() {
942        return Err(Error::InvalidLength(
943            "rsa modulus bits do not satisfy configured secure policy minimum",
944        ));
945    }
946    rsa_generate_keypair_backend_auto(modulus_bits, public_exponent, drbg)
947}
948
949/// Generates an RSA keypair with secure minimum modulus policy and default exponent `65537`.
950///
951/// # Arguments
952/// * `modulus_bits`: Target modulus size in bits for generated key material.
953/// * `policy`: Secure minimum modulus-size policy to enforce.
954/// * `drbg`: DRBG source used to sample prime candidates.
955///
956/// # Returns
957/// Generated `(private_key, public_key)` pair when key size satisfies secure policy.
958pub fn noxtls_rsa_generate_keypair_secure_auto(
959    modulus_bits: usize,
960    policy: RsaKeySizePolicy,
961    drbg: &mut HmacDrbgSha256,
962) -> Result<(RsaPrivateKey, RsaPublicKey)> {
963    noxtls_rsa_generate_keypair_with_policy_auto(modulus_bits, 65_537, policy, drbg)
964}
965
966/// Hashes and signs message using RSASSA-PKCS1-v1_5 with SHA-256.
967///
968/// # Arguments
969/// * `private`: RSA private key used to produce the signature.
970/// * `msg`: Message bytes to hash and sign.
971///
972/// # Returns
973/// PKCS#1 v1.5 RSA signature bytes.
974pub fn noxtls_rsassa_sha256_sign(private: &RsaPrivateKey, msg: &[u8]) -> Result<Vec<u8>> {
975    private.sign_pkcs1_v15_sha256(msg)
976}
977
978/// Hashes and verifies message using RSASSA-PKCS1-v1_5 with SHA-256.
979///
980/// # Arguments
981/// * `public`: RSA public key used to verify the signature.
982/// * `msg`: Original message bytes.
983/// * `signature`: Signature bytes to validate.
984///
985/// # Returns
986/// `Ok(())` when the signature is valid.
987pub fn noxtls_rsassa_sha256_verify(
988    public: &RsaPublicKey,
989    msg: &[u8],
990    signature: &[u8],
991) -> Result<()> {
992    public.verify_pkcs1_v15_sha256(msg, signature)
993}
994
995/// Hashes and signs message using RSASSA-PKCS1-v1_5 with SHA-1.
996///
997/// # Arguments
998/// * `private`: RSA private key used to produce the signature.
999/// * `msg`: Message bytes to hash and sign.
1000///
1001/// # Returns
1002/// PKCS#1 v1.5 RSA signature bytes.
1003pub fn noxtls_rsassa_sha1_sign(private: &RsaPrivateKey, msg: &[u8]) -> Result<Vec<u8>> {
1004    private.sign_pkcs1_v15_sha1(msg)
1005}
1006
1007/// Hashes and verifies message using RSASSA-PKCS1-v1_5 with SHA-1.
1008///
1009/// # Arguments
1010/// * `public`: RSA public key used to verify the signature.
1011/// * `msg`: Original message bytes.
1012/// * `signature`: Signature bytes to validate.
1013///
1014/// # Returns
1015/// `Ok(())` when the signature is valid.
1016pub fn noxtls_rsassa_sha1_verify(
1017    public: &RsaPublicKey,
1018    msg: &[u8],
1019    signature: &[u8],
1020) -> Result<()> {
1021    public.verify_pkcs1_v15_sha1(msg, signature)
1022}
1023
1024/// Hashes and signs message using RSASSA-PKCS1-v1_5 with SHA-384.
1025///
1026/// # Arguments
1027/// * `private`: RSA private key used to produce the signature.
1028/// * `msg`: Message bytes to hash and sign.
1029///
1030/// # Returns
1031/// PKCS#1 v1.5 RSA signature bytes.
1032pub fn noxtls_rsassa_sha384_sign(private: &RsaPrivateKey, msg: &[u8]) -> Result<Vec<u8>> {
1033    private.sign_pkcs1_v15_sha384(msg)
1034}
1035
1036/// Hashes and verifies message using RSASSA-PKCS1-v1_5 with SHA-384.
1037///
1038/// # Arguments
1039/// * `public`: RSA public key used to verify the signature.
1040/// * `msg`: Original message bytes.
1041/// * `signature`: Signature bytes to validate.
1042///
1043/// # Returns
1044/// `Ok(())` when the signature is valid.
1045pub fn noxtls_rsassa_sha384_verify(
1046    public: &RsaPublicKey,
1047    msg: &[u8],
1048    signature: &[u8],
1049) -> Result<()> {
1050    public.verify_pkcs1_v15_sha384(msg, signature)
1051}
1052
1053/// Hashes and signs message using RSASSA-PKCS1-v1_5 with SHA-512.
1054///
1055/// # Arguments
1056/// * `private`: RSA private key used to produce the signature.
1057/// * `msg`: Message bytes to hash and sign.
1058///
1059/// # Returns
1060/// PKCS#1 v1.5 RSA signature bytes.
1061pub fn noxtls_rsassa_sha512_sign(private: &RsaPrivateKey, msg: &[u8]) -> Result<Vec<u8>> {
1062    private.sign_pkcs1_v15_sha512(msg)
1063}
1064
1065/// Hashes and verifies message using RSASSA-PKCS1-v1_5 with SHA-512.
1066///
1067/// # Arguments
1068/// * `public`: RSA public key used to verify the signature.
1069/// * `msg`: Original message bytes.
1070/// * `signature`: Signature bytes to validate.
1071///
1072/// # Returns
1073/// `Ok(())` when the signature is valid.
1074pub fn noxtls_rsassa_sha512_verify(
1075    public: &RsaPublicKey,
1076    msg: &[u8],
1077    signature: &[u8],
1078) -> Result<()> {
1079    public.verify_pkcs1_v15_sha512(msg, signature)
1080}
1081
1082/// Signs message using RSASSA-PSS with SHA-256 and caller-provided salt.
1083///
1084/// # Arguments
1085/// * `private`: RSA private key used to sign.
1086/// * `msg`: Message bytes to hash and sign.
1087/// * `salt`: Caller-provided random salt used by PSS encoding.
1088///
1089/// # Returns
1090/// RSASSA-PSS signature bytes.
1091pub fn noxtls_rsassa_pss_sha256_sign(
1092    private: &RsaPrivateKey,
1093    msg: &[u8],
1094    salt: &[u8],
1095) -> Result<Vec<u8>> {
1096    private.sign_pss_sha256(msg, salt)
1097}
1098
1099/// Signs message using RSASSA-PSS with SHA-256 and DRBG-generated salt.
1100///
1101/// # Arguments
1102/// * `private`: RSA private key used to sign.
1103/// * `msg`: Message bytes to hash and sign.
1104/// * `drbg`: DRBG used to generate PSS salt bytes.
1105/// * `salt_len`: Requested salt length in bytes.
1106///
1107/// # Returns
1108/// RSASSA-PSS signature bytes.
1109pub fn noxtls_rsassa_pss_sha256_sign_auto(
1110    private: &RsaPrivateKey,
1111    msg: &[u8],
1112    drbg: &mut HmacDrbgSha256,
1113    salt_len: usize,
1114) -> Result<Vec<u8>> {
1115    let salt = drbg.generate(salt_len, b"rsa_pss_sha256_salt")?;
1116    private.sign_pss_sha256(msg, &salt)
1117}
1118
1119/// Verifies RSASSA-PSS signature for SHA-256 with expected salt length.
1120///
1121/// # Arguments
1122/// * `public`: RSA public key used to verify.
1123/// * `msg`: Original message bytes.
1124/// * `signature`: Signature bytes to validate.
1125/// * `salt_len`: Expected salt length used in PSS encoding.
1126///
1127/// # Returns
1128/// `Ok(())` when the signature is valid.
1129pub fn noxtls_rsassa_pss_sha256_verify(
1130    public: &RsaPublicKey,
1131    msg: &[u8],
1132    signature: &[u8],
1133    salt_len: usize,
1134) -> Result<()> {
1135    public.verify_pss_sha256(msg, signature, salt_len)
1136}
1137
1138/// Signs message using RSASSA-PSS with SHA-384 and caller-provided salt.
1139///
1140/// # Arguments
1141/// * `private`: RSA private key used to sign.
1142/// * `msg`: Message bytes to hash and sign.
1143/// * `salt`: Caller-provided random salt used by PSS encoding.
1144///
1145/// # Returns
1146/// RSASSA-PSS signature bytes.
1147pub fn noxtls_rsassa_pss_sha384_sign(
1148    private: &RsaPrivateKey,
1149    msg: &[u8],
1150    salt: &[u8],
1151) -> Result<Vec<u8>> {
1152    private.sign_pss_sha384(msg, salt)
1153}
1154
1155/// Signs message using RSASSA-PSS with SHA-384 and DRBG-generated salt.
1156///
1157/// # Arguments
1158/// * `private`: RSA private key used to sign.
1159/// * `msg`: Message bytes to hash and sign.
1160/// * `drbg`: DRBG used to generate PSS salt bytes.
1161/// * `salt_len`: Requested salt length in bytes.
1162///
1163/// # Returns
1164/// RSASSA-PSS signature bytes.
1165pub fn noxtls_rsassa_pss_sha384_sign_auto(
1166    private: &RsaPrivateKey,
1167    msg: &[u8],
1168    drbg: &mut HmacDrbgSha256,
1169    salt_len: usize,
1170) -> Result<Vec<u8>> {
1171    let salt = drbg.generate(salt_len, b"rsa_pss_sha384_salt")?;
1172    private.sign_pss_sha384(msg, &salt)
1173}
1174
1175/// Verifies RSASSA-PSS signature for SHA-384 with expected salt length.
1176///
1177/// # Arguments
1178/// * `public`: RSA public key used to verify.
1179/// * `msg`: Original message bytes.
1180/// * `signature`: Signature bytes to validate.
1181/// * `salt_len`: Expected salt length used in PSS encoding.
1182///
1183/// # Returns
1184/// `Ok(())` when the signature is valid.
1185pub fn noxtls_rsassa_pss_sha384_verify(
1186    public: &RsaPublicKey,
1187    msg: &[u8],
1188    signature: &[u8],
1189    salt_len: usize,
1190) -> Result<()> {
1191    public.verify_pss_sha384(msg, signature, salt_len)
1192}
1193
1194pub fn noxtls_rsassa_pss_sign(
1195    private: &RsaPrivateKey,
1196    msg: &[u8],
1197    salt: &[u8],
1198    message_hash: RsaPssHashAlgorithm,
1199    mgf_hash: RsaPssHashAlgorithm,
1200) -> Result<Vec<u8>> {
1201    private.sign_pss_with_hashes(msg, salt, message_hash, mgf_hash)
1202}
1203
1204pub fn noxtls_rsassa_pss_verify(
1205    public: &RsaPublicKey,
1206    msg: &[u8],
1207    signature: &[u8],
1208    message_hash: RsaPssHashAlgorithm,
1209    mgf_hash: RsaPssHashAlgorithm,
1210    salt_len: usize,
1211) -> Result<()> {
1212    public.verify_pss_with_hashes(msg, signature, message_hash, mgf_hash, salt_len)
1213}
1214
1215/// Encrypts plaintext using RSAES-PKCS1-v1_5 with DRBG-generated non-zero padding.
1216///
1217/// # Arguments
1218/// * `public`: RSA public key used to encrypt.
1219/// * `plaintext`: Plaintext bytes to encrypt.
1220/// * `drbg`: DRBG used to generate PKCS#1 v1.5 PS bytes.
1221///
1222/// # Returns
1223/// Ciphertext bytes padded to modulus length.
1224pub fn noxtls_rsaes_pkcs1_v15_encrypt_auto(
1225    public: &RsaPublicKey,
1226    plaintext: &[u8],
1227    drbg: &mut HmacDrbgSha256,
1228) -> Result<Vec<u8>> {
1229    public.encrypt_pkcs1_v15_auto(plaintext, drbg)
1230}
1231
1232/// Decrypts RSAES-PKCS1-v1_5 ciphertext.
1233///
1234/// # Arguments
1235/// * `private`: RSA private key used to decrypt.
1236/// * `ciphertext`: Ciphertext bytes to decrypt.
1237///
1238/// # Returns
1239/// Decrypted plaintext bytes.
1240pub fn noxtls_rsaes_pkcs1_v15_decrypt(
1241    private: &RsaPrivateKey,
1242    ciphertext: &[u8],
1243) -> Result<Vec<u8>> {
1244    private.decrypt_pkcs1_v15(ciphertext)
1245}
1246
1247/// Decrypts RSAES-PKCS1-v1_5 ciphertext via CRT-only compatibility API.
1248///
1249/// # Arguments
1250/// * `private`: RSA private key used to decrypt.
1251/// * `ciphertext`: Ciphertext bytes to decrypt.
1252///
1253/// # Returns
1254/// Decrypted plaintext bytes.
1255///
1256/// # Notes
1257/// This API mirrors the C compatibility surface. Current Rust key material stores
1258/// `(n, d)` only, so it delegates to standard private exponent decryption while
1259/// preserving external API shape for parity tracking.
1260pub fn noxtls_rsaes_pkcs1_v15_decrypt_crt_only(
1261    private: &RsaPrivateKey,
1262    ciphertext: &[u8],
1263) -> Result<Vec<u8>> {
1264    private.decrypt_pkcs1_v15_crt_only(ciphertext)
1265}
1266
1267/// Encrypts plaintext using RSAES-OAEP with SHA-256 and DRBG-derived seed.
1268///
1269/// # Arguments
1270/// * `public`: RSA public key used to encrypt.
1271/// * `plaintext`: Plaintext bytes to encrypt.
1272/// * `label`: OAEP label bytes hashed into encoding parameters.
1273/// * `drbg`: DRBG used to generate OAEP seed bytes.
1274///
1275/// # Returns
1276/// Ciphertext bytes padded to modulus length.
1277pub fn noxtls_rsaes_oaep_sha256_encrypt_auto(
1278    public: &RsaPublicKey,
1279    plaintext: &[u8],
1280    label: &[u8],
1281    drbg: &mut HmacDrbgSha256,
1282) -> Result<Vec<u8>> {
1283    public.encrypt_oaep_sha256_auto(plaintext, label, drbg)
1284}
1285
1286/// Decrypts RSAES-OAEP ciphertext with SHA-256 and caller-provided label.
1287///
1288/// # Arguments
1289/// * `private`: RSA private key used to decrypt.
1290/// * `ciphertext`: Ciphertext bytes to decrypt.
1291/// * `label`: OAEP label bytes hashed into encoding parameters.
1292///
1293/// # Returns
1294/// Decrypted plaintext bytes.
1295pub fn noxtls_rsaes_oaep_sha256_decrypt(
1296    private: &RsaPrivateKey,
1297    ciphertext: &[u8],
1298    label: &[u8],
1299) -> Result<Vec<u8>> {
1300    private.decrypt_oaep_sha256(ciphertext, label)
1301}
1302
1303/// Decrypts RSAES-OAEP ciphertext via CRT-only compatibility API.
1304///
1305/// # Arguments
1306/// * `private`: RSA private key used to decrypt.
1307/// * `ciphertext`: Ciphertext bytes to decrypt.
1308/// * `label`: OAEP label bytes hashed into encoding parameters.
1309///
1310/// # Returns
1311/// Decrypted plaintext bytes.
1312pub fn noxtls_rsaes_oaep_sha256_decrypt_crt_only(
1313    private: &RsaPrivateKey,
1314    ciphertext: &[u8],
1315    label: &[u8],
1316) -> Result<Vec<u8>> {
1317    private.decrypt_oaep_sha256_crt_only(ciphertext, label)
1318}
1319
1320const PKCS1_V15_DIGESTINFO_SHA1_PREFIX: &[u8] = &[
1321    0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,
1322];
1323const PKCS1_V15_DIGESTINFO_SHA256_PREFIX: &[u8] = &[
1324    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
1325    0x00, 0x04, 0x20,
1326];
1327const PKCS1_V15_DIGESTINFO_SHA384_PREFIX: &[u8] = &[
1328    0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
1329    0x00, 0x04, 0x30,
1330];
1331const PKCS1_V15_DIGESTINFO_SHA512_PREFIX: &[u8] = &[
1332    0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
1333    0x00, 0x04, 0x40,
1334];
1335
1336/// Encodes digest bytes into EMSA-PKCS1-v1_5 block for modulus length `k`.
1337///
1338/// # Arguments
1339///
1340/// * `hash` — `&[u8]`.
1341/// * `digest_info_prefix` — `&[u8]`.
1342/// * `k` — `usize`.
1343///
1344/// # Returns
1345///
1346/// On success, the `Ok` payload from `emsa_pkcs1_v15_encode`; see implementation for value shape.
1347///
1348/// # Errors
1349///
1350/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1351///
1352/// # Panics
1353///
1354/// This function does not panic unless otherwise noted.
1355fn emsa_pkcs1_v15_encode(hash: &[u8], digest_info_prefix: &[u8], k: usize) -> Result<Vec<u8>> {
1356    let t_len = digest_info_prefix.len() + hash.len();
1357    if k < t_len + 11 {
1358        return Err(Error::InvalidLength("rsa modulus too short for pkcs1 v1.5"));
1359    }
1360    let ps_len = k - t_len - 3;
1361    let mut em = Vec::with_capacity(k);
1362    em.push(0x00);
1363    em.push(0x01);
1364    em.extend(core::iter::repeat(0xff_u8).take(ps_len));
1365    em.push(0x00);
1366    em.extend_from_slice(digest_info_prefix);
1367    em.extend_from_slice(hash);
1368    Ok(em)
1369}
1370
1371/// Encodes a message hash using EMSA-PSS (SHA-256) with caller-provided salt.
1372///
1373/// # Arguments
1374///
1375/// * `m_hash` — 32-byte message digest.
1376/// * `salt` — PSS salt bytes.
1377/// * `em_bits` — Effective encoded message bit length.
1378/// * `em_len` — Encoded message byte length `em_bits` maps to.
1379///
1380/// # Returns
1381///
1382/// On success, the encoded message bytes.
1383///
1384/// # Errors
1385///
1386/// Returns `noxtls_core::Error` when the modulus is too short for the chosen parameters.
1387///
1388/// # Panics
1389///
1390/// This function does not panic.
1391fn emsa_pss_encode_sha256(
1392    m_hash: &[u8; 32],
1393    salt: &[u8],
1394    em_bits: usize,
1395    em_len: usize,
1396) -> Result<Vec<u8>> {
1397    const HASH_LEN: usize = 32;
1398    if em_len < HASH_LEN + salt.len() + 2 {
1399        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1400    }
1401
1402    let mut m_prime = vec![0_u8; 8];
1403    m_prime.extend_from_slice(m_hash);
1404    m_prime.extend_from_slice(salt);
1405    let h = noxtls_sha256(&m_prime);
1406
1407    let ps_len = em_len - salt.len() - HASH_LEN - 2;
1408    let mut db = vec![0_u8; ps_len];
1409    db.push(0x01);
1410    db.extend_from_slice(salt);
1411
1412    let db_mask = mgf1_sha256(&h, em_len - HASH_LEN - 1)?;
1413    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1414        *byte ^= *mask;
1415    }
1416
1417    let unused_bits = 8 * em_len - em_bits;
1418    if unused_bits > 0 {
1419        db[0] &= 0xff_u8 >> unused_bits;
1420    }
1421
1422    let mut em = db;
1423    em.extend_from_slice(&h);
1424    em.push(0xbc);
1425    Ok(em)
1426}
1427
1428/// Verifies an EMSA-PSS (SHA-256) encoded message block against a digest and salt length.
1429///
1430/// # Arguments
1431///
1432/// * `m_hash` — Expected 32-byte message digest.
1433/// * `em` — Encoded message bytes to verify.
1434/// * `em_bits` — Effective encoded message bit length.
1435/// * `salt_len` — Expected salt byte length embedded in the encoding.
1436///
1437/// # Returns
1438///
1439/// `Ok(())` when the PSS structure and digest match.
1440///
1441/// # Errors
1442///
1443/// Returns `noxtls_core::Error` on malformed padding, hash mismatch, or insufficient length.
1444///
1445/// # Panics
1446///
1447/// This function does not panic.
1448fn emsa_pss_verify_sha256(
1449    m_hash: &[u8; 32],
1450    em: &[u8],
1451    em_bits: usize,
1452    salt_len: usize,
1453) -> Result<()> {
1454    const HASH_LEN: usize = 32;
1455    if em.len() < HASH_LEN + salt_len + 2 {
1456        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1457    }
1458    if em.last().copied() != Some(0xbc) {
1459        return Err(Error::CryptoFailure("RSA verification failed"));
1460    }
1461
1462    let db_len = em.len() - HASH_LEN - 1;
1463    let (masked_db, rest) = em.split_at(db_len);
1464    let h = &rest[..HASH_LEN];
1465
1466    let unused_bits = 8 * em.len() - em_bits;
1467    if unused_bits > 0 {
1468        let mask = 0xff_u8 << (8 - unused_bits);
1469        if masked_db[0] & mask != 0 {
1470            return Err(Error::CryptoFailure("RSA verification failed"));
1471        }
1472    }
1473
1474    let db_mask = mgf1_sha256(h, db_len)?;
1475    let mut db = masked_db.to_vec();
1476    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1477        *byte ^= *mask;
1478    }
1479    if unused_bits > 0 {
1480        db[0] &= 0xff_u8 >> unused_bits;
1481    }
1482
1483    let ps_len = em.len() - HASH_LEN - salt_len - 2;
1484    if !ct_all_zero(&db[..ps_len]) || db[ps_len] != 0x01 {
1485        return Err(Error::CryptoFailure("RSA verification failed"));
1486    }
1487    let salt = &db[db.len() - salt_len..];
1488
1489    let mut m_prime = vec![0_u8; 8];
1490    m_prime.extend_from_slice(m_hash);
1491    m_prime.extend_from_slice(salt);
1492    let expected_h = noxtls_sha256(&m_prime);
1493    if ct_bytes_eq(expected_h.as_slice(), h) {
1494        Ok(())
1495    } else {
1496        Err(Error::CryptoFailure("RSA verification failed"))
1497    }
1498}
1499
1500/// Encodes a message hash using EMSA-PSS (SHA-384) with caller-provided salt.
1501///
1502/// # Arguments
1503///
1504/// * `m_hash` — 48-byte message digest.
1505/// * `salt` — PSS salt bytes.
1506/// * `em_bits` — Effective encoded message bit length.
1507/// * `em_len` — Encoded message byte length.
1508///
1509/// # Returns
1510///
1511/// On success, the encoded message bytes.
1512///
1513/// # Errors
1514///
1515/// Returns `noxtls_core::Error` when the modulus is too short for the chosen parameters.
1516///
1517/// # Panics
1518///
1519/// This function does not panic.
1520fn emsa_pss_encode_sha384(
1521    m_hash: &[u8; 48],
1522    salt: &[u8],
1523    em_bits: usize,
1524    em_len: usize,
1525) -> Result<Vec<u8>> {
1526    const HASH_LEN: usize = 48;
1527    if em_len < HASH_LEN + salt.len() + 2 {
1528        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1529    }
1530
1531    let mut m_prime = vec![0_u8; 8];
1532    m_prime.extend_from_slice(m_hash);
1533    m_prime.extend_from_slice(salt);
1534    let h = noxtls_sha384(&m_prime);
1535
1536    let ps_len = em_len - salt.len() - HASH_LEN - 2;
1537    let mut db = vec![0_u8; ps_len];
1538    db.push(0x01);
1539    db.extend_from_slice(salt);
1540
1541    let db_mask = mgf1_sha384(&h, em_len - HASH_LEN - 1)?;
1542    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1543        *byte ^= *mask;
1544    }
1545
1546    let unused_bits = 8 * em_len - em_bits;
1547    if unused_bits > 0 {
1548        db[0] &= 0xff_u8 >> unused_bits;
1549    }
1550
1551    let mut em = db;
1552    em.extend_from_slice(&h);
1553    em.push(0xbc);
1554    Ok(em)
1555}
1556
1557fn emsa_pss_encode(
1558    m_hash: &[u8],
1559    salt: &[u8],
1560    em_bits: usize,
1561    em_len: usize,
1562    message_hash: RsaPssHashAlgorithm,
1563    mgf_hash: RsaPssHashAlgorithm,
1564) -> Result<Vec<u8>> {
1565    let hash_len = m_hash.len();
1566    if em_len < hash_len + salt.len() + 2 {
1567        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1568    }
1569
1570    let mut m_prime = vec![0_u8; 8];
1571    m_prime.extend_from_slice(m_hash);
1572    m_prime.extend_from_slice(salt);
1573    let h = rsa_pss_hash(message_hash, &m_prime);
1574
1575    let ps_len = em_len - salt.len() - hash_len - 2;
1576    let mut db = vec![0_u8; ps_len];
1577    db.push(0x01);
1578    db.extend_from_slice(salt);
1579
1580    let db_mask = mgf1_hash(mgf_hash, &h, em_len - hash_len - 1)?;
1581    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1582        *byte ^= *mask;
1583    }
1584
1585    let unused_bits = 8 * em_len - em_bits;
1586    if unused_bits > 0 {
1587        db[0] &= 0xff_u8 >> unused_bits;
1588    }
1589
1590    let mut em = db;
1591    em.extend_from_slice(&h);
1592    em.push(0xbc);
1593    Ok(em)
1594}
1595
1596/// Verifies an EMSA-PSS (SHA-384) encoded message block against a digest and salt length.
1597///
1598/// # Arguments
1599///
1600/// * `m_hash` — Expected 48-byte message digest.
1601/// * `em` — Encoded message bytes to verify.
1602/// * `em_bits` — Effective encoded message bit length.
1603/// * `salt_len` — Expected salt byte length.
1604///
1605/// # Returns
1606///
1607/// `Ok(())` when the PSS structure and digest match.
1608///
1609/// # Errors
1610///
1611/// Returns `noxtls_core::Error` on malformed padding, hash mismatch, or insufficient length.
1612///
1613/// # Panics
1614///
1615/// This function does not panic.
1616fn emsa_pss_verify_sha384(
1617    m_hash: &[u8; 48],
1618    em: &[u8],
1619    em_bits: usize,
1620    salt_len: usize,
1621) -> Result<()> {
1622    const HASH_LEN: usize = 48;
1623    if em.len() < HASH_LEN + salt_len + 2 {
1624        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1625    }
1626    if em.last().copied() != Some(0xbc) {
1627        return Err(Error::CryptoFailure("RSA verification failed"));
1628    }
1629
1630    let db_len = em.len() - HASH_LEN - 1;
1631    let (masked_db, rest) = em.split_at(db_len);
1632    let h = &rest[..HASH_LEN];
1633
1634    let unused_bits = 8 * em.len() - em_bits;
1635    if unused_bits > 0 {
1636        let mask = 0xff_u8 << (8 - unused_bits);
1637        if masked_db[0] & mask != 0 {
1638            return Err(Error::CryptoFailure("RSA verification failed"));
1639        }
1640    }
1641
1642    let db_mask = mgf1_sha384(h, db_len)?;
1643    let mut db = masked_db.to_vec();
1644    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1645        *byte ^= *mask;
1646    }
1647    if unused_bits > 0 {
1648        db[0] &= 0xff_u8 >> unused_bits;
1649    }
1650
1651    let ps_len = em.len() - HASH_LEN - salt_len - 2;
1652    if !ct_all_zero(&db[..ps_len]) || db[ps_len] != 0x01 {
1653        return Err(Error::CryptoFailure("RSA verification failed"));
1654    }
1655    let salt = &db[db.len() - salt_len..];
1656
1657    let mut m_prime = vec![0_u8; 8];
1658    m_prime.extend_from_slice(m_hash);
1659    m_prime.extend_from_slice(salt);
1660    let expected_h = noxtls_sha384(&m_prime);
1661    if ct_bytes_eq(expected_h.as_slice(), h) {
1662        Ok(())
1663    } else {
1664        Err(Error::CryptoFailure("RSA verification failed"))
1665    }
1666}
1667
1668fn emsa_pss_verify(
1669    m_hash: &[u8],
1670    em: &[u8],
1671    em_bits: usize,
1672    salt_len: usize,
1673    message_hash: RsaPssHashAlgorithm,
1674    mgf_hash: RsaPssHashAlgorithm,
1675) -> Result<()> {
1676    let hash_len = m_hash.len();
1677    if em.len() < hash_len + salt_len + 2 {
1678        return Err(Error::InvalidLength("rsa modulus too short for pss"));
1679    }
1680    if em.last().copied() != Some(0xbc) {
1681        return Err(Error::CryptoFailure("RSA verification failed"));
1682    }
1683
1684    let db_len = em.len() - hash_len - 1;
1685    let (masked_db, rest) = em.split_at(db_len);
1686    let h = &rest[..hash_len];
1687
1688    let unused_bits = 8 * em.len() - em_bits;
1689    if unused_bits > 0 {
1690        let mask = 0xff_u8 << (8 - unused_bits);
1691        if masked_db[0] & mask != 0 {
1692            return Err(Error::CryptoFailure("RSA verification failed"));
1693        }
1694    }
1695
1696    let db_mask = mgf1_hash(mgf_hash, h, db_len)?;
1697    let mut db = masked_db.to_vec();
1698    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1699        *byte ^= *mask;
1700    }
1701    if unused_bits > 0 {
1702        db[0] &= 0xff_u8 >> unused_bits;
1703    }
1704
1705    let ps_len = em.len() - hash_len - salt_len - 2;
1706    if !ct_all_zero(&db[..ps_len]) || db[ps_len] != 0x01 {
1707        return Err(Error::CryptoFailure("RSA verification failed"));
1708    }
1709    let salt = &db[db.len() - salt_len..];
1710
1711    let mut m_prime = vec![0_u8; 8];
1712    m_prime.extend_from_slice(m_hash);
1713    m_prime.extend_from_slice(salt);
1714    let expected_h = rsa_pss_hash(message_hash, &m_prime);
1715    if ct_bytes_eq(expected_h.as_slice(), h) {
1716        Ok(())
1717    } else {
1718        Err(Error::CryptoFailure("RSA verification failed"))
1719    }
1720}
1721
1722fn rsa_pss_hash(hash: RsaPssHashAlgorithm, input: &[u8]) -> Vec<u8> {
1723    match hash {
1724        RsaPssHashAlgorithm::Sha1 => noxtls_sha1(input).to_vec(),
1725        RsaPssHashAlgorithm::Sha256 => noxtls_sha256(input).to_vec(),
1726        RsaPssHashAlgorithm::Sha384 => noxtls_sha384(input).to_vec(),
1727        RsaPssHashAlgorithm::Sha512 => noxtls_sha512(input).to_vec(),
1728    }
1729}
1730
1731fn mgf1_hash(hash: RsaPssHashAlgorithm, seed: &[u8], out_len: usize) -> Result<Vec<u8>> {
1732    match hash {
1733        RsaPssHashAlgorithm::Sha1 => mgf1(seed, out_len, RsaPssHashAlgorithm::Sha1),
1734        RsaPssHashAlgorithm::Sha256 => mgf1_sha256(seed, out_len),
1735        RsaPssHashAlgorithm::Sha384 => mgf1_sha384(seed, out_len),
1736        RsaPssHashAlgorithm::Sha512 => mgf1(seed, out_len, RsaPssHashAlgorithm::Sha512),
1737    }
1738}
1739
1740fn mgf1(seed: &[u8], out_len: usize, hash: RsaPssHashAlgorithm) -> Result<Vec<u8>> {
1741    let mut out = Vec::with_capacity(out_len);
1742    let mut counter = 0_u32;
1743    while out.len() < out_len {
1744        if counter == u32::MAX {
1745            return Err(Error::InvalidLength("mgf1 output too large"));
1746        }
1747        let mut block_input = Vec::with_capacity(seed.len() + 4);
1748        block_input.extend_from_slice(seed);
1749        block_input.extend_from_slice(&counter.to_be_bytes());
1750        out.extend_from_slice(&rsa_pss_hash(hash, &block_input));
1751        counter = counter.wrapping_add(1);
1752    }
1753    out.truncate(out_len);
1754    Ok(out)
1755}
1756
1757/// Implements MGF1 using SHA-256. Parameters: `seed` mask-generation seed and `out_len` requested mask length.
1758///
1759/// # Arguments
1760///
1761/// * `seed` — `&[u8]`.
1762/// * `out_len` — `usize`.
1763///
1764/// # Returns
1765///
1766/// On success, the `Ok` payload from `mgf1_sha256`; see implementation for value shape.
1767///
1768/// # Errors
1769///
1770/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1771///
1772/// # Panics
1773///
1774/// This function does not panic unless otherwise noted.
1775fn mgf1_sha256(seed: &[u8], out_len: usize) -> Result<Vec<u8>> {
1776    let mut out = Vec::with_capacity(out_len);
1777    let mut counter = 0_u32;
1778    while out.len() < out_len {
1779        if counter == u32::MAX {
1780            return Err(Error::InvalidLength("mgf1 output too large"));
1781        }
1782        let mut block_input = Vec::with_capacity(seed.len() + 4);
1783        block_input.extend_from_slice(seed);
1784        block_input.extend_from_slice(&counter.to_be_bytes());
1785        out.extend_from_slice(&noxtls_sha256(&block_input));
1786        counter = counter.wrapping_add(1);
1787    }
1788    out.truncate(out_len);
1789    Ok(out)
1790}
1791
1792/// Implements MGF1 using SHA-384.
1793///
1794/// # Arguments
1795///
1796/// * `seed` — `&[u8]`.
1797/// * `out_len` — `usize`.
1798///
1799/// # Returns
1800///
1801/// On success, the `Ok` payload from `mgf1_sha384`; see implementation for value shape.
1802///
1803/// # Errors
1804///
1805/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1806///
1807/// # Panics
1808///
1809/// This function does not panic unless otherwise noted.
1810fn mgf1_sha384(seed: &[u8], out_len: usize) -> Result<Vec<u8>> {
1811    let mut out = Vec::with_capacity(out_len);
1812    let mut counter = 0_u32;
1813    while out.len() < out_len {
1814        if counter == u32::MAX {
1815            return Err(Error::InvalidLength("mgf1 output too large"));
1816        }
1817        let mut block_input = Vec::with_capacity(seed.len() + 4);
1818        block_input.extend_from_slice(seed);
1819        block_input.extend_from_slice(&counter.to_be_bytes());
1820        out.extend_from_slice(&noxtls_sha384(&block_input));
1821        counter = counter.wrapping_add(1);
1822    }
1823    out.truncate(out_len);
1824    Ok(out)
1825}
1826
1827/// Builds DRBG-backed non-zero PKCS#1 v1.5 padding bytes for encryption.
1828///
1829/// # Arguments
1830///
1831/// * `drbg` — `&mut HmacDrbgSha256`.
1832/// * `len` — `usize`.
1833///
1834/// # Returns
1835///
1836/// On success, the `Ok` payload from `drbg_nonzero_padding`; see implementation for value shape.
1837///
1838/// # Errors
1839///
1840/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1841///
1842/// # Panics
1843///
1844/// This function does not panic unless otherwise noted.
1845fn drbg_nonzero_padding(drbg: &mut HmacDrbgSha256, len: usize) -> Result<Vec<u8>> {
1846    let mut out = Vec::with_capacity(len);
1847    while out.len() < len {
1848        let block = drbg.generate(len.saturating_sub(out.len()), b"rsa_pkcs1_v15_ps")?;
1849        for byte in block {
1850            if byte != 0 {
1851                out.push(byte);
1852                if out.len() == len {
1853                    break;
1854                }
1855            }
1856        }
1857    }
1858    Ok(out)
1859}
1860
1861/// Encodes a message with EME-OAEP-SHA256 using a caller-provided seed.
1862///
1863/// # Arguments
1864///
1865/// * `plaintext` — Message bytes to encode.
1866/// * `label` — OAEP label bytes.
1867/// * `seed` — 32-byte seed (must match `HASH_LEN`).
1868/// * `k` — Modulus byte length (encoded message size target).
1869///
1870/// # Returns
1871///
1872/// On success, the encoded message as a byte vector of length `k`.
1873///
1874/// # Errors
1875///
1876/// Returns `noxtls_core::Error` when lengths are inconsistent with OAEP-SHA256 constraints.
1877///
1878/// # Panics
1879///
1880/// This function does not panic.
1881fn emea_oaep_encode_sha256(
1882    plaintext: &[u8],
1883    label: &[u8],
1884    seed: &[u8],
1885    k: usize,
1886) -> Result<Vec<u8>> {
1887    const HASH_LEN: usize = 32;
1888    if seed.len() != HASH_LEN {
1889        return Err(Error::InvalidLength("rsa oaep seed must be 32 bytes"));
1890    }
1891    if k < (2 * HASH_LEN + 2) {
1892        return Err(Error::InvalidLength(
1893            "rsa modulus too short for oaep sha256",
1894        ));
1895    }
1896    if plaintext.len() > k - (2 * HASH_LEN + 2) {
1897        return Err(Error::InvalidLength(
1898            "rsa plaintext too long for oaep sha256",
1899        ));
1900    }
1901    let l_hash = noxtls_sha256(label);
1902    let ps_len = k - plaintext.len() - (2 * HASH_LEN + 2);
1903    let mut db = Vec::with_capacity(k - HASH_LEN - 1);
1904    db.extend_from_slice(&l_hash);
1905    db.extend(core::iter::repeat(0_u8).take(ps_len));
1906    db.push(0x01);
1907    db.extend_from_slice(plaintext);
1908    let db_mask = mgf1_sha256(seed, k - HASH_LEN - 1)?;
1909    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1910        *byte ^= *mask;
1911    }
1912    let seed_mask = mgf1_sha256(&db, HASH_LEN)?;
1913    let mut masked_seed = seed.to_vec();
1914    for (byte, mask) in masked_seed.iter_mut().zip(seed_mask.iter()) {
1915        *byte ^= *mask;
1916    }
1917    let mut em = Vec::with_capacity(k);
1918    em.push(0x00);
1919    em.extend_from_slice(&masked_seed);
1920    em.extend_from_slice(&db);
1921    Ok(em)
1922}
1923
1924/// Decodes EME-OAEP encoded bytes using SHA-256 and caller-provided label.
1925///
1926/// # Arguments
1927///
1928/// * `encoded` — `&[u8]`.
1929/// * `label` — `&[u8]`.
1930///
1931/// # Returns
1932///
1933/// On success, the `Ok` payload from `decode_oaep_sha256_plaintext`; see implementation for value shape.
1934///
1935/// # Errors
1936///
1937/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1938///
1939/// # Panics
1940///
1941/// This function does not panic unless otherwise noted.
1942fn decode_oaep_sha256_plaintext(encoded: &[u8], label: &[u8]) -> Result<Vec<u8>> {
1943    const HASH_LEN: usize = 32;
1944    if encoded.len() < (2 * HASH_LEN + 2) {
1945        return Err(Error::InvalidLength(
1946            "rsa modulus too short for oaep sha256",
1947        ));
1948    }
1949    let mut invalid = 0_u8;
1950    invalid |= encoded[0];
1951    let (masked_seed, masked_db) = encoded[1..].split_at(HASH_LEN);
1952    let seed_mask = mgf1_sha256(masked_db, HASH_LEN)?;
1953    let mut seed = masked_seed.to_vec();
1954    for (byte, mask) in seed.iter_mut().zip(seed_mask.iter()) {
1955        *byte ^= *mask;
1956    }
1957    let db_mask = mgf1_sha256(&seed, masked_db.len())?;
1958    let mut db = masked_db.to_vec();
1959    for (byte, mask) in db.iter_mut().zip(db_mask.iter()) {
1960        *byte ^= *mask;
1961    }
1962    let expected_l_hash = noxtls_sha256(label);
1963    invalid |= u8::from(!ct_bytes_eq(&db[..HASH_LEN], expected_l_hash.as_slice()));
1964    let rest = &db[HASH_LEN..];
1965    let mut marker_idx = 0_usize;
1966    let mut found_marker = 0_u8;
1967    let mut invalid_ps = 0_u8;
1968    for (idx, &byte) in rest.iter().enumerate() {
1969        let is_zero = u8::from(byte == 0);
1970        let is_one = u8::from(byte == 1);
1971        let before_marker = 1_u8 ^ found_marker;
1972        let should_set = before_marker & is_one;
1973        marker_idx = ct_select_usize(should_set, idx, marker_idx);
1974        invalid_ps |= before_marker & (1_u8 ^ is_zero) & (1_u8 ^ is_one);
1975        found_marker |= is_one;
1976    }
1977    invalid |= invalid_ps;
1978    invalid |= 1_u8 ^ found_marker;
1979    if invalid != 0 {
1980        return Err(Error::CryptoFailure("rsa decryption failed"));
1981    }
1982    Ok(rest[marker_idx.saturating_add(1)..].to_vec())
1983}
1984
1985/// Decodes PKCS#1 v1.5 encoded message and returns plaintext.
1986///
1987/// # Arguments
1988///
1989/// * `encoded` — `&[u8]`.
1990///
1991/// # Returns
1992///
1993/// On success, the `Ok` payload from `decode_pkcs1_v15_plaintext`; see implementation for value shape.
1994///
1995/// # Errors
1996///
1997/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
1998///
1999/// # Panics
2000///
2001/// This function does not panic unless otherwise noted.
2002fn decode_pkcs1_v15_plaintext(encoded: &[u8]) -> Result<Vec<u8>> {
2003    if encoded.len() < 11 {
2004        return Err(Error::CryptoFailure("rsa decryption failed"));
2005    }
2006    let mut invalid = 0_u8;
2007    invalid |= encoded[0];
2008    invalid |= encoded[1] ^ 0x02;
2009
2010    let mut sep_idx = 0_usize;
2011    let mut found_sep = 0_u8;
2012    for (idx, &byte) in encoded.iter().enumerate().skip(2) {
2013        let is_zero = u8::from(byte == 0);
2014        let should_set = is_zero & (1_u8 ^ found_sep);
2015        sep_idx = ct_select_usize(should_set, idx, sep_idx);
2016        found_sep |= is_zero;
2017    }
2018    if found_sep == 0 {
2019        invalid |= 1;
2020    }
2021    if sep_idx < 10 {
2022        invalid |= 1;
2023    }
2024    if invalid != 0 {
2025        return Err(Error::CryptoFailure("rsa decryption failed"));
2026    }
2027    Ok(encoded[sep_idx + 1..].to_vec())
2028}
2029
2030/// Compares two byte slices in constant-time when lengths are equal. Parameters: `left` and `right` byte slices to compare.
2031///
2032/// # Arguments
2033///
2034/// * `left` — `&[u8]`.
2035/// * `right` — `&[u8]`.
2036///
2037/// # Returns
2038///
2039/// `bool` produced by `ct_bytes_eq` (see implementation).
2040///
2041/// # Panics
2042///
2043/// This function does not panic unless otherwise noted.
2044fn ct_bytes_eq(left: &[u8], right: &[u8]) -> bool {
2045    if left.len() != right.len() {
2046        return false;
2047    }
2048    let mut diff = 0_u8;
2049    for (&l, &r) in left.iter().zip(right.iter()) {
2050        diff |= l ^ r;
2051    }
2052    diff == 0
2053}
2054
2055/// Returns true when every byte in one slice is zero without early exit. Parameter: `bytes` candidate slice.
2056///
2057/// # Arguments
2058///
2059/// * `bytes` — `&[u8]`.
2060///
2061/// # Returns
2062///
2063/// `bool` produced by `ct_all_zero` (see implementation).
2064///
2065/// # Panics
2066///
2067/// This function does not panic unless otherwise noted.
2068fn ct_all_zero(bytes: &[u8]) -> bool {
2069    let mut acc = 0_u8;
2070    for &byte in bytes {
2071        acc |= byte;
2072    }
2073    acc == 0
2074}
2075
2076/// Selects one of two usize values using one-byte selector without branch-on-secret. Parameters: `selector` must be 0 or 1, `if_one` selected when 1, `if_zero` when 0.
2077///
2078/// # Arguments
2079///
2080/// * `selector` — `u8`.
2081/// * `if_one` — `usize`.
2082/// * `if_zero` — `usize`.
2083///
2084/// # Returns
2085///
2086/// `usize` produced by `ct_select_usize` (see implementation).
2087///
2088/// # Panics
2089///
2090/// This function does not panic unless otherwise noted.
2091fn ct_select_usize(selector: u8, if_one: usize, if_zero: usize) -> usize {
2092    let mask = (0_usize).wrapping_sub(usize::from(selector));
2093    (if_one & mask) | (if_zero & !mask)
2094}
2095
2096/// Validates RSA private-key scalar components before private operations.
2097///
2098/// # Arguments
2099///
2100/// * `n` — `&BigUint`.
2101/// * `d` — `&BigUint`.
2102///
2103/// # Returns
2104///
2105/// On success, the `Ok` payload from `validate_private_components`; see implementation for value shape.
2106///
2107/// # Errors
2108///
2109/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
2110///
2111/// # Panics
2112///
2113/// This function does not panic unless otherwise noted.
2114fn validate_private_components(n: &BigUint, d: &BigUint) -> Result<()> {
2115    validate_modulus(n)?;
2116    if d.is_zero() {
2117        return Err(Error::CryptoFailure(
2118            "rsa private exponent must be non-zero",
2119        ));
2120    }
2121    if !d.is_odd() {
2122        return Err(Error::CryptoFailure("rsa private exponent must be odd"));
2123    }
2124    if d.cmp(n).is_ge() {
2125        return Err(Error::CryptoFailure(
2126            "rsa private exponent must be smaller than modulus",
2127        ));
2128    }
2129    Ok(())
2130}
2131
2132/// Validates RSA public-key scalar components before public operations.
2133///
2134/// # Arguments
2135///
2136/// * `n` — `&BigUint`.
2137/// * `e` — `&BigUint`.
2138///
2139/// # Returns
2140///
2141/// On success, the `Ok` payload from `validate_public_components`; see implementation for value shape.
2142///
2143/// # Errors
2144///
2145/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
2146///
2147/// # Panics
2148///
2149/// This function does not panic unless otherwise noted.
2150fn validate_public_components(n: &BigUint, e: &BigUint) -> Result<()> {
2151    validate_modulus(n)?;
2152    let three = BigUint::from_u128(3);
2153    if e.cmp(&three).is_lt() {
2154        return Err(Error::CryptoFailure(
2155            "rsa public exponent must be at least 3",
2156        ));
2157    }
2158    if !e.is_odd() {
2159        return Err(Error::CryptoFailure("rsa public exponent must be odd"));
2160    }
2161    if e.cmp(n).is_ge() {
2162        return Err(Error::CryptoFailure(
2163            "rsa public exponent must be smaller than modulus",
2164        ));
2165    }
2166    Ok(())
2167}
2168
2169/// Validates shared modulus requirements for RSA public/private keys.
2170///
2171/// # Arguments
2172///
2173/// * `n` — `&BigUint`.
2174///
2175/// # Returns
2176///
2177/// On success, the `Ok` payload from `validate_modulus`; see implementation for value shape.
2178///
2179/// # Errors
2180///
2181/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
2182///
2183/// # Panics
2184///
2185/// This function does not panic unless otherwise noted.
2186fn validate_modulus(n: &BigUint) -> Result<()> {
2187    let three = BigUint::from_u128(3);
2188    if n.cmp(&three).is_lt() {
2189        return Err(Error::CryptoFailure("rsa modulus must be greater than 3"));
2190    }
2191    if !n.is_odd() {
2192        return Err(Error::CryptoFailure("rsa modulus must be odd"));
2193    }
2194    Ok(())
2195}
2196
2197/// Validates CRT parameter relationships for a private RSA key.
2198///
2199/// # Arguments
2200///
2201/// * `n` — `&BigUint`.
2202/// * `crt` — `&RsaPrivateCrtComponents`.
2203///
2204/// # Returns
2205///
2206/// On success, the `Ok` payload from `validate_crt_components`; see implementation for value shape.
2207///
2208/// # Errors
2209///
2210/// Returns `noxtls_core::Error` when validation or a numeric step fails; see implementation for specific variants.
2211///
2212/// # Panics
2213///
2214/// This function does not panic unless otherwise noted.
2215fn validate_crt_components(n: &BigUint, crt: &RsaPrivateCrtComponents) -> Result<()> {
2216    if crt.p.is_zero()
2217        || crt.q.is_zero()
2218        || crt.dp.is_zero()
2219        || crt.dq.is_zero()
2220        || crt.qinv.is_zero()
2221    {
2222        return Err(Error::CryptoFailure("rsa crt parameters must be non-zero"));
2223    }
2224    if !crt.p.is_odd() || !crt.q.is_odd() {
2225        return Err(Error::CryptoFailure("rsa crt primes must be odd"));
2226    }
2227    if crt.p.mul(&crt.q).cmp(n).is_ne() {
2228        return Err(Error::CryptoFailure(
2229            "rsa crt prime product must equal modulus",
2230        ));
2231    }
2232    if crt.dp.cmp(&crt.p).is_ge() || crt.dq.cmp(&crt.q).is_ge() {
2233        return Err(Error::CryptoFailure("rsa crt exponents must be reduced"));
2234    }
2235    if crt.qinv.cmp(&crt.p).is_ge() {
2236        return Err(Error::CryptoFailure(
2237            "rsa crt coefficient must be smaller than p",
2238        ));
2239    }
2240    let one = BigUint::one();
2241    if crt.q.mul(&crt.qinv).modulo(&crt.p).cmp(&one).is_ne() {
2242        return Err(Error::CryptoFailure(
2243            "rsa crt coefficient must be inverse of q modulo p",
2244        ));
2245    }
2246    Ok(())
2247}
2248
2249/// Samples odd RSA prime candidates until one passes primality and coprimality checks.
2250///
2251/// # Arguments
2252///
2253/// * `bits` — Desired prime bit width.
2254/// * `e` — Public exponent used for the gcd(`p-1`, `e`) test.
2255/// * `drbg` — DRBG source for random candidates.
2256///
2257/// # Returns
2258///
2259/// On success, a probable prime `BigUint` of the requested width.
2260///
2261/// # Errors
2262///
2263/// Returns `noxtls_core::Error` when randomness is unavailable or generation exhausts its attempt budget.
2264///
2265/// # Panics
2266///
2267/// This function does not panic.
2268fn generate_rsa_prime_candidate_auto(
2269    bits: usize,
2270    e: &BigUint,
2271    drbg: &mut HmacDrbgSha256,
2272) -> Result<BigUint> {
2273    let one = BigUint::one();
2274    let mut attempts = 0_u32;
2275    while attempts < 20_000 {
2276        let candidate = random_biguint_with_bits(bits, drbg, b"rsa_prime_candidate")?;
2277        if candidate.bit_len() != bits {
2278            attempts = attempts.saturating_add(1);
2279            continue;
2280        }
2281        if !is_probable_prime(&candidate) {
2282            attempts = attempts.saturating_add(1);
2283            continue;
2284        }
2285        let pm1 = candidate.sub(&one);
2286        if BigUint::gcd(e, &pm1).cmp(&one).is_eq() {
2287            return Ok(candidate);
2288        }
2289        attempts = attempts.saturating_add(1);
2290    }
2291    Err(Error::StateError(
2292        "rsa prime generation exhausted attempt budget",
2293    ))
2294}
2295
2296/// Samples a random odd `BigUint` with an exact bit width from DRBG output.
2297///
2298/// # Arguments
2299///
2300/// * `bits` — Target bit width (at least 2).
2301/// * `drbg` — DRBG used to draw random bytes.
2302/// * `label` — Domain separation label passed to `drbg.generate`.
2303///
2304/// # Returns
2305///
2306/// On success, an odd integer occupying exactly `bits` bits.
2307///
2308/// # Errors
2309///
2310/// Returns `noxtls_core::Error` when `bits` is too small or DRBG output is insufficient.
2311///
2312/// # Panics
2313///
2314/// This function does not panic.
2315fn random_biguint_with_bits(
2316    bits: usize,
2317    drbg: &mut HmacDrbgSha256,
2318    label: &[u8],
2319) -> Result<BigUint> {
2320    if bits < 2 {
2321        return Err(Error::InvalidLength(
2322            "rsa prime candidate bits must be at least 2",
2323        ));
2324    }
2325    let byte_len = bits.div_ceil(8);
2326    let mut random = drbg.generate(byte_len, label)?;
2327    let top_bits = bits % 8;
2328    if top_bits != 0 {
2329        random[0] &= (1_u8 << top_bits) - 1;
2330    }
2331    let high_bit_index = (bits - 1) % 8;
2332    random[0] |= 1_u8 << high_bit_index;
2333    let last = random.len() - 1;
2334    random[last] |= 1;
2335    Ok(BigUint::from_be_bytes(&random))
2336}
2337
2338/// Performs probabilistic primality check for BigUint candidates.
2339///
2340/// # Arguments
2341///
2342/// * `n` — `&BigUint`.
2343///
2344/// # Returns
2345///
2346/// `bool` produced by `is_probable_prime` (see implementation).
2347///
2348/// # Panics
2349///
2350/// This function does not panic unless otherwise noted.
2351fn is_probable_prime(n: &BigUint) -> bool {
2352    let two = BigUint::from_u128(2);
2353    if n.cmp(&two).is_lt() {
2354        return false;
2355    }
2356    for small in [2_u32, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
2357        let small_bn = BigUint::from_u128(u128::from(small));
2358        if n.cmp(&small_bn).is_eq() {
2359            return true;
2360        }
2361        if n.mod_u32(small) == 0 {
2362            return false;
2363        }
2364    }
2365    let one = BigUint::one();
2366    let n_minus_one = n.sub(&one);
2367    let mut d = n_minus_one.clone();
2368    let mut s = 0_u32;
2369    while d.is_even() {
2370        d = d.shr1();
2371        s = s.saturating_add(1);
2372    }
2373    for witness in [2_u32, 3, 5, 7, 11, 13, 17, 19, 23, 29] {
2374        if !miller_rabin_round(n, &d, s, witness) {
2375            return false;
2376        }
2377    }
2378    true
2379}
2380
2381/// Runs one Miller-Rabin witness round for one odd candidate.
2382///
2383/// # Arguments
2384///
2385/// * `n` — `&BigUint`.
2386/// * `d` — `&BigUint`.
2387/// * `s` — `u32`.
2388/// * `witness` — `u32`.
2389///
2390/// # Returns
2391///
2392/// `bool` produced by `miller_rabin_round` (see implementation).
2393///
2394/// # Panics
2395///
2396/// This function does not panic unless otherwise noted.
2397fn miller_rabin_round(n: &BigUint, d: &BigUint, s: u32, witness: u32) -> bool {
2398    let a = BigUint::from_u128(u128::from(witness)).modulo(n);
2399    if a.is_zero() {
2400        return true;
2401    }
2402    let one = BigUint::one();
2403    let n_minus_one = n.sub(&one);
2404    let mut x = BigUint::mod_exp(&a, d, n);
2405    if x.cmp(&one).is_eq() || x.cmp(&n_minus_one).is_eq() {
2406        return true;
2407    }
2408    for _ in 1..s {
2409        x = x.mul(&x).modulo(n);
2410        if x.cmp(&n_minus_one).is_eq() {
2411            return true;
2412        }
2413    }
2414    false
2415}