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