josekit/jwe/
jwe_header.rs

1use std::any::Any;
2use std::cmp::Eq;
3use std::convert::Into;
4use std::fmt::{Debug, Display};
5use std::ops::Deref;
6
7use anyhow::bail;
8
9use crate::jwk::Jwk;
10use crate::util;
11use crate::{JoseError, JoseHeader, Map, Value};
12
13/// Represent JWE header claims
14#[derive(Debug, Eq, PartialEq, Clone)]
15pub struct JweHeader {
16    claims: Map<String, Value>,
17}
18
19impl JweHeader {
20    /// Return a new JweHeader instance.
21    pub fn new() -> Self {
22        Self { claims: Map::new() }
23    }
24
25    /// Return a new header instance from json style header.
26    ///
27    /// # Arguments
28    ///
29    /// * `value` - The json style header claims
30    pub fn from_bytes(value: &[u8]) -> Result<Self, JoseError> {
31        let claims = (|| -> anyhow::Result<Map<String, Value>> {
32            let claims: Map<String, Value> = serde_json::from_slice(value)?;
33            Ok(claims)
34        })()
35        .map_err(|err| JoseError::InvalidJson(err))?;
36
37        let header = Self::from_map(claims)?;
38        Ok(header)
39    }
40
41    /// Return a new header instance from map.
42    ///
43    /// # Arguments
44    ///
45    /// * `map` - The header claims
46    pub fn from_map(map: impl Into<Map<String, Value>>) -> Result<Self, JoseError> {
47        let map: Map<String, Value> = map.into();
48        for (key, value) in &map {
49            Self::check_claim(key, value)?;
50        }
51
52        Ok(Self { claims: map })
53    }
54
55    /// Set a value for algorithm header claim (alg).
56    ///
57    /// # Arguments
58    ///
59    /// * `value` - a algorithm
60    pub fn set_algorithm(&mut self, value: impl Into<String>) {
61        let value: String = value.into();
62        self.claims.insert("alg".to_string(), Value::String(value));
63    }
64
65    /// Return the value for algorithm header claim (alg).
66    pub fn algorithm(&self) -> Option<&str> {
67        match self.claim("alg") {
68            Some(Value::String(val)) => Some(&val),
69            _ => None,
70        }
71    }
72
73    /// Set a value for content encryption header claim (enc).
74    ///
75    /// # Arguments
76    ///
77    /// * `value` - a content encryption
78    pub fn set_content_encryption(&mut self, value: impl Into<String>) {
79        let value: String = value.into();
80        self.claims.insert("enc".to_string(), Value::String(value));
81    }
82
83    /// Return the value for content encryption header claim (enc).
84    pub fn content_encryption(&self) -> Option<&str> {
85        match self.claims.get("enc") {
86            Some(Value::String(val)) => Some(val),
87            _ => None,
88        }
89    }
90
91    /// Set a value for compression header claim (zip).
92    ///
93    /// # Arguments
94    ///
95    /// * `value` - a encryption
96    pub fn set_compression(&mut self, value: impl Into<String>) {
97        let value: String = value.into();
98        self.claims.insert("zip".to_string(), Value::String(value));
99    }
100
101    /// Return the value for compression header claim (zip).
102    pub fn compression(&self) -> Option<&str> {
103        match self.claims.get("zip") {
104            Some(Value::String(val)) => Some(val),
105            _ => None,
106        }
107    }
108
109    /// Set a value for JWK set URL header claim (jku).
110    ///
111    /// # Arguments
112    ///
113    /// * `value` - a JWK set URL
114    pub fn set_jwk_set_url(&mut self, value: impl Into<String>) {
115        let value: String = value.into();
116        self.claims.insert("jku".to_string(), Value::String(value));
117    }
118
119    /// Return the value for JWK set URL header claim (jku).
120    pub fn jwk_set_url(&self) -> Option<&str> {
121        match self.claims.get("jku") {
122            Some(Value::String(val)) => Some(val),
123            _ => None,
124        }
125    }
126
127    /// Set a value for JWK header claim (jwk).
128    ///
129    /// # Arguments
130    ///
131    /// * `value` - a JWK
132    pub fn set_jwk(&mut self, value: Jwk) {
133        let key = "jwk";
134        let value: Map<String, Value> = value.into();
135        self.claims.insert(key.to_string(), Value::Object(value));
136    }
137
138    /// Return the value for JWK header claim (jwk).
139    pub fn jwk(&self) -> Option<Jwk> {
140        match self.claims.get("jwk") {
141            Some(Value::Object(vals)) => match Jwk::from_map(vals.clone()) {
142                Ok(val) => Some(val),
143                Err(_) => None,
144            },
145            _ => None,
146        }
147    }
148
149    /// Set a value for X.509 URL header claim (x5u).
150    ///
151    /// # Arguments
152    ///
153    /// * `value` - a X.509 URL
154    pub fn set_x509_url(&mut self, value: impl Into<String>) {
155        let value: String = value.into();
156        self.claims.insert("x5u".to_string(), Value::String(value));
157    }
158
159    /// Return a value for a X.509 URL header claim (x5u).
160    pub fn x509_url(&self) -> Option<&str> {
161        match self.claims.get("x5u") {
162            Some(Value::String(val)) => Some(val),
163            _ => None,
164        }
165    }
166
167    /// Set values for X.509 certificate chain header claim (x5c).
168    ///
169    /// # Arguments
170    ///
171    /// * `values` - X.509 certificate chain
172    pub fn set_x509_certificate_chain(&mut self, values: &Vec<impl AsRef<[u8]>>) {
173        let key = "x5c";
174        let mut vec = Vec::with_capacity(values.len());
175        for val in values {
176            vec.push(Value::String(util::encode_base64_standard(val)));
177        }
178        self.claims.insert(key.to_string(), Value::Array(vec));
179    }
180
181    /// Return values for a X.509 certificate chain header claim (x5c).
182    pub fn x509_certificate_chain(&self) -> Option<Vec<Vec<u8>>> {
183        match self.claims.get("x5c") {
184            Some(Value::Array(vals)) => {
185                let mut vec = Vec::with_capacity(vals.len());
186                for val in vals {
187                    match val {
188                        Value::String(val2) => match util::decode_base64_standard(val2) {
189                            Ok(val3) => vec.push(val3.clone()),
190                            Err(_) => return None,
191                        },
192                        _ => return None,
193                    }
194                }
195                Some(vec)
196            }
197            _ => None,
198        }
199    }
200
201    /// Set a value for X.509 certificate SHA-1 thumbprint header claim (x5t).
202    ///
203    /// # Arguments
204    ///
205    /// * `value` - A X.509 certificate SHA-1 thumbprint
206    pub fn set_x509_certificate_sha1_thumbprint(&mut self, value: impl AsRef<[u8]>) {
207        let key = "x5t";
208        let val = util::encode_base64_urlsafe_nopad(value);
209        self.claims.insert(key.to_string(), Value::String(val));
210    }
211
212    /// Return the value for X.509 certificate SHA-1 thumbprint header claim (x5t).
213    pub fn x509_certificate_sha1_thumbprint(&self) -> Option<Vec<u8>> {
214        match self.claims.get("x5t") {
215            Some(Value::String(val)) => match util::decode_base64_urlsafe_no_pad(val) {
216                Ok(val2) => Some(val2),
217                Err(_) => None,
218            },
219            _ => None,
220        }
221    }
222
223    /// Set a value for a x509 certificate SHA-256 thumbprint header claim (x5t#S256).
224    ///
225    /// # Arguments
226    ///
227    /// * `value` - A x509 certificate SHA-256 thumbprint
228    pub fn set_x509_certificate_sha256_thumbprint(&mut self, value: impl AsRef<[u8]>) {
229        let key = "x5t#S256";
230        let val = util::encode_base64_urlsafe_nopad(value);
231        self.claims.insert(key.to_string(), Value::String(val));
232    }
233
234    /// Return the value for X.509 certificate SHA-256 thumbprint header claim (x5t#S256).
235    pub fn x509_certificate_sha256_thumbprint(&self) -> Option<Vec<u8>> {
236        match self.claims.get("x5t#S256") {
237            Some(Value::String(val)) => match util::decode_base64_urlsafe_no_pad(val) {
238                Ok(val2) => Some(val2),
239                Err(_) => None,
240            },
241            _ => None,
242        }
243    }
244
245    /// Set a value for key ID header claim (kid).
246    ///
247    /// # Arguments
248    ///
249    /// * `value` - a key ID
250    pub fn set_key_id(&mut self, value: impl Into<String>) {
251        let value: String = value.into();
252        self.claims.insert("kid".to_string(), Value::String(value));
253    }
254
255    /// Return the value for key ID header claim (kid).
256    pub fn key_id(&self) -> Option<&str> {
257        match self.claims.get("kid") {
258            Some(Value::String(val)) => Some(val),
259            _ => None,
260        }
261    }
262
263    /// Set a value for token type header claim (typ).
264    ///
265    /// # Arguments
266    ///
267    /// * `value` - a token type (e.g. "JWT")
268    pub fn set_token_type(&mut self, value: impl Into<String>) {
269        let value: String = value.into();
270        self.claims.insert("typ".to_string(), Value::String(value));
271    }
272
273    /// Return the value for token type header claim (typ).
274    pub fn token_type(&self) -> Option<&str> {
275        match self.claims.get("typ") {
276            Some(Value::String(val)) => Some(val),
277            _ => None,
278        }
279    }
280
281    /// Set a value for content type header claim (cty).
282    ///
283    /// # Arguments
284    ///
285    /// * `value` - a content type (e.g. "JWT")
286    pub fn set_content_type(&mut self, value: impl Into<String>) {
287        let value: String = value.into();
288        self.claims.insert("cty".to_string(), Value::String(value));
289    }
290
291    /// Return the value for content type header claim (cty).
292    pub fn content_type(&self) -> Option<&str> {
293        match self.claims.get("cty") {
294            Some(Value::String(val)) => Some(val),
295            _ => None,
296        }
297    }
298
299    /// Set values for critical header claim (crit).
300    ///
301    /// # Arguments
302    ///
303    /// * `values` - critical claim names
304    pub fn set_critical(&mut self, values: &Vec<impl AsRef<str>>) {
305        let key = "crit";
306        let vec = values
307            .iter()
308            .map(|v| Value::String(v.as_ref().to_string()))
309            .collect();
310        self.claims.insert(key.to_string(), Value::Array(vec));
311    }
312
313    /// Return values for critical header claim (crit).
314    pub fn critical(&self) -> Option<Vec<&str>> {
315        match self.claims.get("crit") {
316            Some(Value::Array(vals)) => {
317                let mut vec = Vec::with_capacity(vals.len());
318                for val in vals {
319                    match val {
320                        Value::String(val2) => vec.push(val2.as_str()),
321                        _ => return None,
322                    }
323                }
324                Some(vec)
325            }
326            _ => None,
327        }
328    }
329
330    /// Set a value for url header claim (url).
331    ///
332    /// # Arguments
333    ///
334    /// * `value` - a url
335    pub fn set_url(&mut self, value: impl Into<String>) {
336        let value: String = value.into();
337        self.claims.insert("url".to_string(), Value::String(value));
338    }
339
340    /// Return the value for url header claim (url).
341    pub fn url(&self) -> Option<&str> {
342        match self.claims.get("url") {
343            Some(Value::String(val)) => Some(val),
344            _ => None,
345        }
346    }
347
348    /// Set a value for a nonce header claim (nonce).
349    ///
350    /// # Arguments
351    ///
352    /// * `value` - A nonce
353    pub fn set_nonce(&mut self, value: impl AsRef<[u8]>) {
354        let key = "nonce";
355        let val = util::encode_base64_urlsafe_nopad(value);
356        self.claims.insert(key.to_string(), Value::String(val));
357    }
358
359    /// Return the value for nonce header claim (nonce).
360    pub fn nonce(&self) -> Option<Vec<u8>> {
361        match self.claims.get("nonce") {
362            Some(Value::String(val)) => match util::decode_base64_urlsafe_no_pad(val) {
363                Ok(val2) => Some(val2),
364                Err(_) => None,
365            },
366            _ => None,
367        }
368    }
369
370    /// Set a value for a agreement PartyUInfo header claim (apu).
371    ///
372    /// # Arguments
373    ///
374    /// * `value` - A agreement PartyUInfo
375    pub fn set_agreement_partyuinfo(&mut self, value: impl AsRef<[u8]>) {
376        let key = "apu";
377        let val = util::encode_base64_urlsafe_nopad(value);
378        self.claims.insert(key.to_string(), Value::String(val));
379    }
380
381    /// Return the value for agreement PartyUInfo header claim (apu).
382    pub fn agreement_partyuinfo(&self) -> Option<Vec<u8>> {
383        match self.claims.get("apu") {
384            Some(Value::String(val)) => match util::decode_base64_urlsafe_no_pad(val) {
385                Ok(val2) => Some(val2),
386                Err(_) => None,
387            },
388            _ => None,
389        }
390    }
391
392    /// Set a value for a agreement PartyVInfo header claim (apv).
393    ///
394    /// # Arguments
395    ///
396    /// * `value` - A agreement PartyVInfo
397    pub fn set_agreement_partyvinfo(&mut self, value: impl AsRef<[u8]>) {
398        let key = "apv";
399        let val = util::encode_base64_urlsafe_nopad(value);
400        self.claims.insert(key.to_string(), Value::String(val));
401    }
402
403    /// Return the value for agreement PartyVInfo header claim (apv).
404    pub fn agreement_partyvinfo(&self) -> Option<Vec<u8>> {
405        match self.claims.get("apv") {
406            Some(Value::String(val)) => match util::decode_base64_urlsafe_no_pad(val) {
407                Ok(val2) => Some(val2),
408                Err(_) => None,
409            },
410            _ => None,
411        }
412    }
413
414    /// Set a value for issuer header claim (iss).
415    ///
416    /// # Arguments
417    ///
418    /// * `value` - a issuer
419    pub fn set_issuer(&mut self, value: impl Into<String>) {
420        let key = "iss";
421        let value: String = value.into();
422        self.claims.insert(key.to_string(), Value::String(value));
423    }
424
425    /// Return the value for issuer header claim (iss).
426    pub fn issuer(&self) -> Option<&str> {
427        match self.claims.get("iss") {
428            Some(Value::String(val)) => Some(val),
429            _ => None,
430        }
431    }
432
433    /// Set a value for subject header claim (sub).
434    ///
435    /// # Arguments
436    ///
437    /// * `value` - a subject
438    pub fn set_subject(&mut self, value: impl Into<String>) {
439        let key = "sub";
440        let value: String = value.into();
441        self.claims.insert(key.to_string(), Value::String(value));
442    }
443
444    /// Return the value for subject header claim (sub).
445    pub fn subject(&self) -> Option<&str> {
446        match self.claims.get("sub") {
447            Some(Value::String(val)) => Some(val),
448            _ => None,
449        }
450    }
451
452    /// Set values for audience header claim (aud).
453    ///
454    /// # Arguments
455    ///
456    /// * `values` - a list of audiences
457    pub fn set_audience(&mut self, values: Vec<impl Into<String>>) {
458        let key = "aud".to_string();
459        if values.len() == 1 {
460            for val in values {
461                let val: String = val.into();
462                self.claims.insert(key, Value::String(val));
463                break;
464            }
465        } else if values.len() > 1 {
466            let mut vec = Vec::with_capacity(values.len());
467            for val in values {
468                let val: String = val.into();
469                vec.push(Value::String(val.clone()));
470            }
471            self.claims.insert(key.clone(), Value::Array(vec));
472        }
473    }
474
475    /// Return values for audience header claim (aud).
476    pub fn audience(&self) -> Option<Vec<&str>> {
477        match self.claims.get("aud") {
478            Some(Value::Array(vals)) => {
479                let mut vec = Vec::with_capacity(vals.len());
480                for val in vals {
481                    match val {
482                        Value::String(val2) => {
483                            vec.push(val2.as_str());
484                        }
485                        _ => return None,
486                    }
487                }
488                Some(vec)
489            }
490            Some(Value::String(val)) => Some(vec![val]),
491            _ => None,
492        }
493    }
494
495    /// Set a value for header claim of a specified key.
496    ///
497    /// # Arguments
498    ///
499    /// * `key` - a key name of header claim
500    /// * `value` - a typed value of header claim
501    pub fn set_claim(&mut self, key: &str, value: Option<Value>) -> Result<(), JoseError> {
502        match value {
503            Some(val) => {
504                Self::check_claim(key, &val)?;
505                self.claims.insert(key.to_string(), val);
506            }
507            None => {
508                self.claims.remove(key);
509            }
510        }
511
512        Ok(())
513    }
514
515    /// Return values for header claims set
516    pub fn claims_set(&self) -> &Map<String, Value> {
517        &self.claims
518    }
519
520    /// Convert into map
521    pub fn into_map(self) -> Map<String, Value> {
522        self.claims
523    }
524
525    pub(crate) fn check_claim(key: &str, value: &Value) -> Result<(), JoseError> {
526        (|| -> anyhow::Result<()> {
527            match key {
528                "alg" | "enc" | "zip" | "jku" | "x5u" | "kid" | "typ" | "cty" | "url" | "iss"
529                | "sub" => match &value {
530                    Value::String(_) => {}
531                    _ => bail!("The JWE {} header claim must be string.", key),
532                },
533                "aud" => match &value {
534                    Value::String(_) => {}
535                    Value::Array(vals) => {
536                        for val in vals {
537                            match val {
538                                Value::String(_) => {}
539                                _ => bail!(
540                                    "An element of the JWE {} header claim must be a string.",
541                                    key
542                                ),
543                            }
544                        }
545                    }
546                    _ => bail!("The JWE {} payload claim must be a string or array.", key),
547                },
548                "crit" => match &value {
549                    Value::Array(vals) => {
550                        for val in vals {
551                            match val {
552                                Value::String(_) => {}
553                                _ => bail!(
554                                    "An element of the JWE {} header claim must be a string.",
555                                    key
556                                ),
557                            }
558                        }
559                    }
560                    _ => bail!("The JWE {} header claim must be a array.", key),
561                },
562                "x5t" | "x5t#S256" | "nonce" | "apu" | "apv" => match &value {
563                    Value::String(val) => {
564                        if !util::is_base64_urlsafe_nopad(val) {
565                            bail!("The JWE {} header claim must be a base64 string.", key);
566                        }
567                    }
568                    _ => bail!("The JWE {} header claim must be a string.", key),
569                },
570                "x5c" => match &value {
571                    Value::Array(vals) => {
572                        for val in vals {
573                            match val {
574                                Value::String(val) => {
575                                    if !util::is_base64_standard(val) {
576                                        bail!(
577                                            "The JWE {} header claim must be a base64 string.",
578                                            key
579                                        );
580                                    }
581                                }
582                                _ => bail!(
583                                    "An element of the JWE {} header claim must be a string.",
584                                    key
585                                ),
586                            }
587                        }
588                    }
589                    _ => bail!("The JWE {} header claim must be a array.", key),
590                },
591                "jwk" => match &value {
592                    Value::Object(vals) => Jwk::check_map(vals)?,
593                    _ => bail!("The JWE {} header claim must be a string.", key),
594                },
595                _ => {}
596            }
597
598            Ok(())
599        })()
600        .map_err(|err| JoseError::InvalidJweFormat(err))
601    }
602}
603
604impl JoseHeader for JweHeader {
605    fn len(&self) -> usize {
606        self.claims.len()
607    }
608
609    fn claim(&self, key: &str) -> Option<&Value> {
610        self.claims.get(key)
611    }
612
613    fn box_clone(&self) -> Box<dyn JoseHeader> {
614        Box::new(self.clone())
615    }
616
617    fn as_any(&self) -> &dyn Any {
618        self
619    }
620
621    fn as_any_mut(&mut self) -> &mut dyn Any {
622        self
623    }
624
625    fn into_any(self: Box<Self>) -> Box<dyn Any> {
626        self
627    }
628}
629
630impl AsRef<Map<String, Value>> for JweHeader {
631    fn as_ref(&self) -> &Map<String, Value> {
632        &self.claims
633    }
634}
635
636impl Into<Map<String, Value>> for JweHeader {
637    fn into(self) -> Map<String, Value> {
638        self.into_map()
639    }
640}
641
642impl Display for JweHeader {
643    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
644        let val = serde_json::to_string(&self.claims).map_err(|_e| std::fmt::Error {})?;
645        fmt.write_str(&val)
646    }
647}
648
649impl Deref for JweHeader {
650    type Target = dyn JoseHeader;
651
652    fn deref(&self) -> &Self::Target {
653        self
654    }
655}
656
657#[cfg(test)]
658mod tests {
659    use anyhow::Result;
660    use serde_json::json;
661
662    use crate::jwe::JweHeader;
663    use crate::jwk::Jwk;
664    use crate::{Map, Value};
665
666    #[test]
667    fn test_new_jwe_header() -> Result<()> {
668        let mut header = JweHeader::new();
669        let jwk = Jwk::new("oct");
670        header.set_algorithm("alg");
671        header.set_content_encryption("enc");
672        header.set_compression("zip");
673        header.set_jwk_set_url("jku");
674        header.set_jwk(jwk.clone());
675        header.set_x509_url("x5u");
676        header.set_x509_certificate_chain(&vec![
677            b"x5c0".to_vec(),
678            b"x5c1".to_vec(),
679            "@@~".as_bytes().to_vec(),
680        ]);
681        header.set_x509_certificate_sha1_thumbprint(b"x5t@@~");
682        header.set_x509_certificate_sha256_thumbprint(b"x5t#S256 @@~");
683        header.set_key_id("kid");
684        header.set_token_type("typ");
685        header.set_content_type("cty");
686        header.set_critical(&vec!["crit0", "crit1"]);
687        header.set_url("url");
688        header.set_nonce(b"nonce");
689        header.set_agreement_partyuinfo(b"apu");
690        header.set_agreement_partyvinfo(b"apv");
691        header.set_issuer("iss");
692        header.set_subject("sub");
693        header.set_claim("header_claim", Some(json!("header_claim")))?;
694
695        assert_eq!(header.algorithm(), Some("alg"));
696        assert_eq!(header.content_encryption(), Some("enc"));
697        assert_eq!(header.compression(), Some("zip"));
698        assert_eq!(header.jwk_set_url(), Some("jku"));
699        assert_eq!(header.jwk(), Some(jwk));
700        assert_eq!(header.x509_url(), Some("x5u"));
701        assert_eq!(
702            header.x509_certificate_chain(),
703            Some(vec![
704                b"x5c0".to_vec(),
705                b"x5c1".to_vec(),
706                "@@~".as_bytes().to_vec()
707            ])
708        );
709        assert_eq!(
710            header.claim("x5c"),
711            Some(&Value::Array(vec![
712                Value::String("eDVjMA==".to_string()),
713                Value::String("eDVjMQ==".to_string()),
714                Value::String("QEB+".to_string()),
715            ]))
716        );
717        assert_eq!(
718            header.x509_certificate_sha1_thumbprint(),
719            Some(b"x5t@@~".to_vec())
720        );
721        assert_eq!(
722            header.claim("x5t"),
723            Some(&Value::String("eDV0QEB-".to_string()))
724        );
725        assert_eq!(
726            header.x509_certificate_sha256_thumbprint(),
727            Some(b"x5t#S256 @@~".to_vec())
728        );
729        assert_eq!(
730            header.claim("x5t#S256"),
731            Some(&Value::String("eDV0I1MyNTYgQEB-".to_string()))
732        );
733        assert_eq!(header.key_id(), Some("kid"));
734        assert_eq!(header.token_type(), Some("typ"));
735        assert_eq!(header.content_type(), Some("cty"));
736        assert_eq!(header.url(), Some("url"));
737        assert_eq!(header.nonce(), Some(b"nonce".to_vec()));
738        assert_eq!(header.agreement_partyuinfo(), Some(b"apu".to_vec()));
739        assert_eq!(header.agreement_partyvinfo(), Some(b"apv".to_vec()));
740        assert_eq!(header.issuer(), Some("iss"));
741        assert_eq!(header.subject(), Some("sub"));
742        assert_eq!(header.critical(), Some(vec!["crit0", "crit1"]));
743        assert_eq!(header.claim("header_claim"), Some(&json!("header_claim")));
744
745        let map: Map<String, Value> = header.clone().into();
746        assert_eq!(JweHeader::from_map(map)?, header);
747
748        Ok(())
749    }
750}