1use std::collections::HashSet;
5use std::fmt::Display;
6use std::ops::Deref;
7use std::ops::DerefMut;
8use std::str::FromStr;
9
10use crate::jwt::Jwt;
11use crate::Disclosure;
12use crate::Error;
13use crate::Hasher;
14use crate::JsonObject;
15use crate::KeyBindingJwt;
16use crate::RequiredKeyBinding;
17use crate::Result;
18use crate::SdObjectDecoder;
19use crate::ARRAY_DIGEST_KEY;
20use crate::DIGESTS_KEY;
21use crate::SHA_ALG_NAME;
22use indexmap::IndexMap;
23use itertools::Either;
24use itertools::Itertools;
25use serde::Deserialize;
26use serde::Serialize;
27use serde_json::Value;
28
29#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
30pub struct SdJwtClaims {
31 #[serde(skip_serializing_if = "Vec::is_empty", default)]
32 pub _sd: Vec<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub _sd_alg: Option<String>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub cnf: Option<RequiredKeyBinding>,
37 #[serde(flatten)]
38 properties: JsonObject,
39}
40
41impl Deref for SdJwtClaims {
42 type Target = JsonObject;
43 fn deref(&self) -> &Self::Target {
44 &self.properties
45 }
46}
47
48impl DerefMut for SdJwtClaims {
49 fn deref_mut(&mut self) -> &mut Self::Target {
50 &mut self.properties
51 }
52}
53
54#[derive(Debug, Clone, Eq, PartialEq)]
57pub struct SdJwt {
58 jwt: Jwt<SdJwtClaims>,
60 disclosures: Vec<Disclosure>,
62 key_binding_jwt: Option<KeyBindingJwt>,
64}
65
66impl SdJwt {
67 pub(crate) fn new(
69 jwt: Jwt<SdJwtClaims>,
70 disclosures: Vec<Disclosure>,
71 key_binding_jwt: Option<KeyBindingJwt>,
72 ) -> Self {
73 Self {
74 jwt,
75 disclosures,
76 key_binding_jwt,
77 }
78 }
79
80 pub fn headers(&self) -> &JsonObject {
81 &self.jwt.header
82 }
83
84 pub fn claims(&self) -> &SdJwtClaims {
85 &self.jwt.claims
86 }
87
88 pub fn claims_mut(&mut self) -> &mut SdJwtClaims {
93 &mut self.jwt.claims
94 }
95
96 pub fn disclosures(&self) -> &[Disclosure] {
98 &self.disclosures
99 }
100
101 pub fn required_key_bind(&self) -> Option<&RequiredKeyBinding> {
103 self.claims().cnf.as_ref()
104 }
105
106 pub fn key_binding_jwt(&self) -> Option<&KeyBindingJwt> {
108 self.key_binding_jwt.as_ref()
109 }
110
111 pub fn attach_key_binding_jwt(&mut self, kb_jwt: KeyBindingJwt) {
116 self.key_binding_jwt = Some(kb_jwt);
117 }
118
119 pub fn presentation(&self) -> String {
121 let disclosures = self.disclosures.iter().map(ToString::to_string).join("~");
122 let key_bindings = self
123 .key_binding_jwt
124 .as_ref()
125 .map(ToString::to_string)
126 .unwrap_or_default();
127 if disclosures.is_empty() {
128 format!("{}~{}", self.jwt, key_bindings)
129 } else {
130 format!("{}~{}~{}", self.jwt, disclosures, key_bindings)
131 }
132 }
133
134 pub fn parse(sd_jwt: &str) -> Result<Self> {
136 let sd_segments: Vec<&str> = sd_jwt.split('~').collect();
137 let num_of_segments = sd_segments.len();
138 if num_of_segments < 2 {
139 return Err(Error::DeserializationError(
140 "SD-JWT format is invalid, less than 2 segments".to_string(),
141 ));
142 }
143
144 let jwt = sd_segments.first().unwrap().parse()?;
145
146 let disclosures = sd_segments[1..num_of_segments - 1]
147 .iter()
148 .map(|s| Disclosure::parse(s))
149 .try_collect()?;
150
151 let key_binding_jwt = sd_segments
152 .last()
153 .filter(|segment| !segment.is_empty())
154 .map(|segment| segment.parse())
155 .transpose()?;
156
157 Ok(Self {
158 jwt,
159 disclosures,
160 key_binding_jwt,
161 })
162 }
163
164 pub fn into_presentation(self, hasher: &dyn Hasher) -> Result<SdJwtPresentationBuilder> {
169 SdJwtPresentationBuilder::new(self, hasher)
170 }
171
172 pub fn into_disclosed_object(self, hasher: &dyn Hasher) -> Result<JsonObject> {
175 let decoder = SdObjectDecoder;
176 let object = serde_json::to_value(self.claims()).unwrap();
177
178 let disclosure_map = self
179 .disclosures
180 .into_iter()
181 .map(|disclosure| (hasher.encoded_digest(disclosure.as_str()), disclosure))
182 .collect();
183
184 decoder.decode(object.as_object().unwrap(), &disclosure_map)
185 }
186}
187
188impl Display for SdJwt {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 f.write_str(&(self.presentation()))
191 }
192}
193
194impl FromStr for SdJwt {
195 type Err = Error;
196 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
197 Self::parse(s)
198 }
199}
200
201#[derive(Debug, Clone)]
202pub struct SdJwtPresentationBuilder {
203 sd_jwt: SdJwt,
204 disclosures: IndexMap<String, Disclosure>,
205 disclosures_to_omit: HashSet<usize>,
206 object: Value,
207}
208
209impl Deref for SdJwtPresentationBuilder {
210 type Target = SdJwt;
211 fn deref(&self) -> &Self::Target {
212 &self.sd_jwt
213 }
214}
215
216impl SdJwtPresentationBuilder {
217 pub fn new(mut sd_jwt: SdJwt, hasher: &dyn Hasher) -> Result<Self> {
218 let required_hasher = sd_jwt.claims()._sd_alg.as_deref().unwrap_or(SHA_ALG_NAME);
219 if required_hasher != hasher.alg_name() {
220 return Err(Error::InvalidHasher(format!(
221 "hasher \"{}\" was provided, but \"{required_hasher} is required\"",
222 hasher.alg_name()
223 )));
224 }
225 let disclosures = std::mem::take(&mut sd_jwt.disclosures)
226 .into_iter()
227 .map(|disclosure| (hasher.encoded_digest(disclosure.as_str()), disclosure))
228 .collect();
229 let object = {
230 let sd = std::mem::take(&mut sd_jwt.jwt.claims._sd)
231 .into_iter()
232 .map(Value::String)
233 .collect();
234 let mut object = Value::Object(std::mem::take(&mut sd_jwt.jwt.claims.properties));
235 object
236 .as_object_mut()
237 .unwrap()
238 .insert(DIGESTS_KEY.to_string(), Value::Array(sd));
239
240 object
241 };
242 Ok(Self {
243 sd_jwt,
244 disclosures,
245 disclosures_to_omit: HashSet::default(),
246 object,
247 })
248 }
249
250 pub fn conceal(mut self, path: &str) -> Result<Self> {
256 self
257 .disclosures_to_omit
258 .extend(find_disclosure_and_sub_disclosures_for_value_at_path(
259 &self.object,
260 path,
261 &self.disclosures,
262 )?);
263 Ok(self)
264 }
265
266 pub fn conceal_all(mut self) -> Self {
270 self.disclosures_to_omit.extend(0..self.disclosures.len());
271 self
272 }
273
274 pub fn disclose(mut self, path: &str) -> Result<Self> {
282 let disclosing = find_disclosure_and_parent_disclosures_for_value_at_path(&self.object, path, &self.disclosures)?;
283 for idx in disclosing {
284 self.disclosures_to_omit.remove(&idx);
285 }
286 Ok(self)
287 }
288
289 pub fn finish(self) -> (SdJwt, Vec<Disclosure>) {
291 let SdJwtPresentationBuilder {
293 mut sd_jwt,
294 disclosures,
295 disclosures_to_omit,
296 object,
297 ..
298 } = self;
299
300 let (disclosures_to_keep, omitted_disclosures) =
301 disclosures
302 .into_values()
303 .enumerate()
304 .partition_map(|(idx, disclosure)| {
305 if disclosures_to_omit.contains(&idx) {
306 Either::Right(disclosure)
307 } else {
308 Either::Left(disclosure)
309 }
310 });
311
312 let Value::Object(mut obj) = object else {
313 unreachable!();
314 };
315 let Value::Array(sd) = obj.remove(DIGESTS_KEY).unwrap_or(Value::Array(vec![])) else {
316 unreachable!()
317 };
318 sd_jwt.jwt.claims._sd = sd
319 .into_iter()
320 .map(|value| {
321 if let Value::String(s) = value {
322 s
323 } else {
324 unreachable!()
325 }
326 })
327 .collect();
328 sd_jwt.jwt.claims.properties = obj;
329 sd_jwt.disclosures = disclosures_to_keep;
330
331 (sd_jwt, omitted_disclosures)
332 }
333}
334
335fn find_disclosure_and_sub_disclosures_for_value_at_path<'a>(
336 value: &'a Value,
337 path: &str,
338 disclosures: &'a IndexMap<String, Disclosure>,
339) -> Result<Vec<usize>> {
340 let path_segments = path.trim_start_matches('/').split('/').collect_vec();
341 let (value, mut visited_disclosures) = traverse_disclosable_object(value, &path_segments, disclosures)
342 .ok_or_else(|| Error::InvalidPath("the referenced element doesn't exist or is not concealable".to_owned()))?;
343 let path_referenced_disclosure = visited_disclosures
344 .pop()
345 .ok_or_else(|| Error::InvalidPath("the referenced element doesn't exist or is not concealable".to_owned()))?;
346
347 let mut disclosures_to_omit = get_all_sub_disclosures(value, disclosures);
348 disclosures_to_omit.push(path_referenced_disclosure);
349
350 Ok(disclosures_to_omit)
351}
352
353fn find_disclosure_and_parent_disclosures_for_value_at_path<'a>(
354 value: &'a Value,
355 path: &str,
356 disclosures: &'a IndexMap<String, Disclosure>,
357) -> Result<Vec<usize>> {
358 let path_segments = path.trim_start_matches('/').split('/').collect_vec();
359 traverse_disclosable_object(value, &path_segments, disclosures)
360 .map(|(_, disclosures)| disclosures)
361 .ok_or_else(|| Error::InvalidPath("the referenced element doesn't exist or is not concealable".to_owned()))
362}
363
364fn find_disclosure(object: &JsonObject, key: &str, disclosures: &IndexMap<String, Disclosure>) -> Option<usize> {
365 object
368 .get(DIGESTS_KEY)
369 .and_then(|value| value.as_array())
370 .iter()
371 .flat_map(|values| values.iter())
372 .flat_map(|value| value.as_str())
373 .find(|digest| {
374 disclosures
375 .get(*digest)
376 .and_then(|disclosure| disclosure.claim_name.as_deref())
377 .is_some_and(|name| name == key)
378 })
379 .and_then(|digest| disclosures.get_index_of(digest))
380}
381
382fn traverse_disclosable_object<'a>(
383 mut value: &'a Value,
384 path: &[&str],
385 disclosures: &'a IndexMap<String, Disclosure>,
386) -> Option<(&'a Value, Vec<usize>)> {
387 let mut visited_disclosures = vec![];
388 for path_segment in path {
389 let step = traverse_disclosable_object_step(value, path_segment, disclosures)?;
390 value = step.value;
391 if let Some(disclosure) = step.disclosure {
392 visited_disclosures.push(disclosure)
393 }
394 }
395
396 Some((value, visited_disclosures))
397}
398
399fn traverse_disclosable_object_step<'a>(
400 value: &'a Value,
401 path_fragment: &str,
402 disclosures: &'a IndexMap<String, Disclosure>,
403) -> Option<TraversalResult<'a>> {
404 match value {
405 Value::Object(object) if object.contains_key(path_fragment) => {
407 Some(TraversalResult::new_value(object.get(path_fragment).unwrap()))
408 }
409 Value::Object(object) => {
411 let idx = find_disclosure(object, path_fragment, disclosures)?;
412 let (_, disclosure) = disclosures.get_index(idx).unwrap();
413 Some(TraversalResult::new_from_disclosure(idx, disclosure))
414 }
415 Value::Array(array) => {
416 let arr_idx = path_fragment.parse::<usize>().ok()?;
417 let value = array.get(arr_idx)?;
418
419 if let Some(digest) = value.get(ARRAY_DIGEST_KEY).and_then(|value| value.as_str()) {
421 disclosures
422 .get_full(digest)
423 .map(|(idx, _, disclosure)| TraversalResult::new_from_disclosure(idx, disclosure))
424 } else {
425 Some(TraversalResult::new_value(value))
426 }
427 }
428 _ => None,
429 }
430}
431
432#[derive(Debug)]
434struct TraversalResult<'a> {
435 value: &'a Value,
437 disclosure: Option<usize>,
439}
440
441impl<'a> TraversalResult<'a> {
442 fn new_value(value: &'a Value) -> Self {
443 Self {
444 value,
445 disclosure: None,
446 }
447 }
448
449 fn new_from_disclosure(idx: usize, disclosure: &'a Disclosure) -> Self {
450 Self {
451 value: &disclosure.claim_value,
452 disclosure: Some(idx),
453 }
454 }
455}
456
457fn get_all_sub_disclosures<'a>(value: &'a Value, disclosures: &'a IndexMap<String, Disclosure>) -> Vec<usize> {
458 let mut sub_disclosures = vec![];
459 match value {
460 Value::Object(object) => {
461 object
463 .get(DIGESTS_KEY)
464 .and_then(|sd| sd.as_array())
465 .map(|sd| sd.iter())
466 .unwrap_or_default()
467 .flat_map(|value| value.as_str())
468 .filter_map(|digest| disclosures.get_index_of(digest))
469 .for_each(|idx| sub_disclosures.push(idx));
470 object.values().for_each(|value| {
472 let found_sub_disclosures = get_all_sub_disclosures(value, disclosures);
473 sub_disclosures.extend(found_sub_disclosures);
474 });
475 }
476 Value::Array(arr) => {
477 for value in arr.iter().filter(|value| value.is_object()) {
478 if let Some(idx) = value
479 .get(ARRAY_DIGEST_KEY)
480 .and_then(|value| value.as_str())
481 .and_then(|digest| disclosures.get_index_of(digest))
482 {
483 sub_disclosures.push(idx);
484 } else {
485 sub_disclosures.extend(get_all_sub_disclosures(value, disclosures));
486 }
487 }
488 }
489 _ => (),
490 }
491
492 sub_disclosures
493}
494
495#[cfg(test)]
496mod test {
497 use crate::SdJwt;
498 const SD_JWT: &str = "eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImV4YW1wbGUrc2Qtand0In0.eyJfc2QiOiBbIkM5aW5wNllvUmFFWFI0Mjd6WUpQN1FyazFXSF84YmR3T0FfWVVyVW5HUVUiLCAiS3VldDF5QWEwSElRdlluT1ZkNTloY1ZpTzlVZzZKMmtTZnFZUkJlb3d2RSIsICJNTWxkT0ZGekIyZDB1bWxtcFRJYUdlcmhXZFVfUHBZZkx2S2hoX2ZfOWFZIiwgIlg2WkFZT0lJMnZQTjQwVjd4RXhad1Z3ejd5Um1MTmNWd3Q1REw4Ukx2NGciLCAiWTM0em1JbzBRTExPdGRNcFhHd2pCZ0x2cjE3eUVoaFlUMEZHb2ZSLWFJRSIsICJmeUdwMFdUd3dQdjJKRFFsbjFsU2lhZW9iWnNNV0ExMGJRNTk4OS05RFRzIiwgIm9tbUZBaWNWVDhMR0hDQjB1eXd4N2ZZdW8zTUhZS08xNWN6LVJaRVlNNVEiLCAiczBCS1lzTFd4UVFlVTh0VmxsdE03TUtzSVJUckVJYTFQa0ptcXhCQmY1VSJdLCAiaXNzIjogImh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAiYWRkcmVzcyI6IHsiX3NkIjogWyI2YVVoelloWjdTSjFrVm1hZ1FBTzN1MkVUTjJDQzFhSGhlWnBLbmFGMF9FIiwgIkF6TGxGb2JrSjJ4aWF1cFJFUHlvSnotOS1OU2xkQjZDZ2pyN2ZVeW9IemciLCAiUHp6Y1Z1MHFiTXVCR1NqdWxmZXd6a2VzRDl6dXRPRXhuNUVXTndrclEtayIsICJiMkRrdzBqY0lGOXJHZzhfUEY4WmN2bmNXN3p3Wmo1cnlCV3ZYZnJwemVrIiwgImNQWUpISVo4VnUtZjlDQ3lWdWIyVWZnRWs4anZ2WGV6d0sxcF9KbmVlWFEiLCAiZ2xUM2hyU1U3ZlNXZ3dGNVVEWm1Xd0JUdzMyZ25VbGRJaGk4aEdWQ2FWNCIsICJydkpkNmlxNlQ1ZWptc0JNb0d3dU5YaDlxQUFGQVRBY2k0MG9pZEVlVnNBIiwgInVOSG9XWWhYc1poVkpDTkUyRHF5LXpxdDd0NjlnSkt5NVFhRnY3R3JNWDQiXX0sICJfc2RfYWxnIjogInNoYS0yNTYifQ.gR6rSL7urX79CNEvTQnP1MH5xthG11ucIV44SqKFZ4Pvlu_u16RfvXQd4k4CAIBZNKn2aTI18TfvFwV97gJFoA~WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ~WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgImNvdW50cnkiLCAiSlAiXQ~";
499
500 #[test]
501 fn parse() {
502 let sd_jwt = SdJwt::parse(SD_JWT).unwrap();
503 assert_eq!(sd_jwt.disclosures.len(), 2);
504 assert!(sd_jwt.key_binding_jwt.is_none());
505 }
506
507 #[test]
508 fn round_trip_ser_des() {
509 let sd_jwt = SdJwt::parse(SD_JWT).unwrap();
510 assert_eq!(&sd_jwt.to_string(), SD_JWT);
511 }
512}