paseto_v1/core/
public.rs

1use alloc::boxed::Box;
2#[cfg(feature = "signing")]
3use alloc::vec::Vec;
4
5use digest::Digest;
6use paseto_core::PasetoError;
7use paseto_core::key::HasKey;
8use paseto_core::pae::{WriteBytes, pre_auth_encode};
9use paseto_core::version::Public;
10use rsa::pss::Signature;
11#[cfg(feature = "signing")]
12use rsa::rand_core::OsRng;
13use rsa::traits::PublicKeyParts;
14
15#[cfg(feature = "signing")]
16use super::SecretKey;
17use super::{PublicKey, V1};
18
19impl HasKey<Public> for V1 {
20    type Key = PublicKey;
21
22    fn decode(bytes: &[u8]) -> Result<PublicKey, PasetoError> {
23        use rsa::pkcs8::spki::DecodePublicKey;
24
25        let key = if let Ok(key) = rsa::RsaPublicKey::from_public_key_der(bytes) {
26            key
27        } else {
28            let s = str::from_utf8(bytes).map_err(|_| PasetoError::InvalidKey)?;
29            rsa::RsaPublicKey::from_public_key_pem(s).map_err(|_| PasetoError::InvalidKey)?
30        };
31
32        if key.n().bits() != 2048 {
33            return Err(PasetoError::InvalidKey);
34        }
35
36        Ok(PublicKey(rsa::pss::VerifyingKey::new(key)))
37    }
38    fn encode(key: &PublicKey) -> Box<[u8]> {
39        use rsa::pkcs8::spki::EncodePublicKey;
40
41        key.0
42            .to_public_key_der()
43            .expect("encoding to spki der should succeed")
44            .into_vec()
45            .into_boxed_slice()
46    }
47}
48
49#[cfg(feature = "signing")]
50impl HasKey<paseto_core::version::Secret> for V1 {
51    type Key = SecretKey;
52
53    fn decode(bytes: &[u8]) -> Result<SecretKey, PasetoError> {
54        use rsa::pkcs1::DecodeRsaPrivateKey;
55
56        let key = if let Ok(key) = rsa::RsaPrivateKey::from_pkcs1_der(bytes) {
57            key
58        } else {
59            let s = str::from_utf8(bytes).map_err(|_| PasetoError::InvalidKey)?;
60            rsa::RsaPrivateKey::from_pkcs1_pem(s).map_err(|_| PasetoError::InvalidKey)?
61        };
62
63        if key.n().bits() != 2048 {
64            return Err(PasetoError::InvalidKey);
65        }
66
67        Ok(SecretKey(rsa::pss::SigningKey::new(key)))
68    }
69    fn encode(key: &SecretKey) -> Box<[u8]> {
70        use rsa::pkcs1::EncodeRsaPrivateKey;
71
72        key.0
73            .to_pkcs1_der()
74            .expect("encoding to pkcs1 der should succeed")
75            .as_bytes()
76            .to_vec()
77            .into_boxed_slice()
78    }
79}
80
81#[cfg(feature = "signing")]
82impl SecretKey {
83    pub(crate) fn random() -> Result<Self, PasetoError> {
84        use rsa::rand_core::OsRng;
85
86        rsa::pss::SigningKey::random(&mut OsRng, 2048)
87            .map_err(|_| PasetoError::InvalidKey)
88            .map(Self)
89    }
90}
91
92#[cfg(feature = "signing")]
93impl paseto_core::version::SealingVersion<Public> for V1 {
94    fn unsealing_key(key: &SecretKey) -> PublicKey {
95        use rsa::signature::Keypair;
96
97        PublicKey(key.0.verifying_key())
98    }
99
100    fn random() -> Result<SecretKey, PasetoError> {
101        SecretKey::random()
102    }
103
104    fn nonce() -> Result<Vec<u8>, PasetoError> {
105        Ok(Vec::with_capacity(96))
106    }
107
108    fn dangerous_seal_with_nonce(
109        key: &SecretKey,
110        encoding: &'static str,
111        mut payload: Vec<u8>,
112        footer: &[u8],
113        aad: &[u8],
114    ) -> Result<Vec<u8>, PasetoError> {
115        use rsa::signature::RandomizedDigestSigner;
116
117        if !aad.is_empty() {
118            return Err(PasetoError::ClaimsError);
119        }
120
121        let digest = preauth_public(encoding, &payload, footer);
122        let signature: Box<[u8]> = key
123            .0
124            .try_sign_digest_with_rng(&mut OsRng, digest)
125            .map_err(|_| PasetoError::CryptoError)?
126            .into();
127
128        payload.extend_from_slice(&signature);
129
130        Ok(payload)
131    }
132}
133
134impl paseto_core::version::UnsealingVersion<Public> for V1 {
135    fn unseal<'a>(
136        key: &PublicKey,
137        encoding: &'static str,
138        payload: &'a mut [u8],
139        footer: &[u8],
140        aad: &[u8],
141    ) -> Result<&'a [u8], PasetoError> {
142        use rsa::signature::DigestVerifier;
143
144        if !aad.is_empty() {
145            return Err(PasetoError::ClaimsError);
146        }
147
148        let (cleartext, tag) = payload
149            .split_last_chunk::<256>()
150            .ok_or(PasetoError::InvalidToken)?;
151
152        let signature = Signature::try_from(&tag[..]).map_err(|_| PasetoError::InvalidToken)?;
153        let digest = preauth_public(encoding, cleartext, footer);
154        DigestVerifier::<sha2::Sha384, Signature>::verify_digest(&key.0, digest, &signature)
155            .map_err(|_| PasetoError::CryptoError)?;
156
157        Ok(cleartext)
158    }
159}
160fn preauth_public(encoding: &'static str, cleartext: &[u8], footer: &[u8]) -> sha2::Sha384 {
161    use paseto_core::key::KeyType;
162    struct Context(sha2::Sha384);
163    impl WriteBytes for Context {
164        fn write(&mut self, slice: &[u8]) {
165            self.0.update(slice);
166        }
167    }
168
169    let mut ctx = Context(sha2::Sha384::new());
170    pre_auth_encode(
171        [
172            &[
173                "v1".as_bytes(),
174                encoding.as_bytes(),
175                Public::HEADER.as_bytes(),
176            ],
177            &[cleartext],
178            &[footer],
179        ],
180        &mut ctx,
181    );
182    ctx.0
183}