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    rsa::{pkcs1v15, traits::PublicKeyParts},
11    sha2::{digest::const_oid::AssociatedOid, Digest},
12};
13
14/// RSA public key.
15///
16/// Described in [RFC4253 § 6.6](https://datatracker.ietf.org/doc/html/rfc4253#section-6.6).
17#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
18pub struct RsaPublicKey {
19    /// RSA public exponent.
20    pub e: Mpint,
21
22    /// RSA modulus.
23    pub n: Mpint,
24}
25
26impl RsaPublicKey {
27    /// Minimum allowed RSA key size.
28    #[cfg(feature = "rsa")]
29    pub(crate) const MIN_KEY_SIZE: usize = RsaKeypair::MIN_KEY_SIZE;
30}
31
32impl Decode for RsaPublicKey {
33    type Error = Error;
34
35    fn decode(reader: &mut impl Reader) -> Result<Self> {
36        let e = Mpint::decode(reader)?;
37        let n = Mpint::decode(reader)?;
38        Ok(Self { e, n })
39    }
40}
41
42impl Encode for RsaPublicKey {
43    fn encoded_len(&self) -> encoding::Result<usize> {
44        [self.e.encoded_len()?, self.n.encoded_len()?].checked_sum()
45    }
46
47    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
48        self.e.encode(writer)?;
49        self.n.encode(writer)
50    }
51}
52
53impl Hash for RsaPublicKey {
54    #[inline]
55    fn hash<H: Hasher>(&self, state: &mut H) {
56        self.e.as_bytes().hash(state);
57        self.n.as_bytes().hash(state);
58    }
59}
60
61#[cfg(feature = "rsa")]
62impl TryFrom<RsaPublicKey> for rsa::RsaPublicKey {
63    type Error = Error;
64
65    fn try_from(key: RsaPublicKey) -> Result<rsa::RsaPublicKey> {
66        rsa::RsaPublicKey::try_from(&key)
67    }
68}
69
70#[cfg(feature = "rsa")]
71impl TryFrom<&RsaPublicKey> for rsa::RsaPublicKey {
72    type Error = Error;
73
74    fn try_from(key: &RsaPublicKey) -> Result<rsa::RsaPublicKey> {
75        let ret = rsa::RsaPublicKey::new(
76            rsa::BigUint::try_from(&key.n)?,
77            rsa::BigUint::try_from(&key.e)?,
78        )
79        .map_err(|_| Error::Crypto)?;
80
81        if ret.size().saturating_mul(8) >= RsaPublicKey::MIN_KEY_SIZE {
82            Ok(ret)
83        } else {
84            Err(Error::Crypto)
85        }
86    }
87}
88
89#[cfg(feature = "rsa")]
90impl TryFrom<rsa::RsaPublicKey> for RsaPublicKey {
91    type Error = Error;
92
93    fn try_from(key: rsa::RsaPublicKey) -> Result<RsaPublicKey> {
94        RsaPublicKey::try_from(&key)
95    }
96}
97
98#[cfg(feature = "rsa")]
99impl TryFrom<&rsa::RsaPublicKey> for RsaPublicKey {
100    type Error = Error;
101
102    fn try_from(key: &rsa::RsaPublicKey) -> Result<RsaPublicKey> {
103        Ok(RsaPublicKey {
104            e: key.e().try_into()?,
105            n: key.n().try_into()?,
106        })
107    }
108}
109
110#[cfg(feature = "rsa")]
111impl<D> TryFrom<&RsaPublicKey> for pkcs1v15::VerifyingKey<D>
112where
113    D: Digest + AssociatedOid,
114{
115    type Error = Error;
116
117    fn try_from(key: &RsaPublicKey) -> Result<pkcs1v15::VerifyingKey<D>> {
118        Ok(pkcs1v15::VerifyingKey::new(key.try_into()?))
119    }
120}