ssi_vc/v1/jwt/
encode.rs

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}