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