jwt_next/algorithm/
rust_crypto.rs

1//! RustCrypto implementations of signing and verifying algorithms.
2//! According to that organization, only hmac is safely implemented at the
3//! moment.
4
5use base64::Engine;
6use digest::{
7    block_buffer::Eager,
8    consts::U256,
9    core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore},
10    generic_array::typenum::{IsLess, Le, NonZero},
11    HashMarker,
12};
13use hmac::{Hmac, Mac};
14
15use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm};
16use crate::error::Error;
17use crate::SEPARATOR;
18
19pub mod asymmetric;
20
21/// A trait used to make the implementation of `SigningAlgorithm` and
22/// `VerifyingAlgorithm` easier.
23/// RustCrypto crates tend to have algorithm types defined at the type level,
24/// so they cannot accept a self argument.
25pub trait TypeLevelAlgorithmType {
26    fn algorithm_type() -> AlgorithmType;
27}
28
29macro_rules! type_level_algorithm_type {
30    ($rust_crypto_type: ty, $algorithm_type: expr) => {
31        impl TypeLevelAlgorithmType for $rust_crypto_type {
32            fn algorithm_type() -> AlgorithmType {
33                $algorithm_type
34            }
35        }
36    };
37}
38
39type_level_algorithm_type!(sha2::Sha256, AlgorithmType::Hs256);
40type_level_algorithm_type!(sha2::Sha384, AlgorithmType::Hs384);
41type_level_algorithm_type!(sha2::Sha512, AlgorithmType::Hs512);
42
43impl<D> SigningAlgorithm for Hmac<D>
44where
45    D: CoreProxy + TypeLevelAlgorithmType,
46    D::Core: HashMarker
47        + BufferKindUser<BufferKind = Eager>
48        + FixedOutputCore
49        + digest::Reset
50        + Default
51        + Clone,
52    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
53    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
54{
55    fn algorithm_type(&self) -> AlgorithmType {
56        D::algorithm_type()
57    }
58
59    fn sign(&self, header: &str, claims: &str) -> Result<String, Error> {
60        let hmac = get_hmac_with_data(self, header, claims);
61        let mac_result = hmac.finalize();
62        let code = mac_result.into_bytes();
63        Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(code))
64    }
65}
66
67impl<D> VerifyingAlgorithm for Hmac<D>
68where
69    D: CoreProxy + TypeLevelAlgorithmType,
70    D::Core: HashMarker
71        + BufferKindUser<BufferKind = Eager>
72        + FixedOutputCore
73        + digest::Reset
74        + Default
75        + Clone,
76    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
77    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
78{
79    fn algorithm_type(&self) -> AlgorithmType {
80        D::algorithm_type()
81    }
82
83    fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result<bool, Error> {
84        let hmac = get_hmac_with_data(self, header, claims);
85        hmac.verify_slice(signature)?;
86        Ok(true)
87    }
88}
89
90fn get_hmac_with_data<D>(hmac: &Hmac<D>, header: &str, claims: &str) -> Hmac<D>
91where
92    D: CoreProxy,
93    D::Core: HashMarker
94        + BufferKindUser<BufferKind = Eager>
95        + FixedOutputCore
96        + digest::Reset
97        + Default
98        + Clone,
99    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
100    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
101{
102    let mut hmac = hmac.clone();
103    hmac.reset();
104    hmac.update(header.as_bytes());
105    hmac.update(SEPARATOR.as_bytes());
106    hmac.update(claims.as_bytes());
107    hmac
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::algorithm::{SigningAlgorithm, VerifyingAlgorithm};
113    use crate::error::Error;
114    use hmac::{Hmac, Mac};
115    use sha2::Sha256;
116
117    #[test]
118    pub fn sign() -> Result<(), Error> {
119        let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
120        let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
121        let expected_signature = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
122
123        let signer: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
124        let computed_signature = SigningAlgorithm::sign(&signer, header, claims)?;
125
126        assert_eq!(computed_signature, expected_signature);
127        Ok(())
128    }
129
130    #[test]
131    pub fn verify() -> Result<(), Error> {
132        let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
133        let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9";
134        let signature = "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
135
136        let verifier: Hmac<Sha256> = Hmac::new_from_slice(b"secret")?;
137        assert!(VerifyingAlgorithm::verify(
138            &verifier, header, claims, signature
139        )?);
140        Ok(())
141    }
142}