1use bherror::Error;
19
20use crate::error::FormatError;
21
22pub(crate) const SD_JWT_DELIMITER: &str = "~";
23
24#[derive(Debug)]
36pub(crate) struct SdJwt {
37 pub(crate) jwt: String,
38 pub(crate) disclosures: Vec<String>,
39}
40
41#[derive(Debug)]
50pub struct SdJwtKB {
51 pub(crate) sd_jwt: SdJwt,
52 pub(crate) key_binding_jwt: String,
53}
54
55impl SdJwt {
56 pub(crate) fn new(jwt: String, disclosures: Vec<String>) -> Self {
57 Self { jwt, disclosures }
58 }
59}
60
61impl SdJwtKB {
62 pub(crate) fn new(
70 sd_jwt: SdJwt,
71 key_binding_jwt: String,
72 ) -> Result<Self, bherror::Error<FormatError>> {
73 if key_binding_jwt.is_empty() {
74 return Err(Error::root(FormatError::InvalidSdJwtFormat));
75 }
76 Ok(Self {
77 sd_jwt,
78 key_binding_jwt,
79 })
80 }
81}
82
83impl std::str::FromStr for SdJwt {
84 type Err = bherror::Error<FormatError>;
85
86 fn from_str(value: &str) -> Result<Self, Self::Err> {
108 if !value.ends_with('~') {
110 return Err(Error::root(FormatError::InvalidSdJwtFormat));
111 }
112 let sd_jwt_parts: Vec<&str> = value.split(SD_JWT_DELIMITER).collect();
113
114 debug_assert!(sd_jwt_parts.last().unwrap().is_empty());
117 sd_jwt_from_parts(&sd_jwt_parts[0..sd_jwt_parts.len() - 1])
118 }
119}
120
121impl std::str::FromStr for SdJwtKB {
122 type Err = bherror::Error<FormatError>;
123
124 fn from_str(value: &str) -> Result<Self, Self::Err> {
145 let sd_jwt_parts: Vec<&str> = value.split(SD_JWT_DELIMITER).collect();
146
147 let parts_len = sd_jwt_parts.len();
148 let sd_jwt = sd_jwt_from_parts(&sd_jwt_parts[0..parts_len - 1])?;
149 let key_binding_jwt = sd_jwt_parts[parts_len - 1];
150
151 Self::new(sd_jwt, key_binding_jwt.to_owned())
152 }
153}
154
155impl std::fmt::Display for SdJwt {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 write!(f, "{}{}", self.jwt, SD_JWT_DELIMITER)?;
174
175 for disclosure in &self.disclosures {
176 write!(f, "{}{}", disclosure, SD_JWT_DELIMITER)?;
177 }
178
179 Ok(())
180 }
181}
182
183impl std::fmt::Display for SdJwtKB {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 write!(f, "{}{}", self.sd_jwt, self.key_binding_jwt)?;
201 Ok(())
202 }
203}
204
205fn sd_jwt_from_parts(sd_jwt_parts: &[&str]) -> Result<SdJwt, bherror::Error<FormatError>> {
206 let sd_jwt_parts = sd_jwt_parts.split_first();
207 let Some((jwt, disclosures)) = sd_jwt_parts else {
208 return Err(Error::root(FormatError::InvalidSdJwtFormat));
209 };
210
211 let disclosures: Vec<String> = disclosures.iter().map(|&s| s.to_owned()).collect();
212
213 Ok(SdJwt::new(jwt.to_string(), disclosures))
214}
215
216#[cfg(test)]
217mod test {
218 use super::*;
219
220 const JWT: &str = "\
221eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImV4YW1wbGUrc2Qtand0In0.eyJfc2QiOiBb\
222IkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZ\
223akg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBL\
224dVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1\
225SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tB\
226TmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2\
227Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFr\
228b2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpn\
229bGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vaXNzdWVyLmV4YW1wbGUu\
230Y29tIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjog\
231InVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15\
232VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1\
233ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjog\
234InNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0y\
235NTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VH\
236ZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlG\
237MkhaUSJ9fX0.7oEYwv1H4rBa54xAhDH19DEIy-RRSTdwyJvhbjOKVFyQeM0-gcgpwCq-\
238yFCbWj9THEjD9M4yYkAeaWXfuvBS-Q";
239 const DISCLOSURE_1: &str = "WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd";
240 const DISCLOSURE_2: &str = "WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0";
241 const KEY_BINDING_JWT: &str = "\
242eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY\
2433ODkwIiwgImF1ZCI6ICJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUub3JnIiwgImlhdCI\
2446IDE3MDIzMTYwMTUsICJzZF9oYXNoIjogIm5ZY09YeVA0M3Y5c3pLcnluX2tfNEdrUnJ\
245fajNTVEhoTlNTLWkxRHVhdW8ifQ.12Qymun2geGbkYOwiV-DUVfS-zBBKqNe83yNbxM4\
2465J93bno-oM7mph3L1-rPa4lFKQ04wB-T9rU3uAZnBAan5g";
247
248 #[test]
249 fn test_from_str_without_disclosures_without_kb_jwt() {
250 let sd_jwt_presentation = format!("{JWT}~");
251
252 let sd_jwt: SdJwt = sd_jwt_presentation.parse().unwrap();
253
254 assert!(sd_jwt.disclosures.is_empty());
255 assert_eq!(sd_jwt.jwt, JWT);
256
257 let error: Result<SdJwtKB, Error<FormatError>> = sd_jwt_presentation.parse();
259 assert_eq!(error.unwrap_err().error, FormatError::InvalidSdJwtFormat);
260 }
261
262 #[test]
263 fn test_from_str_without_disclosures_with_kb_jwt() {
264 let sd_jwt_kb_presentation = format!("{JWT}~{KEY_BINDING_JWT}");
265
266 let sd_jwt_kb: SdJwtKB = sd_jwt_kb_presentation.parse().unwrap();
267
268 assert!(sd_jwt_kb.sd_jwt.disclosures.is_empty());
269
270 assert_eq!(sd_jwt_kb.sd_jwt.jwt, JWT);
271 assert_eq!(sd_jwt_kb.key_binding_jwt, KEY_BINDING_JWT);
272
273 let error: Result<SdJwt, Error<FormatError>> = sd_jwt_kb_presentation.parse();
275 assert_eq!(error.unwrap_err().error, FormatError::InvalidSdJwtFormat);
276 }
277
278 #[test]
279 fn test_from_str_with_disclosures_without_kb_jwt() {
280 let sd_jwt_presentation = format!("{JWT}~{DISCLOSURE_1}~{DISCLOSURE_2}~");
281
282 let sd_jwt: SdJwt = sd_jwt_presentation.parse().unwrap();
283
284 assert_eq!(sd_jwt.disclosures.len(), 2);
285
286 assert_eq!(sd_jwt.jwt, JWT);
287 assert_eq!(sd_jwt.disclosures, &[DISCLOSURE_1, DISCLOSURE_2]);
288
289 let error: Result<SdJwtKB, Error<FormatError>> = sd_jwt_presentation.parse();
291 assert_eq!(error.unwrap_err().error, FormatError::InvalidSdJwtFormat);
292 }
293
294 #[test]
295 fn test_from_str_with_disclosures_with_kb_jwt() {
296 let sd_jwt_kb_presentation =
297 format!("{JWT}~{DISCLOSURE_1}~{DISCLOSURE_2}~{KEY_BINDING_JWT}");
298
299 let sd_jwt_kb: SdJwtKB = sd_jwt_kb_presentation.parse().unwrap();
300
301 assert_eq!(sd_jwt_kb.sd_jwt.disclosures.len(), 2);
302
303 assert_eq!(sd_jwt_kb.sd_jwt.jwt, JWT);
304 assert_eq!(sd_jwt_kb.sd_jwt.disclosures, &[DISCLOSURE_1, DISCLOSURE_2]);
305 assert_eq!(sd_jwt_kb.key_binding_jwt, KEY_BINDING_JWT);
306
307 let error: Result<SdJwt, Error<FormatError>> = sd_jwt_kb_presentation.parse();
309 assert_eq!(error.unwrap_err().error, FormatError::InvalidSdJwtFormat);
310 }
311
312 #[test]
313 fn test_from_str_invalid_format() {
314 let result = JWT.parse::<SdJwtKB>();
315 assert!(result.is_err());
316 let result = JWT.parse::<SdJwt>();
317 assert!(result.is_err());
318 }
319
320 #[test]
321 fn test_from_str_sd_jwt_kb_empty_key_binding() {
322 let sd_jwt_kb_with_empty_key_binding_presentation =
323 format!("{JWT}~{DISCLOSURE_1}~{DISCLOSURE_2}~");
324
325 let error: Result<SdJwtKB, Error<FormatError>> =
326 sd_jwt_kb_with_empty_key_binding_presentation.parse();
327
328 assert_eq!(error.unwrap_err().error, FormatError::InvalidSdJwtFormat);
329 }
330
331 #[test]
332 fn test_from_str_empty() {
333 let result = "".parse::<SdJwtKB>();
334 assert!(result.is_err());
335 let result = "".parse::<SdJwt>();
336 assert!(result.is_err());
337 }
338
339 #[test]
340 fn test_display_without_disclosures_without_kb_jwt() {
341 let sd_jwt = SdJwt::new(JWT.to_owned(), Vec::new());
342
343 let expected_presentation = format!("{JWT}~");
344
345 assert_eq!(sd_jwt.to_string(), expected_presentation);
346 }
347
348 #[test]
349 fn test_display_without_disclosures_with_kb_jwt() {
350 let sd_jwt = SdJwt {
351 jwt: JWT.to_owned(),
352 disclosures: Vec::new(),
353 };
354 let sd_jwt_kb = SdJwtKB::new(sd_jwt, KEY_BINDING_JWT.to_owned());
355
356 let expected_presentation = format!("{JWT}~{KEY_BINDING_JWT}");
357
358 assert_eq!(sd_jwt_kb.unwrap().to_string(), expected_presentation);
359 }
360
361 #[test]
362 fn test_display_with_disclosures_without_kb_jwt() {
363 let sd_jwt = SdJwt::new(
364 JWT.to_owned(),
365 vec![DISCLOSURE_1.to_owned(), DISCLOSURE_2.to_owned()],
366 );
367
368 let expected_presentation = format!("{JWT}~{DISCLOSURE_1}~{DISCLOSURE_2}~");
369
370 assert_eq!(sd_jwt.to_string(), expected_presentation);
371 }
372
373 #[test]
374 fn test_display_with_disclosures_with_kb_jwt() {
375 let sd_jwt = SdJwt {
376 jwt: JWT.to_owned(),
377 disclosures: vec![DISCLOSURE_1.to_owned(), DISCLOSURE_2.to_owned()],
378 };
379 let sd_jwt_kb = SdJwtKB::new(sd_jwt, KEY_BINDING_JWT.to_owned());
380
381 let expected_presentation =
382 format!("{JWT}~{DISCLOSURE_1}~{DISCLOSURE_2}~{KEY_BINDING_JWT}");
383
384 assert_eq!(sd_jwt_kb.unwrap().to_string(), expected_presentation);
385 }
386}