Skip to main content

jwt_compact_preview/alg/
rsa.rs

1use rand::thread_rng;
2use rand_core::{CryptoRng, RngCore};
3use rsa::{hash::Hash, BigUint, PaddingScheme, PublicKey, RSAPrivateKey, RSAPublicKey};
4use sha2::{Digest, Sha256, Sha384, Sha512};
5use thiserror::Error;
6
7use std::borrow::Cow;
8
9use crate::{Algorithm, AlgorithmSignature};
10
11/// Errors that may occur during token parsing.
12#[derive(Debug, Error)]
13pub enum RsaError {
14    #[error("Unsupported signature length")]
15    UnsupportedSignatureLength,
16    #[error("Unsupported modulus size")]
17    UnsupportedModulusSize,
18    #[error("Invalid key: {0}")]
19    InvalidKey(rsa::errors::Error),
20    #[error("Key generation error: {0}")]
21    KeygenError(rsa::errors::Error),
22}
23
24#[derive(Debug)]
25pub struct Signature(Vec<u8>);
26
27impl AlgorithmSignature for Signature {
28    fn try_from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
29        match bytes.len() {
30            256 | 384 | 512 => Ok(Signature(bytes.to_vec())),
31            _ => Err(RsaError::UnsupportedSignatureLength.into()),
32        }
33    }
34
35    fn as_bytes(&self) -> Cow<[u8]> {
36        Cow::Owned(self.0.clone())
37    }
38}
39
40/// RSA padding algorithm.
41#[derive(Debug, Copy, Clone, Eq, PartialEq)]
42pub enum Padding {
43    /// PKCS1v1.5
44    Pkcs1v15,
45    /// PSS
46    Pss,
47}
48
49/// An RSA public key.
50#[derive(Debug, Clone, Eq, PartialEq)]
51pub struct RsaVerifyingKey(RSAPublicKey);
52
53impl AsRef<RSAPublicKey> for RsaVerifyingKey {
54    fn as_ref(&self) -> &RSAPublicKey {
55        &self.0
56    }
57}
58
59impl RsaVerifyingKey {
60    /// Create a verification key from a DER-encoded set of the parameters.
61    pub fn from_der(der: &[u8]) -> anyhow::Result<RsaVerifyingKey> {
62        match RSAPublicKey::from_pkcs8(&der) {
63            Err(e) => Err(RsaError::InvalidKey(e).into()),
64            Ok(key) => Ok(RsaVerifyingKey(key)),
65        }
66    }
67
68    /// Create a verification key from a modulus and a public exponent.
69    pub fn from_components(n: &[u8], e: &[u8]) -> anyhow::Result<RsaVerifyingKey> {
70        let n = BigUint::from_bytes_be(n);
71        let e = BigUint::from_bytes_be(e);
72        match RSAPublicKey::new(n, e) {
73            Err(e) => Err(RsaError::InvalidKey(e).into()),
74            Ok(key) => Ok(RsaVerifyingKey(key)),
75        }
76    }
77}
78
79/// An RSA signing key.
80#[derive(Debug)]
81pub struct RsaSigningKey(RSAPrivateKey);
82
83impl AsRef<RSAPrivateKey> for RsaSigningKey {
84    fn as_ref(&self) -> &RSAPrivateKey {
85        &self.0
86    }
87}
88
89impl RsaSigningKey {
90    /// Create a signing key from a DER-encoded set of the parameters.
91    pub fn from_der(der: &[u8]) -> anyhow::Result<RsaSigningKey> {
92        match RSAPrivateKey::from_pkcs8(&der) {
93            Err(e) => Err(RsaError::InvalidKey(e).into()),
94            Ok(key) => {
95                key.validate().map_err(RsaError::InvalidKey)?;
96                Ok(RsaSigningKey(key))
97            }
98        }
99    }
100
101    /// Convert a signing key to a verification key.
102    pub fn to_verifying_key(&self) -> RsaVerifyingKey {
103        RsaVerifyingKey(RSAPublicKey::from(&self.0))
104    }
105}
106
107/// Integrity algorithm using digital signatures on RSA-PKCS1v1.5 and SHA-256.
108///
109/// The name of the algorithm is specified as `RS256` as per the [IANA registry].
110///
111/// *This type is available if the crate is built with the `rsa` feature.*
112///
113/// [IANA registry]: https://www.iana.org/assignments/jose/jose.xhtml
114#[derive(Debug)]
115pub struct Rsa {
116    hash_alg: Hash,
117    padding_alg: Padding,
118}
119
120impl Rsa {
121    /// Create an instance using a specific hash function and padding
122    pub fn new(hash_alg: Hash, padding_alg: Padding) -> Self {
123        Rsa {
124            hash_alg,
125            padding_alg,
126        }
127    }
128}
129
130pub trait RsaVariant {
131    fn rsa() -> Rsa;
132}
133
134impl<T: RsaVariant> Algorithm for T {
135    type SigningKey = RsaSigningKey;
136    type VerifyingKey = RsaVerifyingKey;
137    type Signature = Signature;
138
139    fn name(&self) -> Cow<'static, str> {
140        Self::rsa().name()
141    }
142
143    fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
144        Self::rsa().sign(signing_key, message)
145    }
146
147    fn verify_signature(
148        &self,
149        signature: &Self::Signature,
150        verifying_key: &Self::VerifyingKey,
151        message: &[u8],
152    ) -> bool {
153        Self::rsa().verify_signature(signature, verifying_key, message)
154    }
155}
156
157impl Rsa {
158    fn hash(&self, message: &[u8]) -> Vec<u8> {
159        match self.hash_alg {
160            Hash::SHA2_256 => Sha256::digest(message).to_vec(),
161            Hash::SHA2_384 => Sha384::digest(message).to_vec(),
162            Hash::SHA2_512 => Sha512::digest(message).to_vec(),
163            _ => unreachable!(),
164        }
165    }
166
167    fn padding_scheme(&self) -> PaddingScheme {
168        match self.padding_alg {
169            Padding::Pkcs1v15 => PaddingScheme::new_pkcs1v15_sign(Some(self.hash_alg)),
170            Padding::Pss => {
171                let rng = rand_core::OsRng {};
172                match self.hash_alg {
173                    Hash::SHA2_256 => PaddingScheme::new_pss::<Sha256, _>(rng),
174                    Hash::SHA2_384 => PaddingScheme::new_pss::<Sha384, _>(rng),
175                    Hash::SHA2_512 => PaddingScheme::new_pss::<Sha512, _>(rng),
176                    _ => unreachable!(),
177                }
178            }
179        }
180    }
181
182    fn name(&self) -> Cow<'static, str> {
183        let name = match self.hash_alg {
184            Hash::SHA2_256 => "RS256",
185            Hash::SHA2_384 => "RS384",
186            Hash::SHA2_512 => "RS512",
187            _ => unreachable!(),
188        };
189        Cow::Borrowed(name)
190    }
191
192    fn sign(&self, signing_key: &RsaSigningKey, message: &[u8]) -> Signature {
193        let digest = self.hash(message);
194        let mut rng = thread_rng();
195        Signature(
196            signing_key
197                .as_ref()
198                .sign_blinded(&mut rng, self.padding_scheme(), &digest)
199                .expect("Unexpected RSA signature failure"),
200        )
201    }
202
203    fn verify_signature(
204        &self,
205        signature: &Signature,
206        verifying_key: &RsaVerifyingKey,
207        message: &[u8],
208    ) -> bool {
209        let digest = self.hash(message);
210        verifying_key
211            .as_ref()
212            .verify(self.padding_scheme(), &digest, &signature.0)
213            .is_ok()
214    }
215
216    /// Generate a new key pair.
217    pub fn generate<R: CryptoRng + RngCore>(
218        rng: &mut R,
219        modulus_bits: usize,
220    ) -> anyhow::Result<(RsaSigningKey, RsaVerifyingKey)> {
221        match modulus_bits {
222            2048 | 3072 | 4096 => {}
223            _ => return Err(RsaError::UnsupportedModulusSize.into()),
224        }
225        let signing_key = match RSAPrivateKey::new(rng, modulus_bits) {
226            Err(e) => return Err(RsaError::KeygenError(e).into()),
227            Ok(key) => RsaSigningKey(key),
228        };
229        let verifying_key = signing_key.to_verifying_key();
230        Ok((signing_key, verifying_key))
231    }
232}
233
234/// RSA-PKCS1v1.5 with SHA-256 as a hash function
235#[derive(Debug)]
236pub struct Rs256;
237
238impl RsaVariant for Rs256 {
239    fn rsa() -> Rsa {
240        Rsa::new(Hash::SHA2_256, Padding::Pkcs1v15)
241    }
242}
243
244/// RSA-PKCS1v1.5 with SHA-384 as a hash function
245#[derive(Debug)]
246pub struct Rs384;
247
248impl RsaVariant for Rs384 {
249    fn rsa() -> Rsa {
250        Rsa::new(Hash::SHA2_384, Padding::Pkcs1v15)
251    }
252}
253
254/// RSA-PKCS1v1.5 with SHA-512 as a hash function
255#[derive(Debug)]
256pub struct Rs512;
257
258impl RsaVariant for Rs512 {
259    fn rsa() -> Rsa {
260        Rsa::new(Hash::SHA2_512, Padding::Pkcs1v15)
261    }
262}
263
264/// RSASSA-PSS with SHA-256 as a hash function
265#[derive(Debug)]
266pub struct Ps256;
267
268impl RsaVariant for Ps256 {
269    fn rsa() -> Rsa {
270        Rsa::new(Hash::SHA2_256, Padding::Pss)
271    }
272}
273
274/// RSASSA-PSS with SHA-384 as a hash function
275#[derive(Debug)]
276pub struct Ps384;
277
278impl RsaVariant for Ps384 {
279    fn rsa() -> Rsa {
280        Rsa::new(Hash::SHA2_384, Padding::Pss)
281    }
282}
283
284/// RSASSA-PSS with SHA-512 as a hash function
285#[derive(Debug)]
286pub struct Ps512;
287
288impl RsaVariant for Ps512 {
289    fn rsa() -> Rsa {
290        Rsa::new(Hash::SHA2_512, Padding::Pss)
291    }
292}