1#![warn(missing_docs)]
32use rand::{CryptoRng, RngCore};
33use serde::{de::DeserializeOwned, Deserialize, Serialize};
34use serde_json::Value;
35use ssi_claims_core::{
36 DateTimeProvider, ProofValidationError, ResolverProvider, SignatureError, ValidateClaims,
37 Verification,
38};
39use ssi_core::BytesBuf;
40use ssi_jwk::JWKResolver;
41use ssi_jws::{DecodedJws, Jws, JwsPayload, JwsSignature, JwsSigner, ValidateJwsHeader};
42use ssi_jwt::{AnyClaims, ClaimSet, DecodedJwt, JWTClaims};
43use std::{
44 borrow::{Borrow, Cow},
45 collections::BTreeMap,
46 fmt::{self, Write},
47 ops::Deref,
48 str::FromStr,
49};
50
51pub use ssi_core::{json_pointer, JsonPointer, JsonPointerBuf};
52
53pub(crate) mod utils;
54use utils::is_url_safe_base64_char;
55
56mod digest;
57pub use digest::*;
58
59mod decode;
60pub use decode::*;
61
62mod disclosure;
63pub use disclosure::*;
64
65mod conceal;
66pub use conceal::*;
67
68mod reveal;
69pub use reveal::*;
70
71const SD_CLAIM_NAME: &str = "_sd";
72const SD_ALG_CLAIM_NAME: &str = "_sd_alg";
73const ARRAY_CLAIM_ITEM_PROPERTY_NAME: &str = "...";
74
75#[derive(Debug, thiserror::Error)]
77#[error("invalid SD-JWT")]
78pub struct InvalidSdJwt<T = String>(pub T);
79
80impl<T: ?Sized + ToOwned> InvalidSdJwt<&T> {
81 pub fn into_owned(self) -> InvalidSdJwt<T::Owned> {
83 InvalidSdJwt(self.0.to_owned())
84 }
85}
86
87#[macro_export]
89#[collapse_debuginfo(no)]
90macro_rules! sd_jwt {
91 ($value:literal) => {
92 match $crate::SdJwt::from_str_const($value) {
93 Ok(value) => value,
94 Err(_) => panic!("invalid SD-JWT"),
95 }
96 };
97}
98
99#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
112pub struct SdJwt([u8]);
113
114impl SdJwt {
115 pub fn new<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<&Self, InvalidSdJwt<&T>> {
119 let bytes = input.as_ref();
120 if Self::validate(bytes) {
121 Ok(unsafe { Self::new_unchecked(bytes) })
122 } else {
123 Err(InvalidSdJwt(input))
124 }
125 }
126
127 pub const fn from_str_const(input: &str) -> Result<&Self, InvalidSdJwt<&str>> {
131 let bytes = input.as_bytes();
132 if Self::validate(bytes) {
133 Ok(unsafe { Self::new_unchecked(bytes) })
134 } else {
135 Err(InvalidSdJwt(input))
136 }
137 }
138
139 pub const fn validate(bytes: &[u8]) -> bool {
141 let mut i = 0;
142
143 loop {
145 if i >= bytes.len() {
146 return false;
147 }
148
149 if bytes[i] == b'~' {
150 break;
151 }
152
153 i += 1
154 }
155
156 if !Jws::validate_range(bytes, 0, i) {
158 return false;
159 }
160
161 loop {
163 i += 1;
165
166 if i >= bytes.len() {
168 break true;
169 }
170
171 loop {
172 if i >= bytes.len() {
173 return false;
175 }
176
177 if bytes[i] == b'~' {
179 break;
180 }
181
182 if !is_url_safe_base64_char(bytes[i]) {
184 return false;
185 }
186
187 i += 1
188 }
189 }
190 }
191
192 pub const unsafe fn new_unchecked(input: &[u8]) -> &Self {
198 std::mem::transmute(input)
199 }
200
201 pub fn as_bytes(&self) -> &[u8] {
203 &self.0
204 }
205
206 pub fn as_str(&self) -> &str {
208 unsafe {
209 std::str::from_utf8_unchecked(&self.0)
211 }
212 }
213
214 fn jwt_end(&self) -> usize {
216 self.0.iter().copied().position(|c| c == b'~').unwrap()
217 }
218
219 pub fn jwt(&self) -> &Jws {
221 unsafe {
222 Jws::new_unchecked(&self.0[..self.jwt_end()])
225 }
226 }
227
228 pub fn disclosures(&self) -> Disclosures {
230 Disclosures {
231 bytes: &self.0,
232 offset: self.jwt_end() + 1,
233 }
234 }
235
236 pub fn parts(&self) -> PartsRef {
238 PartsRef {
239 jwt: self.jwt(),
240 disclosures: self.disclosures().collect(),
241 }
242 }
243
244 pub fn decode(&self) -> Result<DecodedSdJwt, DecodeError> {
246 self.parts().decode()
247 }
248
249 pub fn decode_reveal<T: DeserializeOwned>(&self) -> Result<RevealedSdJwt<T>, RevealError> {
251 self.parts().decode_reveal()
252 }
253
254 pub fn decode_reveal_any(&self) -> Result<RevealedSdJwt, RevealError> {
256 self.parts().decode_reveal_any()
257 }
258
259 pub async fn decode_verify_concealed<P>(
261 &self,
262 params: P,
263 ) -> Result<(DecodedSdJwt, Verification), ProofValidationError>
264 where
265 P: ResolverProvider<Resolver: JWKResolver>,
266 {
267 self.parts().decode_verify_concealed(params).await
268 }
269
270 pub async fn decode_reveal_verify_any<P>(
279 &self,
280 params: P,
281 ) -> Result<(RevealedSdJwt, Verification), ProofValidationError>
282 where
283 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
284 {
285 self.parts().decode_reveal_verify_any(params).await
286 }
287
288 pub async fn decode_reveal_verify<T, P>(
297 &self,
298 params: P,
299 ) -> Result<(RevealedSdJwt<T>, Verification), ProofValidationError>
300 where
301 T: ClaimSet + DeserializeOwned + ValidateClaims<P, JwsSignature>,
302 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
303 {
304 self.parts().decode_reveal_verify(params).await
305 }
306}
307
308impl AsRef<str> for SdJwt {
309 fn as_ref(&self) -> &str {
310 self.as_str()
311 }
312}
313
314impl AsRef<[u8]> for SdJwt {
315 fn as_ref(&self) -> &[u8] {
316 self.as_bytes()
317 }
318}
319
320impl fmt::Display for SdJwt {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 self.as_str().fmt(f)
323 }
324}
325
326impl fmt::Debug for SdJwt {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 self.as_str().fmt(f)
329 }
330}
331
332impl serde::Serialize for SdJwt {
333 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
334 where
335 S: serde::Serializer,
336 {
337 self.as_str().serialize(serializer)
338 }
339}
340
341impl<'de> serde::Deserialize<'de> for &'de SdJwt {
342 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
343 where
344 D: serde::Deserializer<'de>,
345 {
346 SdJwt::new(<&'de str>::deserialize(deserializer)?).map_err(serde::de::Error::custom)
347 }
348}
349
350#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
352pub struct SdJwtBuf(Vec<u8>);
353
354impl SdJwtBuf {
355 pub fn new<B: BytesBuf>(bytes: B) -> Result<Self, InvalidSdJwt<B>> {
357 if SdJwt::validate(bytes.as_ref()) {
358 Ok(Self(bytes.into()))
359 } else {
360 Err(InvalidSdJwt(bytes))
361 }
362 }
363
364 pub unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
370 Self(bytes)
371 }
372
373 pub async fn conceal_and_sign(
375 claims: &JWTClaims<impl Serialize>,
376 sd_alg: SdAlg,
377 pointers: &[impl Borrow<JsonPointer>],
378 signer: impl JwsSigner,
379 ) -> Result<Self, SignatureError> {
380 DecodedSdJwt::conceal_and_sign(claims, sd_alg, pointers, signer)
381 .await
382 .map(DecodedSdJwt::into_encoded)
383 }
384
385 pub async fn conceal_and_sign_with(
387 claims: &JWTClaims<impl Serialize>,
388 sd_alg: SdAlg,
389 pointers: &[impl Borrow<JsonPointer>],
390 signer: impl JwsSigner,
391 rng: impl CryptoRng + RngCore,
392 ) -> Result<Self, SignatureError> {
393 DecodedSdJwt::conceal_and_sign_with(claims, sd_alg, pointers, signer, rng)
394 .await
395 .map(DecodedSdJwt::into_encoded)
396 }
397
398 pub fn as_sd_jwt(&self) -> &SdJwt {
400 unsafe { SdJwt::new_unchecked(&self.0) }
401 }
402
403 pub fn into_bytes(self) -> Vec<u8> {
405 self.0
406 }
407
408 pub fn into_string(self) -> String {
410 unsafe {
411 String::from_utf8_unchecked(self.0)
413 }
414 }
415}
416
417impl Deref for SdJwtBuf {
418 type Target = SdJwt;
419
420 fn deref(&self) -> &Self::Target {
421 self.as_sd_jwt()
422 }
423}
424
425impl Borrow<SdJwt> for SdJwtBuf {
426 fn borrow(&self) -> &SdJwt {
427 self.as_sd_jwt()
428 }
429}
430
431impl AsRef<SdJwt> for SdJwtBuf {
432 fn as_ref(&self) -> &SdJwt {
433 self.as_sd_jwt()
434 }
435}
436
437impl AsRef<str> for SdJwtBuf {
438 fn as_ref(&self) -> &str {
439 self.as_str()
440 }
441}
442
443impl AsRef<[u8]> for SdJwtBuf {
444 fn as_ref(&self) -> &[u8] {
445 self.as_bytes()
446 }
447}
448
449impl fmt::Display for SdJwtBuf {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 self.as_str().fmt(f)
452 }
453}
454
455impl fmt::Debug for SdJwtBuf {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 self.as_str().fmt(f)
458 }
459}
460
461impl FromStr for SdJwtBuf {
462 type Err = InvalidSdJwt;
463
464 fn from_str(s: &str) -> Result<Self, Self::Err> {
465 Self::new(s.to_owned())
466 }
467}
468
469impl serde::Serialize for SdJwtBuf {
470 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
471 where
472 S: serde::Serializer,
473 {
474 self.as_str().serialize(serializer)
475 }
476}
477
478impl<'de> serde::Deserialize<'de> for SdJwtBuf {
479 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
480 where
481 D: serde::Deserializer<'de>,
482 {
483 String::deserialize(deserializer)?
484 .parse()
485 .map_err(serde::de::Error::custom)
486 }
487}
488
489pub struct Disclosures<'a> {
491 bytes: &'a [u8],
493
494 offset: usize,
496}
497
498impl<'a> Iterator for Disclosures<'a> {
499 type Item = &'a Disclosure;
500
501 fn next(&mut self) -> Option<Self::Item> {
502 let mut i = self.offset;
503
504 while i < self.bytes.len() {
505 if self.bytes[i] == b'~' {
506 let disclosure = unsafe {
507 Disclosure::new_unchecked(&self.bytes[self.offset..i])
510 };
511
512 self.offset = i + 1;
513 return Some(disclosure);
514 }
515
516 i += 1
517 }
518
519 None
520 }
521}
522
523#[derive(Debug, PartialEq)]
526pub struct PartsRef<'a> {
527 pub jwt: &'a Jws,
529
530 pub disclosures: Vec<&'a Disclosure>,
532}
533
534impl<'a> PartsRef<'a> {
535 pub fn new(jwt: &'a Jws, disclosures: Vec<&'a Disclosure>) -> Self {
537 Self { jwt, disclosures }
538 }
539
540 pub fn decode_reveal<T: DeserializeOwned>(self) -> Result<RevealedSdJwt<'a, T>, RevealError> {
542 let decoded = self.decode()?;
543 decoded.reveal()
544 }
545
546 pub fn decode_reveal_any(self) -> Result<RevealedSdJwt<'a>, RevealError> {
548 let decoded = self.decode()?;
549 decoded.reveal_any()
550 }
551
552 pub async fn decode_verify_concealed<P>(
554 self,
555 params: P,
556 ) -> Result<(DecodedSdJwt<'a>, Verification), ProofValidationError>
557 where
558 P: ResolverProvider<Resolver: JWKResolver>,
559 {
560 let decoded = self.decode().map_err(ProofValidationError::input_data)?;
561 let verification = decoded.verify_concealed(params).await?;
562 Ok((decoded, verification))
563 }
564
565 pub async fn decode_reveal_verify_any<P>(
574 self,
575 params: P,
576 ) -> Result<(RevealedSdJwt<'a>, Verification), ProofValidationError>
577 where
578 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
579 {
580 let decoded = self.decode().map_err(ProofValidationError::input_data)?;
581 decoded.reveal_verify_any(params).await
582 }
583
584 pub async fn decode_reveal_verify<T, P>(
593 self,
594 params: P,
595 ) -> Result<(RevealedSdJwt<'a, T>, Verification), ProofValidationError>
596 where
597 T: ClaimSet + DeserializeOwned + ValidateClaims<P, JwsSignature>,
598 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
599 {
600 let decoded = self.decode().map_err(ProofValidationError::input_data)?;
601 decoded.reveal_verify(params).await
602 }
603}
604
605impl fmt::Display for PartsRef<'_> {
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 self.jwt.fmt(f)?;
608 f.write_char('~')?;
609
610 for d in &self.disclosures {
611 d.fmt(f)?;
612 f.write_char('~')?;
613 }
614
615 Ok(())
616 }
617}
618
619#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct SdJwtPayload {
622 #[serde(rename = "_sd_alg")]
624 pub sd_alg: SdAlg,
625
626 #[serde(flatten)]
628 pub claims: serde_json::Map<String, Value>,
629}
630
631impl JwsPayload for SdJwtPayload {
632 fn payload_bytes(&self) -> Cow<[u8]> {
633 Cow::Owned(serde_json::to_vec(self).unwrap())
634 }
635}
636
637impl<E> ValidateJwsHeader<E> for SdJwtPayload {}
638
639impl<E, P> ValidateClaims<E, P> for SdJwtPayload {}
640
641pub struct DecodedSdJwt<'a> {
643 pub jwt: DecodedJws<'a, SdJwtPayload>,
645
646 pub disclosures: Vec<DecodedDisclosure<'a>>,
648}
649
650impl<'a> DecodedSdJwt<'a> {
651 pub async fn verify_concealed<P>(&self, params: P) -> Result<Verification, ProofValidationError>
656 where
657 P: ResolverProvider<Resolver: JWKResolver>,
658 {
659 self.jwt.verify(params).await
660 }
661
662 pub async fn reveal_verify_any<P>(
670 self,
671 params: P,
672 ) -> Result<(RevealedSdJwt<'a>, Verification), ProofValidationError>
673 where
674 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
675 {
676 let revealed = self
677 .reveal_any()
678 .map_err(ProofValidationError::input_data)?;
679 let verification = revealed.verify(params).await?;
680 Ok((revealed, verification))
681 }
682
683 pub async fn reveal_verify<T, P>(
691 self,
692 params: P,
693 ) -> Result<(RevealedSdJwt<'a, T>, Verification), ProofValidationError>
694 where
695 T: ClaimSet + DeserializeOwned + ValidateClaims<P, JwsSignature>,
696 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
697 {
698 let revealed = self
699 .reveal::<T>()
700 .map_err(ProofValidationError::input_data)?;
701 let verification = revealed.verify(params).await?;
702 Ok((revealed, verification))
703 }
704}
705
706impl DecodedSdJwt<'static> {
707 pub async fn conceal_and_sign(
709 claims: &JWTClaims<impl Serialize>,
710 sd_alg: SdAlg,
711 pointers: &[impl Borrow<JsonPointer>],
712 signer: impl JwsSigner,
713 ) -> Result<Self, SignatureError> {
714 let (payload, disclosures) =
715 SdJwtPayload::conceal(claims, sd_alg, pointers).map_err(SignatureError::other)?;
716
717 Ok(Self {
718 jwt: signer.sign_into_decoded(payload).await?,
719 disclosures,
720 })
721 }
722
723 pub async fn conceal_and_sign_with(
725 claims: &JWTClaims<impl Serialize>,
726 sd_alg: SdAlg,
727 pointers: &[impl Borrow<JsonPointer>],
728 signer: impl JwsSigner,
729 rng: impl CryptoRng + RngCore,
730 ) -> Result<Self, SignatureError> {
731 let (payload, disclosures) = SdJwtPayload::conceal_with(claims, sd_alg, pointers, rng)
732 .map_err(SignatureError::other)?;
733
734 Ok(Self {
735 jwt: signer.sign_into_decoded(payload).await?,
736 disclosures,
737 })
738 }
739
740 pub fn into_encoded(self) -> SdJwtBuf {
742 let mut bytes = self.jwt.into_encoded().into_bytes();
743 bytes.push(b'~');
744
745 for d in self.disclosures {
746 bytes.extend_from_slice(d.encoded.as_bytes());
747 bytes.push(b'~');
748 }
749
750 unsafe {
751 SdJwtBuf::new_unchecked(bytes)
754 }
755 }
756}
757
758#[derive(Debug, Clone)]
764pub struct RevealedSdJwt<'a, T = AnyClaims> {
765 pub jwt: DecodedJwt<'a, T>,
770
771 pub disclosures: BTreeMap<JsonPointerBuf, DecodedDisclosure<'a>>,
773}
774
775impl<'a, T> RevealedSdJwt<'a, T> {
776 pub fn claims(&self) -> &JWTClaims<T> {
778 &self.jwt.signing_bytes.payload
779 }
780
781 pub fn into_claims(self) -> JWTClaims<T> {
783 self.jwt.signing_bytes.payload
784 }
785
786 pub async fn verify<P>(&self, params: P) -> Result<Verification, ProofValidationError>
788 where
789 T: ClaimSet + ValidateClaims<P, JwsSignature>,
790 P: ResolverProvider<Resolver: JWKResolver> + DateTimeProvider,
791 {
792 self.jwt.verify(params).await
793 }
794
795 pub fn clear(&mut self) {
797 self.disclosures.clear()
798 }
799
800 pub fn cleared(mut self) -> Self {
802 self.clear();
803 self
804 }
805
806 pub fn retain(
812 &mut self,
813 pointers: &[impl Borrow<JsonPointer>],
814 ) -> BTreeMap<JsonPointerBuf, DecodedDisclosure<'a>> {
815 let mut disclosures = BTreeMap::new();
816
817 for p in pointers {
818 if let Some((p, d)) = self.disclosures.remove_entry(p.borrow()) {
819 disclosures.insert(p, d);
820 }
821 }
822
823 std::mem::swap(&mut disclosures, &mut self.disclosures);
824 disclosures
825 }
826
827 pub fn retaining(mut self, pointers: &[impl Borrow<JsonPointer>]) -> Self {
833 self.retain(pointers);
834 self
835 }
836
837 pub fn reject(
843 &mut self,
844 pointers: &[impl Borrow<JsonPointer>],
845 ) -> BTreeMap<JsonPointerBuf, DecodedDisclosure<'a>> {
846 let mut disclosures = BTreeMap::new();
847
848 for p in pointers {
849 if let Some((p, d)) = self.disclosures.remove_entry(p.borrow()) {
850 disclosures.insert(p, d);
851 }
852 }
853
854 disclosures
855 }
856
857 pub fn rejecting(mut self, pointers: &[impl Borrow<JsonPointer>]) -> Self {
860 self.reject(pointers);
861 self
862 }
863
864 pub fn into_encoded(self) -> SdJwtBuf {
866 let mut bytes = self.jwt.into_encoded().into_bytes();
867 bytes.push(b'~');
868
869 for d in self.disclosures.into_values() {
870 bytes.extend_from_slice(d.encoded.as_bytes());
871 bytes.push(b'~');
872 }
873
874 unsafe {
875 SdJwtBuf::new_unchecked(bytes)
878 }
879 }
880}
881
882#[cfg(test)]
883mod tests {
884 use super::*;
885
886 const ENCODED: &str = concat!(
887 "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkM5aW5wNllvUmFFWFI0Mjd6WUpQN1Fya",
888 "zFXSF84YmR3T0FfWVVyVW5HUVUiLCAiS3VldDF5QWEwSElRdlluT1ZkNTloY1ZpTzlVZ",
889 "zZKMmtTZnFZUkJlb3d2RSIsICJNTWxkT0ZGekIyZDB1bWxtcFRJYUdlcmhXZFVfUHBZZ",
890 "kx2S2hoX2ZfOWFZIiwgIlg2WkFZT0lJMnZQTjQwVjd4RXhad1Z3ejd5Um1MTmNWd3Q1R",
891 "Ew4Ukx2NGciLCAiWTM0em1JbzBRTExPdGRNcFhHd2pCZ0x2cjE3eUVoaFlUMEZHb2ZSL",
892 "WFJRSIsICJmeUdwMFdUd3dQdjJKRFFsbjFsU2lhZW9iWnNNV0ExMGJRNTk4OS05RFRzI",
893 "iwgIm9tbUZBaWNWVDhMR0hDQjB1eXd4N2ZZdW8zTUhZS08xNWN6LVJaRVlNNVEiLCAic",
894 "zBCS1lzTFd4UVFlVTh0VmxsdE03TUtzSVJUckVJYTFQa0ptcXhCQmY1VSJdLCAiaXNzI",
895 "jogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsI",
896 "CJleHAiOiAxODgzMDAwMDAwLCAiYWRkcmVzcyI6IHsiX3NkIjogWyI2YVVoelloWjdTS",
897 "jFrVm1hZ1FBTzN1MkVUTjJDQzFhSGhlWnBLbmFGMF9FIiwgIkF6TGxGb2JrSjJ4aWF1c",
898 "FJFUHlvSnotOS1OU2xkQjZDZ2pyN2ZVeW9IemciLCAiUHp6Y1Z1MHFiTXVCR1NqdWxmZ",
899 "Xd6a2VzRDl6dXRPRXhuNUVXTndrclEtayIsICJiMkRrdzBqY0lGOXJHZzhfUEY4WmN2b",
900 "mNXN3p3Wmo1cnlCV3ZYZnJwemVrIiwgImNQWUpISVo4VnUtZjlDQ3lWdWIyVWZnRWs4a",
901 "nZ2WGV6d0sxcF9KbmVlWFEiLCAiZ2xUM2hyU1U3ZlNXZ3dGNVVEWm1Xd0JUdzMyZ25Vb",
902 "GRJaGk4aEdWQ2FWNCIsICJydkpkNmlxNlQ1ZWptc0JNb0d3dU5YaDlxQUFGQVRBY2k0M",
903 "G9pZEVlVnNBIiwgInVOSG9XWWhYc1poVkpDTkUyRHF5LXpxdDd0NjlnSkt5NVFhRnY3R",
904 "3JNWDQiXX0sICJfc2RfYWxnIjogInNoYS0yNTYifQ.rFsowW-KSZe7EITlWsGajR9nnG",
905 "BLlQ78qgtdGIZg3FZuZnxtapP0H8CUMnffJAwPQJmGnpFpulTkLWHiI1kMmw~WyJHMDJ",
906 "OU3JRZmpGWFE3SW8wOXN5YWpBIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ~WyJs",
907 "a2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImNvdW50cnkiLCAiSlAiXQ~"
908 );
909
910 const JWT: &str = concat!(
911 "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkM5aW5wNllvUmFFWFI0Mjd6WUpQN1Fya",
912 "zFXSF84YmR3T0FfWVVyVW5HUVUiLCAiS3VldDF5QWEwSElRdlluT1ZkNTloY1ZpTzlVZ",
913 "zZKMmtTZnFZUkJlb3d2RSIsICJNTWxkT0ZGekIyZDB1bWxtcFRJYUdlcmhXZFVfUHBZZ",
914 "kx2S2hoX2ZfOWFZIiwgIlg2WkFZT0lJMnZQTjQwVjd4RXhad1Z3ejd5Um1MTmNWd3Q1R",
915 "Ew4Ukx2NGciLCAiWTM0em1JbzBRTExPdGRNcFhHd2pCZ0x2cjE3eUVoaFlUMEZHb2ZSL",
916 "WFJRSIsICJmeUdwMFdUd3dQdjJKRFFsbjFsU2lhZW9iWnNNV0ExMGJRNTk4OS05RFRzI",
917 "iwgIm9tbUZBaWNWVDhMR0hDQjB1eXd4N2ZZdW8zTUhZS08xNWN6LVJaRVlNNVEiLCAic",
918 "zBCS1lzTFd4UVFlVTh0VmxsdE03TUtzSVJUckVJYTFQa0ptcXhCQmY1VSJdLCAiaXNzI",
919 "jogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsI",
920 "CJleHAiOiAxODgzMDAwMDAwLCAiYWRkcmVzcyI6IHsiX3NkIjogWyI2YVVoelloWjdTS",
921 "jFrVm1hZ1FBTzN1MkVUTjJDQzFhSGhlWnBLbmFGMF9FIiwgIkF6TGxGb2JrSjJ4aWF1c",
922 "FJFUHlvSnotOS1OU2xkQjZDZ2pyN2ZVeW9IemciLCAiUHp6Y1Z1MHFiTXVCR1NqdWxmZ",
923 "Xd6a2VzRDl6dXRPRXhuNUVXTndrclEtayIsICJiMkRrdzBqY0lGOXJHZzhfUEY4WmN2b",
924 "mNXN3p3Wmo1cnlCV3ZYZnJwemVrIiwgImNQWUpISVo4VnUtZjlDQ3lWdWIyVWZnRWs4a",
925 "nZ2WGV6d0sxcF9KbmVlWFEiLCAiZ2xUM2hyU1U3ZlNXZ3dGNVVEWm1Xd0JUdzMyZ25Vb",
926 "GRJaGk4aEdWQ2FWNCIsICJydkpkNmlxNlQ1ZWptc0JNb0d3dU5YaDlxQUFGQVRBY2k0M",
927 "G9pZEVlVnNBIiwgInVOSG9XWWhYc1poVkpDTkUyRHF5LXpxdDd0NjlnSkt5NVFhRnY3R",
928 "3JNWDQiXX0sICJfc2RfYWxnIjogInNoYS0yNTYifQ.rFsowW-KSZe7EITlWsGajR9nnG",
929 "BLlQ78qgtdGIZg3FZuZnxtapP0H8CUMnffJAwPQJmGnpFpulTkLWHiI1kMmw"
930 );
931
932 const DISCLOSURE_0: &str =
933 "WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ";
934 const DISCLOSURE_1: &str = "WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImNvdW50cnkiLCAiSlAiXQ";
935
936 #[test]
937 fn deserialize() {
938 assert_eq!(
939 SdJwt::new(ENCODED).unwrap().parts(),
940 PartsRef::new(
941 Jws::new(JWT).unwrap(),
942 vec![
943 Disclosure::new(DISCLOSURE_0).unwrap(),
944 Disclosure::new(DISCLOSURE_1).unwrap()
945 ]
946 )
947 )
948 }
949
950 #[test]
951 fn deserialize_fails_with_emtpy() {
952 assert!(SdJwt::new("").is_err())
953 }
954
955 #[test]
956 fn serialize_parts() {
957 assert_eq!(
958 PartsRef::new(
959 Jws::new(JWT).unwrap(),
960 vec![
961 Disclosure::new(DISCLOSURE_0).unwrap(),
962 Disclosure::new(DISCLOSURE_1).unwrap()
963 ]
964 )
965 .to_string(),
966 ENCODED,
967 )
968 }
969}