sd_jwt_payload/
disclosure.rs1use crate::Error;
5use serde_json::json;
6use serde_json::Value;
7use std::fmt::Display;
8
9#[derive(Debug, Clone, Eq)]
14pub struct Disclosure {
15 pub salt: String,
17 pub claim_name: Option<String>,
19 pub claim_value: Value,
21 unparsed: String,
23}
24
25impl AsRef<str> for Disclosure {
26 fn as_ref(&self) -> &str {
27 &self.unparsed
28 }
29}
30
31impl Disclosure {
32 pub(crate) fn new(salt: String, claim_name: Option<String>, claim_value: Value) -> Self {
36 let string_encoded = {
37 let json_input = if let Some(name) = claim_name.as_deref() {
38 json!([salt, name, claim_value])
39 } else {
40 json!([salt, claim_value])
41 };
42
43 multibase::Base::Base64Url.encode(json_input.to_string())
44 };
45 Self {
46 salt,
47 claim_name,
48 claim_value,
49 unparsed: string_encoded,
50 }
51 }
52
53 pub fn parse(disclosure: &str) -> Result<Self, Error> {
59 let decoded: Vec<Value> = multibase::Base::Base64Url
60 .decode(disclosure)
61 .map_err(|_e| {
62 Error::InvalidDisclosure(format!(
63 "Base64 decoding of the disclosure was not possible {}",
64 disclosure
65 ))
66 })
67 .and_then(|data| {
68 serde_json::from_slice(&data).map_err(|_e| {
69 Error::InvalidDisclosure(format!(
70 "decoded disclosure could not be serialized as an array {}",
71 disclosure
72 ))
73 })
74 })?;
75
76 if decoded.len() == 2 {
77 Ok(Self {
78 salt: decoded
79 .first()
80 .ok_or(Error::InvalidDisclosure("invalid salt".to_string()))?
81 .as_str()
82 .ok_or(Error::InvalidDisclosure(
83 "salt could not be parsed as a string".to_string(),
84 ))?
85 .to_owned(),
86 claim_name: None,
87 claim_value: decoded
88 .get(1)
89 .ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
90 .clone(),
91 unparsed: disclosure.to_string(),
92 })
93 } else if decoded.len() == 3 {
94 Ok(Self {
95 salt: decoded
96 .first()
97 .ok_or(Error::InvalidDisclosure("invalid salt".to_string()))?
98 .as_str()
99 .ok_or(Error::InvalidDisclosure(
100 "salt could not be parsed as a string".to_string(),
101 ))?
102 .to_owned(),
103 claim_name: Some(
104 decoded
105 .get(1)
106 .ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
107 .as_str()
108 .ok_or(Error::InvalidDisclosure(
109 "claim name could not be parsed as a string".to_string(),
110 ))?
111 .to_owned(),
112 ),
113 claim_value: decoded
114 .get(2)
115 .ok_or(Error::InvalidDisclosure("invalid claim name".to_string()))?
116 .clone(),
117 unparsed: disclosure.to_string(),
118 })
119 } else {
120 Err(Error::InvalidDisclosure(format!(
121 "deserialized array has an invalid length of {}",
122 decoded.len()
123 )))
124 }
125 }
126
127 pub fn as_str(&self) -> &str {
128 self.as_ref()
129 }
130}
131
132impl Display for Disclosure {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.unparsed)
135 }
136}
137
138impl PartialEq for Disclosure {
139 fn eq(&self, other: &Self) -> bool {
140 self.claim_name == other.claim_name && self.salt == other.salt && self.claim_value == other.claim_value
141 }
142}
143
144#[cfg(test)]
145mod test {
146 use super::Disclosure;
147
148 #[test]
151 fn test_parsing() {
152 let disclosure = Disclosure::new(
153 "2GLC42sKQveCfGfryNRN9w".to_string(),
154 Some("time".to_owned()),
155 "2012-04-23T18:25Z".to_owned().into(),
156 );
157
158 let parsed =
159 Disclosure::parse("WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgInRpbWUiLCAiMjAxMi0wNC0yM1QxODoyNVoiXQ").unwrap();
160 assert_eq!(parsed, disclosure);
161 }
162}