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