1use serde::Serialize;
2use ssi_jwt::RegisteredClaims;
3
4#[derive(Debug, thiserror::Error)]
5pub enum JwtVcEncodeError {
6 #[error(transparent)]
7 Serialization(#[from] json_syntax::SerializeError),
8
9 #[error("expected JSON object")]
10 ExpectedJsonObject,
11
12 #[error("invalid date value")]
13 InvalidDateValue,
14
15 #[error(transparent)]
16 NumericDateConversion(#[from] ssi_jwt::NumericDateConversionError),
17
18 #[error("invalid issuer value")]
19 InvalidIssuerValue,
20
21 #[error("invalid subject value")]
22 InvalidSubjectValue,
23
24 #[error("invalid id value")]
25 InvalidIdValue,
26
27 #[error(transparent)]
28 InvalidUri(#[from] iref::InvalidUri<String>),
29}
30
31pub fn encode_jwt_vc_claims<T: Serialize>(
32 credential: &T,
33) -> Result<RegisteredClaims, JwtVcEncodeError> {
34 let mut credential = json_syntax::to_value(credential)?
35 .into_object()
36 .ok_or(JwtVcEncodeError::ExpectedJsonObject)?;
37 let mut claims = RegisteredClaims::default();
38
39 if let Some(date_value) =
40 take_object_property(&mut credential, "credentialSubject", "expirationDate")
41 {
42 match date_value.into_string() {
43 Some(date_value) => {
44 let date_value: xsd_types::DateTime = date_value
45 .parse()
46 .map_err(|_| JwtVcEncodeError::InvalidDateValue)?;
47 claims.set(ssi_jwt::ExpirationTime(date_value.earliest().try_into()?));
48 }
49 None => return Err(JwtVcEncodeError::InvalidDateValue),
50 }
51 }
52
53 if let Some(issuer_entry) = credential.remove("issuer").next() {
54 match issuer_entry.value.into_string() {
55 Some(issuer_value) => {
56 claims.set(ssi_jwt::Issuer(issuer_value.into_string().try_into()?));
57 }
58 None => return Err(JwtVcEncodeError::InvalidIssuerValue),
59 }
60 }
61
62 if let Some(issuance_date_entry) = credential.remove("issuanceDate").next() {
63 match issuance_date_entry.value.into_string() {
64 Some(issuance_date_value) => {
65 let issuance_date_value: xsd_types::DateTime = issuance_date_value
66 .parse()
67 .map_err(|_| JwtVcEncodeError::InvalidDateValue)?;
68 claims.set(ssi_jwt::NotBefore(issuance_date_value.latest().try_into()?));
69 }
70 None => return Err(JwtVcEncodeError::InvalidDateValue),
71 }
72 }
73
74 if let Some(subject_value) =
75 take_value_or_object_property(&mut credential, "credentialSubject", "id")
76 {
77 match subject_value.into_string() {
78 Some(subject_value) => {
79 claims.set(ssi_jwt::Subject(subject_value.into_string().try_into()?));
80 }
81 None => return Err(JwtVcEncodeError::InvalidSubjectValue),
82 }
83 }
84
85 if let Some(id_entry) = credential.remove("id").next() {
86 match id_entry.value.into_string() {
87 Some(id_value) => {
88 claims.set(ssi_jwt::JwtId(id_value.into_string()));
89 }
90 None => return Err(JwtVcEncodeError::InvalidIdValue),
91 }
92 }
93
94 claims.set(ssi_jwt::VerifiableCredential(json_syntax::Value::Object(
95 credential,
96 )));
97
98 Ok(claims)
99}
100
101#[derive(Debug, thiserror::Error)]
102pub enum JwtVpEncodeError {
103 #[error(transparent)]
104 Serialization(#[from] json_syntax::SerializeError),
105
106 #[error("expected JSON object")]
107 ExpectedJsonObject,
108
109 #[error("invalid date value")]
110 InvalidDateValue,
111
112 #[error(transparent)]
113 NumericDateConversion(#[from] ssi_jwt::NumericDateConversionError),
114
115 #[error("invalid holder value")]
116 InvalidHolderValue,
117
118 #[error("invalid id value")]
119 InvalidIdValue,
120
121 #[error(transparent)]
122 InvalidUri(#[from] iref::InvalidUri<String>),
123}
124
125pub fn encode_jwt_vp_claims<T: Serialize>(
126 presentation: &T,
127) -> Result<RegisteredClaims, JwtVpEncodeError> {
128 let mut vp = json_syntax::to_value(presentation)?
129 .into_object()
130 .ok_or(JwtVpEncodeError::ExpectedJsonObject)?;
131 let mut claims = RegisteredClaims::default();
132
133 if let Some(holder_entry) = vp.remove("holder").next() {
134 match holder_entry.value.into_string() {
135 Some(holder_value) => {
136 claims.set(ssi_jwt::Issuer(holder_value.into_string().try_into()?));
137 }
138 None => return Err(JwtVpEncodeError::InvalidHolderValue),
139 }
140 }
141
142 if let Some(issuance_date_entry) = vp.remove("issuanceDate").next() {
143 match issuance_date_entry.value.into_string() {
144 Some(issuance_date_value) => {
145 let issuance_date_value: xsd_types::DateTime = issuance_date_value
146 .parse()
147 .map_err(|_| JwtVpEncodeError::InvalidDateValue)?;
148 claims.set(ssi_jwt::NotBefore(issuance_date_value.latest().try_into()?));
149 }
150 None => return Err(JwtVpEncodeError::InvalidDateValue),
151 }
152 }
153
154 if let Some(id_entry) = vp.remove("id").next() {
155 match id_entry.value.into_string() {
156 Some(id_value) => {
157 claims.set(ssi_jwt::JwtId(id_value.into_string()));
158 }
159 None => return Err(JwtVpEncodeError::InvalidIdValue),
160 }
161 }
162
163 claims.set(ssi_jwt::VerifiablePresentation(json_syntax::Value::Object(
164 vp,
165 )));
166
167 Ok(claims)
168}
169
170fn take_object_property(
171 source: &mut json_syntax::Object,
172 object: &str,
173 prop: &str,
174) -> Option<json_syntax::Value> {
175 source
176 .get_mut(object)
177 .next()?
178 .as_object_mut()?
179 .remove(prop)
180 .next()
181 .map(json_syntax::object::Entry::into_value)
182}
183
184fn take_value_or_object_property(
185 source: &mut json_syntax::Object,
186 object: &str,
187 prop: &str,
188) -> Option<json_syntax::Value> {
189 let v = source.get_mut(object).next()?;
190
191 match v {
192 json_syntax::Value::Object(o) => o
193 .remove(prop)
194 .next()
195 .map(json_syntax::object::Entry::into_value),
196 _ => source
197 .remove(object)
198 .next()
199 .map(json_syntax::object::Entry::into_value),
200 }
201}