jwt_simple/algorithms/
hmac.rs

1use ct_codecs::{Base64UrlSafeNoPadding, Encoder};
2use hmac_sha512::sha384 as hmac_sha384;
3use rand::RngCore;
4use serde::{de::DeserializeOwned, Serialize};
5use zeroize::Zeroize;
6
7use crate::claims::*;
8use crate::common::*;
9#[cfg(feature = "cwt")]
10use crate::cwt_token::*;
11use crate::error::*;
12use crate::jwt_header::*;
13use crate::token::*;
14
15#[doc(hidden)]
16#[derive(Debug, Clone)]
17pub struct HMACKey {
18    raw_key: Vec<u8>,
19    metadata: Option<KeyMetadata>,
20}
21
22impl Drop for HMACKey {
23    fn drop(&mut self) {
24        self.raw_key.zeroize();
25    }
26}
27
28impl HMACKey {
29    /// Create a HMAC key from a byte slice.
30    pub fn from_bytes(raw_key: &[u8]) -> Self {
31        HMACKey {
32            raw_key: raw_key.to_vec(),
33            metadata: None,
34        }
35    }
36
37    /// Convert the HMAC key to a byte slice.
38    pub fn to_bytes(&self) -> Vec<u8> {
39        self.raw_key.clone()
40    }
41
42    /// Get the salt associated with the key.
43    pub fn salt(&self) -> Option<Salt> {
44        self.metadata.as_ref().map(|metadata| metadata.salt.clone())
45    }
46
47    /// Set the salt associated with the key.
48    pub fn with_salt(mut self, salt: Salt) -> Self {
49        if let Some(metadata) = self.metadata.as_mut() {
50            metadata.salt = salt;
51        } else {
52            self.metadata = Some(KeyMetadata {
53                salt,
54                ..Default::default()
55            });
56        }
57        self
58    }
59
60    /// Generate a random HMAC key.
61    pub fn generate() -> Self {
62        let mut raw_key = vec![0u8; 32];
63        rand::thread_rng().fill_bytes(&mut raw_key);
64        HMACKey {
65            raw_key,
66            metadata: None,
67        }
68    }
69
70    /// Generate a random HMAC key with a random salt.
71    pub fn generate_with_salt() -> Self {
72        HMACKey::generate().with_salt(Salt::generate())
73    }
74}
75
76impl AsRef<[u8]> for HMACKey {
77    /// Get the raw key, as a byte slice
78    fn as_ref(&self) -> &[u8] {
79        &self.raw_key
80    }
81}
82
83pub trait MACLike {
84    fn jwt_alg_name() -> &'static str;
85    fn key(&self) -> &HMACKey;
86    fn key_id(&self) -> &Option<String>;
87    fn set_key_id(&mut self, key_id: String);
88    fn metadata(&self) -> &Option<KeyMetadata>;
89    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error>;
90    fn authentication_tag(&self, authenticated: &[u8]) -> Vec<u8>;
91
92    /// Get the salt associated with the key.
93    fn salt(&self) -> Salt {
94        self.metadata()
95            .as_ref()
96            .map(|metadata| metadata.salt.clone())
97            .unwrap_or(Salt::None)
98    }
99
100    /// Compute the salt to be used for verification, given a signer salt.
101    fn verifier_salt(&self) -> Result<Salt, Error> {
102        match self.metadata().as_ref().map(|metadata| &metadata.salt) {
103            None => bail!(JWTError::MissingSalt),
104            Some(Salt::Signer(salt)) => {
105                let authenticated_salt = self.authentication_tag(salt);
106                Ok(Salt::Verifier(authenticated_salt))
107            }
108            Some(x @ Salt::Verifier(_)) => Ok(x.clone()),
109            Some(Salt::None) => bail!(JWTError::MissingSalt),
110        }
111    }
112
113    /// Attach a salt to the key.
114    fn attach_salt(&mut self, salt: Salt) -> Result<(), Error> {
115        let metadata = KeyMetadata {
116            salt,
117            ..Default::default()
118        };
119        self.attach_metadata(metadata)?;
120        Ok(())
121    }
122
123    /// Authenticate a token.
124    fn authenticate<CustomClaims: Serialize>(
125        &self,
126        claims: JWTClaims<CustomClaims>,
127    ) -> Result<String, Error> {
128        self.authenticate_with_options(claims, &Default::default())
129    }
130
131    fn authenticate_with_options<CustomClaims: Serialize>(
132        &self,
133        claims: JWTClaims<CustomClaims>,
134        options: &HeaderOptions,
135    ) -> Result<String, Error> {
136        ensure!(self.key().as_ref().len() >= 12, JWTError::WeakKey);
137        let jwt_header = JWTHeader::new(Self::jwt_alg_name().to_string(), self.key_id().clone())
138            .with_key_metadata(self.metadata())
139            .with_options(options);
140        Token::build(&jwt_header, claims, |authenticated| {
141            Ok(self.authentication_tag(authenticated.as_bytes()))
142        })
143    }
144
145    /// Verify a token.
146    fn verify_token<CustomClaims: DeserializeOwned>(
147        &self,
148        token: &str,
149        options: Option<VerificationOptions>,
150    ) -> Result<JWTClaims<CustomClaims>, Error> {
151        ensure!(self.key().as_ref().len() >= 12, JWTError::WeakKey);
152        Token::verify(
153            Self::jwt_alg_name(),
154            token,
155            options,
156            |authenticated, authentication_tag| {
157                ensure!(
158                    ct_codecs::verify(
159                        &self.authentication_tag(authenticated.as_bytes()),
160                        authentication_tag
161                    ),
162                    JWTError::InvalidAuthenticationTag
163                );
164                Ok(())
165            },
166            |salt: Option<&[u8]>| {
167                if let Some(Salt::Verifier(authenticated_salt)) =
168                    self.metadata().as_ref().map(|metadata| &metadata.salt)
169                {
170                    match salt {
171                        None => bail!(JWTError::MissingSalt),
172                        Some(salt) => {
173                            let expected_authenticated_tag = self.authentication_tag(salt);
174                            ensure!(
175                                ct_codecs::verify(authenticated_salt, &expected_authenticated_tag),
176                                JWTError::InvalidAuthenticationTag
177                            );
178                        }
179                    }
180                } else {
181                    ensure!(salt.is_none(), JWTError::MissingSalt);
182                }
183                Ok(())
184            },
185        )
186    }
187
188    #[cfg(feature = "cwt")]
189    fn verify_cwt_token(
190        &self,
191        token: impl AsRef<[u8]>,
192        options: Option<VerificationOptions>,
193    ) -> Result<JWTClaims<NoCustomClaims>, Error> {
194        ensure!(self.key().as_ref().len() >= 12, JWTError::WeakKey);
195        CWTToken::verify(
196            Self::jwt_alg_name(),
197            token,
198            options,
199            |authenticated, authentication_tag| {
200                ensure!(
201                    ct_codecs::verify(
202                        &self.authentication_tag(authenticated.as_bytes()),
203                        authentication_tag
204                    ),
205                    JWTError::InvalidAuthenticationTag
206                );
207                Ok(())
208            },
209        )
210    }
211
212    /// Verify a CWT token with custom claims
213    ///
214    /// This function allows verification of CWT tokens that contain application-specific
215    /// claims beyond the standard set defined in the CWT specification.
216    ///
217    /// # Type Parameters
218    ///
219    /// * `CustomClaims` - A struct that implements `DeserializeOwned + Default` which
220    ///   represents your application-specific claims.
221    ///
222    /// # Arguments
223    ///
224    /// * `token` - The CWT token to verify
225    /// * `options` - Optional verification options
226    ///
227    /// # Returns
228    ///
229    /// A `Result` containing the verified claims (including custom claims) or an error
230    ///
231    /// # Examples
232    ///
233    /// ```no_run
234    /// # fn main() -> Result<(), jwt_simple::Error> {
235    /// use jwt_simple::prelude::*;
236    /// use serde::{Deserialize, Serialize};
237    ///
238    /// #[derive(Debug, Serialize, Deserialize, Default)]
239    /// struct MyCustomClaims {
240    ///     user_role: String,
241    ///     permissions: Vec<String>,
242    /// }
243    ///
244    /// // Given a key and token bytes
245    /// let key_bytes = b"some secret key bytes";
246    /// let token_bytes = b"a CWT token in bytes"; // This would be your actual token
247    ///
248    /// let key = HS256Key::from_bytes(key_bytes);
249    /// let claims = key.verify_cwt_token_with_custom_claims::<MyCustomClaims>(
250    ///     token_bytes,
251    ///     None
252    /// )?;
253    ///
254    /// // Access verified custom claims
255    /// println!("User role: {}", claims.custom.user_role);
256    /// # Ok(())
257    /// # }
258    /// ```
259    #[cfg(feature = "cwt")]
260    fn verify_cwt_token_with_custom_claims<CustomClaims>(
261        &self,
262        token: impl AsRef<[u8]>,
263        options: Option<VerificationOptions>,
264    ) -> Result<JWTClaims<CustomClaims>, Error>
265    where
266        CustomClaims: DeserializeOwned + Default,
267    {
268        ensure!(self.key().as_ref().len() >= 12, JWTError::WeakKey);
269        CWTToken::verify(
270            Self::jwt_alg_name(),
271            token,
272            options,
273            |authenticated, authentication_tag| {
274                ensure!(
275                    ct_codecs::verify(
276                        &self.authentication_tag(authenticated.as_bytes()),
277                        authentication_tag
278                    ),
279                    JWTError::InvalidAuthenticationTag
280                );
281                Ok(())
282            },
283        )
284    }
285
286    /// Decode CWT token metadata that can be useful prior to signature/tag verification
287    #[cfg(feature = "cwt")]
288    fn decode_cwt_metadata(&self, token: impl AsRef<[u8]>) -> Result<TokenMetadata, Error> {
289        CWTToken::decode_metadata(token)
290    }
291
292    fn create_key_id(&mut self) -> &str {
293        self.set_key_id(
294            Base64UrlSafeNoPadding::encode_to_string(hmac_sha256::Hash::hash(
295                &self.key().to_bytes(),
296            ))
297            .unwrap(),
298        );
299        self.key_id().as_ref().map(|x| x.as_str()).unwrap()
300    }
301}
302
303#[derive(Debug, Clone)]
304pub struct HS256Key {
305    key: HMACKey,
306    key_id: Option<String>,
307}
308
309impl MACLike for HS256Key {
310    fn jwt_alg_name() -> &'static str {
311        "HS256"
312    }
313
314    fn key(&self) -> &HMACKey {
315        &self.key
316    }
317
318    fn key_id(&self) -> &Option<String> {
319        &self.key_id
320    }
321
322    fn set_key_id(&mut self, key_id: String) {
323        self.key_id = Some(key_id);
324    }
325
326    fn metadata(&self) -> &Option<KeyMetadata> {
327        &self.key.metadata
328    }
329
330    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error> {
331        self.key.metadata = Some(metadata);
332        Ok(())
333    }
334
335    fn authentication_tag(&self, authenticated: &[u8]) -> Vec<u8> {
336        hmac_sha256::HMAC::mac(authenticated, self.key().as_ref()).to_vec()
337    }
338}
339
340impl HS256Key {
341    pub fn from_bytes(raw_key: &[u8]) -> Self {
342        HS256Key {
343            key: HMACKey::from_bytes(raw_key),
344            key_id: None,
345        }
346    }
347
348    pub fn to_bytes(&self) -> Vec<u8> {
349        self.key.to_bytes()
350    }
351
352    pub fn generate() -> Self {
353        HS256Key {
354            key: HMACKey::generate(),
355            key_id: None,
356        }
357    }
358
359    pub fn generate_with_salt() -> Self {
360        HS256Key {
361            key: HMACKey::generate_with_salt(),
362            key_id: None,
363        }
364    }
365
366    pub fn with_key_id(mut self, key_id: &str) -> Self {
367        self.key_id = Some(key_id.to_string());
368        self
369    }
370
371    /// Verify a CWT token with custom claims
372    ///
373    /// This function allows verification of CWT tokens that contain application-specific
374    /// claims beyond the standard set defined in the CWT specification.
375    ///
376    /// # Type Parameters
377    ///
378    /// * `CustomClaims` - A struct that implements `DeserializeOwned + Default` which
379    ///   represents your application-specific claims.
380    ///
381    /// # Arguments
382    ///
383    /// * `token` - The CWT token to verify
384    /// * `options` - Optional verification options
385    ///
386    /// # Returns
387    ///
388    /// A `Result` containing the verified claims (including custom claims) or an error
389    ///
390    /// # Examples
391    ///
392    /// ```ignore
393    /// # fn main() -> Result<(), jwt_simple::Error> {
394    /// use jwt_simple::prelude::*;
395    /// use serde::{Deserialize, Serialize};
396    ///
397    /// #[derive(Debug, Serialize, Deserialize, Default)]
398    /// struct MyCustomClaims {
399    ///     user_role: String,
400    ///     permissions: Vec<String>,
401    /// }
402    ///
403    /// // Given a key and token bytes
404    /// let key_bytes = b"some secret key bytes";
405    /// let token_bytes = b"a CWT token in bytes"; // This would be your actual token
406    ///
407    /// let key = HS256Key::from_bytes(key_bytes);
408    /// let claims = key.verify_cwt_token_with_custom_claims::<MyCustomClaims>(
409    ///     token_bytes,
410    ///     None
411    /// )?;
412    ///
413    /// // Access verified custom claims
414    /// println!("User role: {}", claims.custom.user_role);
415    /// # Ok(())
416    /// # }
417    /// ```
418    #[cfg(feature = "cwt")]
419    pub fn verify_cwt_token_with_custom_claims<CustomClaims>(
420        &self,
421        token: impl AsRef<[u8]>,
422        options: Option<VerificationOptions>,
423    ) -> Result<JWTClaims<CustomClaims>, Error>
424    where
425        CustomClaims: DeserializeOwned + Default,
426    {
427        <Self as MACLike>::verify_cwt_token_with_custom_claims::<CustomClaims>(self, token, options)
428    }
429}
430
431#[derive(Debug, Clone)]
432pub struct HS512Key {
433    key: HMACKey,
434    key_id: Option<String>,
435}
436
437impl MACLike for HS512Key {
438    fn jwt_alg_name() -> &'static str {
439        "HS512"
440    }
441
442    fn key(&self) -> &HMACKey {
443        &self.key
444    }
445
446    fn key_id(&self) -> &Option<String> {
447        &self.key_id
448    }
449
450    fn set_key_id(&mut self, key_id: String) {
451        self.key_id = Some(key_id);
452    }
453
454    fn metadata(&self) -> &Option<KeyMetadata> {
455        &self.key.metadata
456    }
457
458    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error> {
459        self.key.metadata = Some(metadata);
460        Ok(())
461    }
462
463    fn authentication_tag(&self, authenticated: &[u8]) -> Vec<u8> {
464        hmac_sha512::HMAC::mac(authenticated, self.key().as_ref()).to_vec()
465    }
466}
467
468impl HS512Key {
469    pub fn from_bytes(raw_key: &[u8]) -> Self {
470        HS512Key {
471            key: HMACKey::from_bytes(raw_key),
472            key_id: None,
473        }
474    }
475
476    pub fn to_bytes(&self) -> Vec<u8> {
477        self.key.to_bytes()
478    }
479
480    pub fn generate() -> Self {
481        HS512Key {
482            key: HMACKey::generate(),
483            key_id: None,
484        }
485    }
486
487    pub fn generate_with_salt() -> Self {
488        HS512Key {
489            key: HMACKey::generate_with_salt(),
490            key_id: None,
491        }
492    }
493
494    pub fn with_key_id(mut self, key_id: &str) -> Self {
495        self.key_id = Some(key_id.to_string());
496        self
497    }
498
499    /// Verify a CWT token with custom claims
500    ///
501    /// This function allows verification of CWT tokens that contain application-specific
502    /// claims beyond the standard set defined in the CWT specification.
503    ///
504    /// # Type Parameters
505    ///
506    /// * `CustomClaims` - A struct that implements `DeserializeOwned + Default` which
507    ///   represents your application-specific claims.
508    ///
509    /// # Arguments
510    ///
511    /// * `token` - The CWT token to verify
512    /// * `options` - Optional verification options
513    ///
514    /// # Returns
515    ///
516    /// A `Result` containing the verified claims (including custom claims) or an error
517    #[cfg(feature = "cwt")]
518    pub fn verify_cwt_token_with_custom_claims<CustomClaims>(
519        &self,
520        token: impl AsRef<[u8]>,
521        options: Option<VerificationOptions>,
522    ) -> Result<JWTClaims<CustomClaims>, Error>
523    where
524        CustomClaims: DeserializeOwned + Default,
525    {
526        <Self as MACLike>::verify_cwt_token_with_custom_claims::<CustomClaims>(self, token, options)
527    }
528}
529
530#[derive(Debug, Clone)]
531pub struct HS384Key {
532    key: HMACKey,
533    key_id: Option<String>,
534}
535
536impl MACLike for HS384Key {
537    fn jwt_alg_name() -> &'static str {
538        "HS384"
539    }
540
541    fn key(&self) -> &HMACKey {
542        &self.key
543    }
544
545    fn key_id(&self) -> &Option<String> {
546        &self.key_id
547    }
548
549    fn set_key_id(&mut self, key_id: String) {
550        self.key_id = Some(key_id);
551    }
552
553    fn metadata(&self) -> &Option<KeyMetadata> {
554        &self.key.metadata
555    }
556
557    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error> {
558        self.key.metadata = Some(metadata);
559        Ok(())
560    }
561
562    fn authentication_tag(&self, authenticated: &[u8]) -> Vec<u8> {
563        hmac_sha384::HMAC::mac(authenticated, self.key().as_ref()).to_vec()
564    }
565}
566
567impl HS384Key {
568    pub fn from_bytes(raw_key: &[u8]) -> Self {
569        HS384Key {
570            key: HMACKey::from_bytes(raw_key),
571            key_id: None,
572        }
573    }
574
575    pub fn to_bytes(&self) -> Vec<u8> {
576        self.key.to_bytes()
577    }
578
579    pub fn generate() -> Self {
580        HS384Key {
581            key: HMACKey::generate(),
582            key_id: None,
583        }
584    }
585
586    pub fn generate_with_salt() -> Self {
587        HS384Key {
588            key: HMACKey::generate_with_salt(),
589            key_id: None,
590        }
591    }
592
593    pub fn with_key_id(mut self, key_id: &str) -> Self {
594        self.key_id = Some(key_id.to_string());
595        self
596    }
597
598    /// Verify a CWT token with custom claims
599    ///
600    /// This function allows verification of CWT tokens that contain application-specific
601    /// claims beyond the standard set defined in the CWT specification.
602    ///
603    /// # Type Parameters
604    ///
605    /// * `CustomClaims` - A struct that implements `DeserializeOwned + Default` which
606    ///   represents your application-specific claims.
607    ///
608    /// # Arguments
609    ///
610    /// * `token` - The CWT token to verify
611    /// * `options` - Optional verification options
612    ///
613    /// # Returns
614    ///
615    /// A `Result` containing the verified claims (including custom claims) or an error
616    #[cfg(feature = "cwt")]
617    pub fn verify_cwt_token_with_custom_claims<CustomClaims>(
618        &self,
619        token: impl AsRef<[u8]>,
620        options: Option<VerificationOptions>,
621    ) -> Result<JWTClaims<CustomClaims>, Error>
622    where
623        CustomClaims: DeserializeOwned + Default,
624    {
625        <Self as MACLike>::verify_cwt_token_with_custom_claims::<CustomClaims>(self, token, options)
626    }
627}
628
629//
630
631#[derive(Debug, Clone)]
632pub struct Blake2bKey {
633    key: HMACKey,
634    key_id: Option<String>,
635}
636
637impl MACLike for Blake2bKey {
638    fn jwt_alg_name() -> &'static str {
639        "BLAKE2B"
640    }
641
642    fn key(&self) -> &HMACKey {
643        &self.key
644    }
645
646    fn key_id(&self) -> &Option<String> {
647        &self.key_id
648    }
649
650    fn set_key_id(&mut self, key_id: String) {
651        self.key_id = Some(key_id);
652    }
653
654    fn metadata(&self) -> &Option<KeyMetadata> {
655        &self.key.metadata
656    }
657
658    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error> {
659        self.key.metadata = Some(metadata);
660        Ok(())
661    }
662
663    fn authentication_tag(&self, authenticated: &[u8]) -> Vec<u8> {
664        blake2b_simd::Params::new()
665            .hash_length(32)
666            .key(self.key().as_ref())
667            .to_state()
668            .update(authenticated)
669            .finalize()
670            .as_bytes()
671            .to_vec()
672    }
673}
674
675impl Blake2bKey {
676    pub fn from_bytes(raw_key: &[u8]) -> Self {
677        Blake2bKey {
678            key: HMACKey::from_bytes(raw_key),
679            key_id: None,
680        }
681    }
682
683    pub fn to_bytes(&self) -> Vec<u8> {
684        self.key.to_bytes()
685    }
686
687    pub fn generate() -> Self {
688        Blake2bKey {
689            key: HMACKey::generate(),
690            key_id: None,
691        }
692    }
693
694    /// Verify a CWT token with custom claims
695    ///
696    /// This function allows verification of CWT tokens that contain application-specific
697    /// claims beyond the standard set defined in the CWT specification.
698    ///
699    /// # Type Parameters
700    ///
701    /// * `CustomClaims` - A struct that implements `DeserializeOwned + Default` which
702    ///   represents your application-specific claims.
703    ///
704    /// # Arguments
705    ///
706    /// * `token` - The CWT token to verify
707    /// * `options` - Optional verification options
708    ///
709    /// # Returns
710    ///
711    /// A `Result` containing the verified claims (including custom claims) or an error
712    #[cfg(feature = "cwt")]
713    pub fn verify_cwt_token_with_custom_claims<CustomClaims>(
714        &self,
715        token: impl AsRef<[u8]>,
716        options: Option<VerificationOptions>,
717    ) -> Result<JWTClaims<CustomClaims>, Error>
718    where
719        CustomClaims: DeserializeOwned + Default,
720    {
721        <Self as MACLike>::verify_cwt_token_with_custom_claims::<CustomClaims>(self, token, options)
722    }
723
724    pub fn generate_with_salt() -> Self {
725        Blake2bKey {
726            key: HMACKey::generate_with_salt(),
727            key_id: None,
728        }
729    }
730
731    pub fn with_key_id(mut self, key_id: &str) -> Self {
732        self.key_id = Some(key_id.to_string());
733        self
734    }
735}