paseto_v3/core/
public.rs

1use alloc::boxed::Box;
2#[cfg(feature = "signing")]
3use alloc::vec::Vec;
4
5use digest::Digest;
6use p384::ecdsa::Signature;
7use paseto_core::PasetoError;
8use paseto_core::key::HasKey;
9use paseto_core::pae::{WriteBytes, pre_auth_encode};
10use paseto_core::version::Public;
11
12#[cfg(feature = "signing")]
13use super::SecretKey;
14use super::{PublicKey, V3};
15
16impl HasKey<Public> for V3 {
17    type Key = PublicKey;
18
19    fn decode(bytes: &[u8]) -> Result<PublicKey, PasetoError> {
20        p384::ecdsa::VerifyingKey::from_sec1_bytes(bytes)
21            .map(PublicKey)
22            .map_err(|_| PasetoError::InvalidKey)
23    }
24    fn encode(key: &PublicKey) -> Box<[u8]> {
25        key.0
26            .to_encoded_point(true)
27            .as_bytes()
28            .to_vec()
29            .into_boxed_slice()
30    }
31}
32
33#[cfg(feature = "signing")]
34impl HasKey<paseto_core::version::Secret> for V3 {
35    type Key = SecretKey;
36
37    fn decode(bytes: &[u8]) -> Result<SecretKey, PasetoError> {
38        if bytes.len() != 48 {
39            return Err(PasetoError::InvalidKey);
40        }
41        let sk = p384::SecretKey::from_slice(bytes).map_err(|_| PasetoError::InvalidKey)?;
42        Ok(SecretKey(sk.into()))
43    }
44    fn encode(key: &SecretKey) -> Box<[u8]> {
45        key.0.to_bytes().to_vec().into_boxed_slice()
46    }
47}
48
49#[cfg(feature = "signing")]
50impl SecretKey {
51    pub(crate) fn random() -> Result<Self, PasetoError> {
52        let mut bytes = generic_array::GenericArray::default();
53        loop {
54            getrandom::fill(&mut bytes).map_err(|_| PasetoError::CryptoError)?;
55            if let Ok(key) = p384::ecdsa::SigningKey::from_bytes(&bytes).map(Self) {
56                break Ok(key);
57            }
58        }
59    }
60}
61
62#[cfg(feature = "signing")]
63impl paseto_core::version::SealingVersion<Public> for V3 {
64    fn unsealing_key(key: &SecretKey) -> PublicKey {
65        PublicKey(*key.0.verifying_key())
66    }
67
68    fn random() -> Result<SecretKey, PasetoError> {
69        SecretKey::random()
70    }
71
72    fn nonce() -> Result<Vec<u8>, PasetoError> {
73        Ok(Vec::with_capacity(96))
74    }
75
76    fn dangerous_seal_with_nonce(
77        key: &SecretKey,
78        encoding: &'static str,
79        mut payload: Vec<u8>,
80        footer: &[u8],
81        aad: &[u8],
82    ) -> Result<Vec<u8>, PasetoError> {
83        use p384::ecdsa::signature::DigestSigner;
84
85        let digest = preauth_public(key.0.verifying_key(), encoding, &payload, footer, aad);
86        let signature: Signature = key.0.sign_digest(digest);
87        let signature = signature.normalize_s().unwrap_or(signature);
88
89        payload.extend_from_slice(&signature.to_bytes());
90
91        Ok(payload)
92    }
93}
94
95impl paseto_core::version::UnsealingVersion<Public> for V3 {
96    fn unseal<'a>(
97        key: &PublicKey,
98        encoding: &'static str,
99        payload: &'a mut [u8],
100        footer: &[u8],
101        aad: &[u8],
102    ) -> Result<&'a [u8], PasetoError> {
103        use p384::ecdsa::signature::DigestVerifier;
104
105        let (cleartext, tag) = payload
106            .split_last_chunk::<96>()
107            .ok_or(PasetoError::InvalidToken)?;
108
109        let signature =
110            Signature::from_bytes(tag[..].into()).map_err(|_| PasetoError::InvalidToken)?;
111        let digest = preauth_public(&key.0, encoding, cleartext, footer, aad);
112        DigestVerifier::<sha2::Sha384, Signature>::verify_digest(&key.0, digest, &signature)
113            .map_err(|_| PasetoError::CryptoError)?;
114
115        Ok(cleartext)
116    }
117}
118fn preauth_public(
119    key: &p384::ecdsa::VerifyingKey,
120    encoding: &'static str,
121    cleartext: &[u8],
122    footer: &[u8],
123    aad: &[u8],
124) -> sha2::Sha384 {
125    use paseto_core::key::KeyType;
126    struct Context(sha2::Sha384);
127    impl WriteBytes for Context {
128        fn write(&mut self, slice: &[u8]) {
129            self.0.update(slice);
130        }
131    }
132
133    let key = key.to_encoded_point(true);
134
135    let mut ctx = Context(sha2::Sha384::new());
136    pre_auth_encode(
137        [
138            &[key.as_bytes()],
139            &[
140                "v3".as_bytes(),
141                encoding.as_bytes(),
142                Public::HEADER.as_bytes(),
143            ],
144            &[cleartext],
145            &[footer],
146            &[aad],
147        ],
148        &mut ctx,
149    );
150    ctx.0
151}