Skip to main content

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