ssh_key/public/
rsa.rs

1//! Rivest–Shamir–Adleman (RSA) public keys.
2
3use crate::{Error, Mpint, Result};
4use core::hash::{Hash, Hasher};
5use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
6
7#[cfg(feature = "rsa")]
8use {
9    crate::private::RsaKeypair,
10    encoding::Uint,
11    rsa::{pkcs1v15, traits::PublicKeyParts},
12    sha2::{Digest, digest::const_oid::AssociatedOid},
13};
14
15/// RSA public key.
16///
17/// Described in [RFC4253 § 6.6](https://datatracker.ietf.org/doc/html/rfc4253#section-6.6).
18#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
19pub struct RsaPublicKey {
20    /// RSA public exponent.
21    e: Mpint,
22
23    /// RSA modulus.
24    n: Mpint,
25
26    /// Length of this key in bits.
27    bits: u32,
28}
29
30impl RsaPublicKey {
31    /// Minimum allowed RSA key size.
32    #[cfg(feature = "rsa")]
33    pub(crate) const MIN_KEY_SIZE: usize = RsaKeypair::MIN_KEY_SIZE;
34
35    /// Create a new [`RsaPublicKey`] with the given components:
36    ///
37    /// - `e`: RSA public exponent.
38    /// - `n`: RSA modulus.
39    pub fn new(e: Mpint, n: Mpint) -> Result<Self> {
40        if !e.is_positive() {
41            return Err(Error::FormatEncoding);
42        }
43
44        let bits = match n.as_positive_bytes() {
45            Some(bytes) => bytes
46                .len()
47                .checked_mul(8)
48                .and_then(|bits| u32::try_from(bits).ok())
49                .ok_or(Error::FormatEncoding)?,
50            None => return Err(Error::FormatEncoding),
51        };
52
53        Ok(Self { e, n, bits })
54    }
55
56    /// Get the RSA public exponent.
57    pub fn e(&self) -> &Mpint {
58        &self.e
59    }
60
61    /// Get the RSA modulus.
62    pub fn n(&self) -> &Mpint {
63        &self.n
64    }
65
66    /// Get the size of the RSA modulus in bits.
67    pub fn key_size(&self) -> u32 {
68        self.bits
69    }
70}
71
72impl Decode for RsaPublicKey {
73    type Error = Error;
74
75    fn decode(reader: &mut impl Reader) -> Result<Self> {
76        let e = Mpint::decode(reader)?;
77        let n = Mpint::decode(reader)?;
78        Self::new(e, n)
79    }
80}
81
82impl Encode for RsaPublicKey {
83    fn encoded_len(&self) -> encoding::Result<usize> {
84        [self.e.encoded_len()?, self.n.encoded_len()?].checked_sum()
85    }
86
87    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
88        self.e.encode(writer)?;
89        self.n.encode(writer)
90    }
91}
92
93impl Hash for RsaPublicKey {
94    #[inline]
95    fn hash<H: Hasher>(&self, state: &mut H) {
96        self.e.as_bytes().hash(state);
97        self.n.as_bytes().hash(state);
98    }
99}
100
101#[cfg(feature = "rsa")]
102impl TryFrom<RsaPublicKey> for rsa::RsaPublicKey {
103    type Error = Error;
104
105    fn try_from(key: RsaPublicKey) -> Result<rsa::RsaPublicKey> {
106        rsa::RsaPublicKey::try_from(&key)
107    }
108}
109
110#[cfg(feature = "rsa")]
111impl TryFrom<&RsaPublicKey> for rsa::RsaPublicKey {
112    type Error = Error;
113
114    fn try_from(key: &RsaPublicKey) -> Result<rsa::RsaPublicKey> {
115        let n = Uint::try_from(&key.n)?;
116        let e = Uint::try_from(&key.e)?;
117        let ret = rsa::RsaPublicKey::new(n, e).map_err(|_| Error::Crypto)?;
118
119        if ret.size().saturating_mul(8) >= RsaPublicKey::MIN_KEY_SIZE {
120            Ok(ret)
121        } else {
122            Err(Error::Crypto)
123        }
124    }
125}
126
127#[cfg(feature = "rsa")]
128impl TryFrom<rsa::RsaPublicKey> for RsaPublicKey {
129    type Error = Error;
130
131    fn try_from(key: rsa::RsaPublicKey) -> Result<RsaPublicKey> {
132        RsaPublicKey::try_from(&key)
133    }
134}
135
136#[cfg(feature = "rsa")]
137impl TryFrom<&rsa::RsaPublicKey> for RsaPublicKey {
138    type Error = Error;
139
140    fn try_from(key: &rsa::RsaPublicKey) -> Result<RsaPublicKey> {
141        let e = Mpint::try_from(key.e())?;
142        let n = Mpint::try_from(key.n().as_ref())?;
143        RsaPublicKey::new(e, n)
144    }
145}
146
147#[cfg(feature = "rsa")]
148impl<D> TryFrom<&RsaPublicKey> for pkcs1v15::VerifyingKey<D>
149where
150    D: Digest + AssociatedOid,
151{
152    type Error = Error;
153
154    fn try_from(key: &RsaPublicKey) -> Result<pkcs1v15::VerifyingKey<D>> {
155        Ok(pkcs1v15::VerifyingKey::new(key.try_into()?))
156    }
157}