1use serde::{Deserialize, Serialize};
16
17use crate::{
18 encoding::{
19 base64url_decode, base64url_encode, base64url_encode_serializable, SerializationType,
20 },
21 errors::CustomError,
22 jpa::{algs::ProofAlgorithm, bbs_plus::BBSplusAlgorithm},
23 jpt::{
24 claims::{Claims, JptClaims},
25 payloads::{PayloadType, Payloads},
26 },
27 jwk::key::Jwk,
28};
29
30use super::header::IssuerProtectedHeader;
31
32macro_rules! expect_three {
35 ($iter:expr) => {{
36 let mut i = $iter;
37 match (i.next(), i.next(), i.next()) {
38 (Some(first), Some(second), Some(third)) => (first, second, third),
39 _ => return Err(CustomError::InvalidIssuedJwp),
40 }
41 }};
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
46pub struct JwpIssuedBuilder {
47 issuer_protected_header: Option<IssuerProtectedHeader>,
48 payloads: Option<Payloads>,
49}
50
51impl JwpIssuedBuilder {
52 pub fn new(issuer_protected_header: IssuerProtectedHeader, jpt_claims: JptClaims) -> Self {
53 let (claims, payloads) = jpt_claims.get_claims_and_payloads();
54 let mut issuer_protected_header = issuer_protected_header;
56 issuer_protected_header.set_claims(Some(claims));
57
58 Self {
59 issuer_protected_header: Some(issuer_protected_header),
60 payloads: Some(payloads),
61 }
62 }
63
64 pub fn get_issuer_protected_header(&self) -> Option<&IssuerProtectedHeader> {
65 self.issuer_protected_header.as_ref()
66 }
67
68 pub fn get_payloads(&self) -> Option<&Payloads> {
69 self.payloads.as_ref()
70 }
71
72 pub fn build_with_proof(&self, proof: Vec<u8>) -> Result<JwpIssued, CustomError> {
73 if let Some(issuer_protected_header) = self.issuer_protected_header.clone() {
74 if let Some(payloads) = self.payloads.clone() {
75 Ok(JwpIssued {
76 issuer_protected_header,
77 payloads,
78 proof,
79 })
80 } else {
81 Err(CustomError::IncompleteJwpBuild(
82 crate::errors::IncompleteJwpBuild::NoClaimsAndPayloads,
83 ))
84 }
85 } else {
86 Err(CustomError::IncompleteJwpBuild(
87 crate::errors::IncompleteJwpBuild::NoIssuerHeader,
88 ))
89 }
90 }
91
92 pub fn build(&self, jwk: &Jwk) -> Result<JwpIssued, CustomError> {
93 if let Some(issuer_protected_header) = self.issuer_protected_header.clone() {
94 if let Some(payloads) = self.payloads.clone() {
95 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header).unwrap();
96 let proof = Self::generate_proof(
97 issuer_protected_header.alg(),
98 &jwk,
99 &issuer_header_oct,
100 &payloads,
101 )?;
102
103 Ok(JwpIssued {
104 issuer_protected_header,
105 payloads,
106 proof,
107 })
108 } else {
109 Err(CustomError::IncompleteJwpBuild(
110 crate::errors::IncompleteJwpBuild::NoClaimsAndPayloads,
111 ))
112 }
113 } else {
114 Err(CustomError::IncompleteJwpBuild(
115 crate::errors::IncompleteJwpBuild::NoIssuerHeader,
116 ))
117 }
118 }
119
120 fn generate_proof(
121 alg: ProofAlgorithm,
122 key: &Jwk,
123 issuer_header_oct: &[u8],
124 payloads: &Payloads,
125 ) -> Result<Vec<u8>, CustomError> {
126 let proof = match alg {
127 ProofAlgorithm::BBS | ProofAlgorithm::BBS_SHAKE256 => {
128 BBSplusAlgorithm::generate_issuer_proof(alg, payloads, key, issuer_header_oct)?
129 }
130 ProofAlgorithm::SU_ES256 => todo!(),
131 ProofAlgorithm::SU_ES384 => todo!(),
132 ProofAlgorithm::SU_ES512 => todo!(),
133 ProofAlgorithm::MAC_H256 => todo!(),
134 ProofAlgorithm::MAC_H384 => todo!(),
135 ProofAlgorithm::MAC_H512 => todo!(),
136 ProofAlgorithm::MAC_K25519 => todo!(),
137 ProofAlgorithm::MAC_K448 => todo!(),
138 ProofAlgorithm::MAC_H256K => todo!(),
139 };
140
141 Ok(proof)
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
147pub struct JwpIssuedDecoder {
148 issuer_protected_header: IssuerProtectedHeader,
149 payloads: Payloads,
150 proof: Vec<u8>,
151}
152
153impl JwpIssuedDecoder {
154 pub fn decode(jpt: &str, serialization: SerializationType) -> Result<Self, CustomError> {
156 match serialization {
157 SerializationType::COMPACT => {
158 let (encoded_issuer_protected_header, encoded_payloads, encoded_proof) =
159 expect_three!(jpt.splitn(3, '.'));
160 let issuer_protected_header: IssuerProtectedHeader =
161 serde_json::from_slice(&base64url_decode(encoded_issuer_protected_header))
162 .map_err(|_| CustomError::SerializationError)?;
163 let payloads = Payloads(
165 encoded_payloads
166 .splitn(issuer_protected_header.claims().unwrap().0.len(), "~")
167 .map(|v| {
168 if v == "" {
169 (serde_json::Value::Null, PayloadType::Undisclosed)
170 } else {
171 (
172 serde_json::from_slice(&base64url_decode(v)).unwrap(),
173 PayloadType::Disclosed,
174 )
175 }
176 })
177 .collect(),
178 );
179
180 if !match issuer_protected_header.claims() {
181 Some(claims) => claims.0.len() == payloads.0.len(),
182 None => payloads.0.len() == 0,
183 } {
184 return Err(CustomError::InvalidIssuedJwp);
185 }
186
187 let proof = base64url_decode(encoded_proof);
188 Ok(Self {
189 issuer_protected_header,
190 payloads,
191 proof: proof,
192 })
193 }
194 SerializationType::JSON => todo!(),
195 SerializationType::CBOR => todo!(),
196 }
197 }
198
199 pub fn verify(&self, key: &Jwk) -> Result<JwpIssued, CustomError> {
201 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header).unwrap();
202
203 Self::verify_proof(
204 self.issuer_protected_header.alg(),
205 key,
206 &self.proof,
207 &issuer_header_oct,
208 &self.payloads,
209 )?;
210
211 Ok(JwpIssued {
212 issuer_protected_header: self.issuer_protected_header.clone(),
213 payloads: self.payloads.clone(),
214 proof: self.proof.clone(),
215 })
216 }
217
218 pub fn get_header(&self) -> &IssuerProtectedHeader {
219 &self.issuer_protected_header
220 }
221
222 pub fn get_payloads(&self) -> &Payloads {
223 &self.payloads
224 }
225
226 fn verify_proof(
227 alg: ProofAlgorithm,
228 key: &Jwk,
229 proof: &[u8],
230 issuer_header_oct: &[u8],
231 payloads: &Payloads,
232 ) -> Result<(), CustomError> {
233 let check = match alg {
234 ProofAlgorithm::BBS | ProofAlgorithm::BBS_SHAKE256 => {
235 BBSplusAlgorithm::verify_issuer_proof(alg, &key, proof, issuer_header_oct, payloads)
236 }
237 ProofAlgorithm::SU_ES256 => todo!(),
238 ProofAlgorithm::SU_ES384 => todo!(),
239 ProofAlgorithm::SU_ES512 => todo!(),
240 ProofAlgorithm::MAC_H256 => todo!(),
241 ProofAlgorithm::MAC_H384 => todo!(),
242 ProofAlgorithm::MAC_H512 => todo!(),
243 ProofAlgorithm::MAC_K25519 => todo!(),
244 ProofAlgorithm::MAC_K448 => todo!(),
245 ProofAlgorithm::MAC_H256K => todo!(),
246 };
247
248 check
249 }
250}
251
252#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
254pub struct JwpIssued {
255 issuer_protected_header: IssuerProtectedHeader,
256 payloads: Payloads,
257 proof: Vec<u8>,
258}
259
260impl JwpIssued {
261 pub fn encode(&self, serialization: SerializationType) -> Result<String, CustomError> {
262 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header)
265 .map_err(|_| CustomError::SerializationError)?;
266
267 let jwp = Self::serialize(
268 serialization,
269 &issuer_header_oct,
270 &self.payloads,
271 &self.proof,
272 );
273
274 Ok(jwp)
275 }
276
277 pub fn get_issuer_protected_header(&self) -> &IssuerProtectedHeader {
278 &self.issuer_protected_header
279 }
280
281 pub fn get_claims(&self) -> Option<&Claims> {
282 self.issuer_protected_header.claims()
283 }
284
285 pub fn set_claims(&mut self, claims: Claims) {
286 self.issuer_protected_header.set_claims(Some(claims));
287 }
288
289 pub fn get_payloads(&self) -> &Payloads {
290 &self.payloads
291 }
292
293 pub fn set_payloads(&mut self, payloads: Payloads) {
294 self.payloads = payloads;
295 }
296
297 pub fn get_proof(&self) -> &[u8] {
298 self.proof.as_ref()
299 }
300
301 pub fn set_proof(&mut self, proof: &[u8]) {
302 self.proof = proof.to_vec();
303 }
304
305 fn serialize(
306 serialization: SerializationType,
307 issuer_header_oct: &[u8],
308 payloads: &Payloads,
309 proof: &[u8],
310 ) -> String {
311 let encoded_issuer_header = base64url_encode(issuer_header_oct);
312 let encoded_proof = base64url_encode(proof);
313 let jwp = match serialization {
314 SerializationType::COMPACT => {
315 let encoded_payloads = payloads
316 .0
317 .iter()
318 .map(|p| {
319 if p.1 == PayloadType::Undisclosed {
320 "".to_string()
321 } else {
322 base64url_encode_serializable(&p.0)
323 }
324 })
325 .collect::<Vec<String>>()
326 .join("~");
327
328 format!(
329 "{}.{}.{}",
330 encoded_issuer_header, encoded_payloads, encoded_proof
331 )
332 }
333 SerializationType::JSON => todo!(),
334 SerializationType::CBOR => todo!(),
335 };
336
337 jwp
338 }
339}
340
341
342#[cfg(test)]
343mod tests {
344 use crate::encoding::SerializationType;
345 use crate::jpt::claims::JptClaims;
346 use crate::jpa::algs::ProofAlgorithm;
347 use crate::jwk::key::Jwk;
348 use crate::jwk::types::KeyPairSubtype;
349 use crate::jwp::header::IssuerProtectedHeader;
350 use crate::jwp::issued::{JwpIssuedBuilder, JwpIssuedDecoder};
351
352 #[test]
353 fn test_jwp_issued() {
354 let custom_claims = serde_json::json!({
355 "degree": {
356 "type": "BachelorDegree",
357 "name": "Bachelor of Science and Arts",
358 },
359 "name": "John Doe"
360 });
361
362 let mut jpt_claims = JptClaims::new();
363 jpt_claims.set_iss("https://issuer.example".to_owned());
364 jpt_claims.set_claim(Some("vc"), custom_claims, true);
365
366 let issued_header = IssuerProtectedHeader::new(ProofAlgorithm::BBS);
367
368 let bbs_jwk = Jwk::generate(KeyPairSubtype::BLS12381G2Sha256).unwrap();
369
370 let issued_jwp = JwpIssuedBuilder::new(issued_header, jpt_claims)
371 .build(&bbs_jwk)
372 .unwrap();
373
374 let compact_issued_jwp = issued_jwp.encode(SerializationType::COMPACT).unwrap();
375
376 let decoded_issued_jwp =
377 JwpIssuedDecoder::decode(&compact_issued_jwp, SerializationType::COMPACT)
378 .unwrap()
379 .verify(&bbs_jwk.to_public().unwrap())
380 .unwrap();
381
382 assert_eq!(issued_jwp, decoded_issued_jwp);
383
384 }
385
386}