ssh_key/private/
rsa.rs

1//! Rivest–Shamir–Adleman (RSA) private keys.
2
3use crate::{
4    checked::CheckedSum, decode::Decode, encode::Encode, public::RsaPublicKey, reader::Reader,
5    writer::Writer, MPInt, Result,
6};
7use core::fmt;
8use zeroize::Zeroize;
9
10#[cfg(feature = "rsa")]
11use {
12    crate::Error,
13    rand_core::{CryptoRng, RngCore},
14    rsa::PublicKeyParts,
15};
16
17#[cfg(feature = "subtle")]
18use subtle::{Choice, ConstantTimeEq};
19
20/// RSA private key.
21#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
22#[derive(Clone)]
23pub struct RsaPrivateKey {
24    /// RSA private exponent.
25    pub d: MPInt,
26
27    /// CRT coefficient: `(inverse of q) mod p`.
28    pub iqmp: MPInt,
29
30    /// First prime factor of `n`.
31    pub p: MPInt,
32
33    /// Second prime factor of `n`.
34    pub q: MPInt,
35}
36
37impl Decode for RsaPrivateKey {
38    fn decode(reader: &mut impl Reader) -> Result<Self> {
39        let d = MPInt::decode(reader)?;
40        let iqmp = MPInt::decode(reader)?;
41        let p = MPInt::decode(reader)?;
42        let q = MPInt::decode(reader)?;
43        Ok(Self { d, iqmp, p, q })
44    }
45}
46
47impl Encode for RsaPrivateKey {
48    fn encoded_len(&self) -> Result<usize> {
49        [
50            self.d.encoded_len()?,
51            self.iqmp.encoded_len()?,
52            self.p.encoded_len()?,
53            self.q.encoded_len()?,
54        ]
55        .checked_sum()
56    }
57
58    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
59        self.d.encode(writer)?;
60        self.iqmp.encode(writer)?;
61        self.p.encode(writer)?;
62        self.q.encode(writer)
63    }
64}
65
66impl Drop for RsaPrivateKey {
67    fn drop(&mut self) {
68        self.d.zeroize();
69        self.iqmp.zeroize();
70        self.p.zeroize();
71        self.q.zeroize();
72    }
73}
74
75#[cfg(feature = "subtle")]
76#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
77impl ConstantTimeEq for RsaPrivateKey {
78    fn ct_eq(&self, other: &Self) -> Choice {
79        self.d.ct_eq(&other.d)
80            & self.iqmp.ct_eq(&self.iqmp)
81            & self.p.ct_eq(&other.p)
82            & self.q.ct_eq(&other.q)
83    }
84}
85
86#[cfg(feature = "subtle")]
87#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
88impl PartialEq for RsaPrivateKey {
89    fn eq(&self, other: &Self) -> bool {
90        self.ct_eq(other).into()
91    }
92}
93
94#[cfg(feature = "subtle")]
95#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
96impl Eq for RsaPrivateKey {}
97
98/// RSA private/public keypair.
99#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
100#[derive(Clone)]
101pub struct RsaKeypair {
102    /// Public key.
103    pub public: RsaPublicKey,
104
105    /// Private key.
106    pub private: RsaPrivateKey,
107}
108
109impl RsaKeypair {
110    /// Minimum allowed RSA key size.
111    #[cfg(feature = "rsa")]
112    pub(crate) const MIN_KEY_SIZE: usize = 2048;
113
114    /// Generate a random RSA keypair of the given size.
115    #[cfg(feature = "rsa")]
116    #[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
117    pub fn random(mut rng: impl CryptoRng + RngCore, bit_size: usize) -> Result<Self> {
118        if bit_size >= Self::MIN_KEY_SIZE {
119            rsa::RsaPrivateKey::new(&mut rng, bit_size)?.try_into()
120        } else {
121            Err(Error::Crypto)
122        }
123    }
124}
125
126impl Decode for RsaKeypair {
127    fn decode(reader: &mut impl Reader) -> Result<Self> {
128        let n = MPInt::decode(reader)?;
129        let e = MPInt::decode(reader)?;
130        let public = RsaPublicKey { n, e };
131        let private = RsaPrivateKey::decode(reader)?;
132        Ok(RsaKeypair { public, private })
133    }
134}
135
136impl Encode for RsaKeypair {
137    fn encoded_len(&self) -> Result<usize> {
138        [
139            self.public.n.encoded_len()?,
140            self.public.e.encoded_len()?,
141            self.private.encoded_len()?,
142        ]
143        .checked_sum()
144    }
145
146    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
147        self.public.n.encode(writer)?;
148        self.public.e.encode(writer)?;
149        self.private.encode(writer)
150    }
151}
152
153impl From<RsaKeypair> for RsaPublicKey {
154    fn from(keypair: RsaKeypair) -> RsaPublicKey {
155        keypair.public
156    }
157}
158
159impl From<&RsaKeypair> for RsaPublicKey {
160    fn from(keypair: &RsaKeypair) -> RsaPublicKey {
161        keypair.public.clone()
162    }
163}
164
165impl fmt::Debug for RsaKeypair {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        f.debug_struct("RsaKeypair")
168            .field("public", &self.public)
169            .finish_non_exhaustive()
170    }
171}
172
173#[cfg(feature = "rsa")]
174#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
175impl TryFrom<RsaKeypair> for rsa::RsaPrivateKey {
176    type Error = Error;
177
178    fn try_from(key: RsaKeypair) -> Result<rsa::RsaPrivateKey> {
179        rsa::RsaPrivateKey::try_from(&key)
180    }
181}
182
183#[cfg(feature = "rsa")]
184#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
185impl TryFrom<&RsaKeypair> for rsa::RsaPrivateKey {
186    type Error = Error;
187
188    fn try_from(key: &RsaKeypair) -> Result<rsa::RsaPrivateKey> {
189        let ret = rsa::RsaPrivateKey::from_components(
190            rsa::BigUint::try_from(&key.public.n)?,
191            rsa::BigUint::try_from(&key.public.e)?,
192            rsa::BigUint::try_from(&key.private.d)?,
193            vec![
194                rsa::BigUint::try_from(&key.private.p)?,
195                rsa::BigUint::try_from(&key.private.p)?,
196            ],
197        );
198
199        if ret.size().saturating_mul(8) >= RsaKeypair::MIN_KEY_SIZE {
200            Ok(ret)
201        } else {
202            Err(Error::Crypto)
203        }
204    }
205}
206
207#[cfg(feature = "rsa")]
208#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
209impl TryFrom<rsa::RsaPrivateKey> for RsaKeypair {
210    type Error = Error;
211
212    fn try_from(key: rsa::RsaPrivateKey) -> Result<RsaKeypair> {
213        RsaKeypair::try_from(&key)
214    }
215}
216
217#[cfg(feature = "rsa")]
218#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
219impl TryFrom<&rsa::RsaPrivateKey> for RsaKeypair {
220    type Error = Error;
221
222    fn try_from(key: &rsa::RsaPrivateKey) -> Result<RsaKeypair> {
223        // Multi-prime keys are not supported
224        if key.primes().len() > 2 {
225            return Err(Error::Crypto);
226        }
227
228        let public = RsaPublicKey::try_from(key.to_public_key())?;
229
230        let p = &key.primes()[0];
231        let q = &key.primes()[1];
232        let iqmp = key.crt_coefficient().ok_or(Error::Crypto)?;
233
234        let private = RsaPrivateKey {
235            d: key.d().try_into()?,
236            iqmp: iqmp.try_into()?,
237            p: p.try_into()?,
238            q: q.try_into()?,
239        };
240
241        Ok(RsaKeypair { public, private })
242    }
243}
244
245#[cfg(feature = "subtle")]
246#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
247impl ConstantTimeEq for RsaKeypair {
248    fn ct_eq(&self, other: &Self) -> Choice {
249        Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private)
250    }
251}
252
253#[cfg(feature = "subtle")]
254#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
255impl PartialEq for RsaKeypair {
256    fn eq(&self, other: &Self) -> bool {
257        self.ct_eq(other).into()
258    }
259}
260
261#[cfg(feature = "subtle")]
262#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))]
263impl Eq for RsaKeypair {}