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::PresentationProofAlgorithm, bbs_plus::BBSplusAlgorithm},
23 jpt::{
24 claims::Claims,
25 payloads::{PayloadType, Payloads},
26 },
27 jwk::key::Jwk,
28};
29
30use super::{
31 header::{IssuerProtectedHeader, PresentationProtectedHeader},
32 issued::JwpIssued,
33};
34
35macro_rules! expect_four {
38 ($iter:expr) => {{
39 let mut i = $iter;
40 match (i.next(), i.next(), i.next(), i.next()) {
41 (Some(first), Some(second), Some(third), Some(fourth)) => {
42 (first, second, third, fourth)
43 }
44 _ => return Err(CustomError::InvalidPresentedJwp),
45 }
46 }};
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
51pub struct JwpPresentedBuilder {
52 issuer_protected_header: IssuerProtectedHeader,
53 presentation_protected_header: Option<PresentationProtectedHeader>,
54 payloads: Payloads,
55 issuer_proof: Vec<u8>,
56}
57
58impl JwpPresentedBuilder {
59 pub fn new(issued_jwp: &JwpIssued) -> Self {
60 Self {
61 issuer_protected_header: issued_jwp.get_issuer_protected_header().clone(),
62 presentation_protected_header: None,
63 payloads: issued_jwp.get_payloads().clone(),
64 issuer_proof: issued_jwp.get_proof().to_vec(),
65 }
66 }
67
68 pub fn set_presentation_protected_header(
69 &mut self,
70 header: PresentationProtectedHeader,
71 ) -> &mut Self {
72 self.presentation_protected_header = Some(header);
73 self
74 }
75
76 pub fn get_issuer_protected_header(&self) -> &IssuerProtectedHeader {
78 &self.issuer_protected_header
79 }
80
81 pub fn get_presentation_protected_header(&self) -> Option<&PresentationProtectedHeader> {
83 self.presentation_protected_header.as_ref()
84 }
85
86 pub fn get_payloads(&self) -> &Payloads {
88 &self.payloads
89 }
90
91 pub fn issuer_proof(&self) -> &Vec<u8> {
93 &self.issuer_proof
94 }
95
96 pub fn set_undisclosed(&mut self, claim: &str) -> Result<&mut Self, CustomError> {
97 let index = self
98 .issuer_protected_header
99 .claims()
100 .and_then(|c| c.0.iter().position(|x| x == claim))
101 .ok_or(CustomError::SelectiveDisclosureError)?;
102 self.payloads.set_undisclosed(index);
103 Ok(self)
104 }
105
106 pub fn build_with_proof(&self, proof: Vec<u8>) -> Result<JwpPresented, CustomError> {
107 if let Some(presentation_protected_header) = self.presentation_protected_header.clone() {
108 Ok(JwpPresented {
109 issuer_protected_header: self.issuer_protected_header.clone(),
110 presentation_protected_header,
111 payloads: self.payloads.clone(),
112 proof,
113 })
114 } else {
115 Err(CustomError::IncompleteJwpBuild(
116 crate::errors::IncompleteJwpBuild::NoIssuerHeader,
117 ))
118 }
119 }
120
121 pub fn build(&self, jwk: &Jwk) -> Result<JwpPresented, CustomError> {
122 if let Some(presentation_protected_header) = self.presentation_protected_header.clone() {
123 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header).unwrap();
124 let presentation_header_oct =
125 serde_json::to_vec(&self.presentation_protected_header).unwrap();
126
127 let proof = Self::generate_proof(
128 presentation_protected_header.alg(),
129 jwk,
130 &self.issuer_proof,
131 &issuer_header_oct,
132 &presentation_header_oct,
133 &self.payloads,
134 )?;
135 Ok(JwpPresented {
136 issuer_protected_header: self.issuer_protected_header.clone(),
137 presentation_protected_header,
138 payloads: self.payloads.clone(),
139 proof,
140 })
141 } else {
142 Err(CustomError::IncompleteJwpBuild(
143 crate::errors::IncompleteJwpBuild::NoIssuerHeader,
144 ))
145 }
146 }
147
148 fn generate_proof(
149 alg: PresentationProofAlgorithm,
150 key: &Jwk,
151 issuer_proof: &[u8],
152 issuer_header_oct: &[u8],
153 presentation_header_oct: &[u8],
154 payloads: &Payloads,
155 ) -> Result<Vec<u8>, CustomError> {
156 let proof = match alg {
157 PresentationProofAlgorithm::BLS12381_SHA256_PROOF
158 | PresentationProofAlgorithm::BLS12381_SHAKE256_PROOF => {
159 BBSplusAlgorithm::generate_presentation_proof(
160 alg,
161 issuer_proof,
162 payloads,
163 key,
164 issuer_header_oct,
165 presentation_header_oct,
166 )?
167 }
168 PresentationProofAlgorithm::SU_ES256 => todo!(),
169 PresentationProofAlgorithm::MAC_H256 => todo!(),
170 PresentationProofAlgorithm::MAC_H384 => todo!(),
171 PresentationProofAlgorithm::MAC_H512 => todo!(),
172 PresentationProofAlgorithm::MAC_K25519 => todo!(),
173 PresentationProofAlgorithm::MAC_K448 => todo!(),
174 PresentationProofAlgorithm::MAC_H256K => todo!(),
175 };
176
177 Ok(proof)
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
183pub struct JwpPresentedDecoder {
184 issuer_protected_header: IssuerProtectedHeader,
185 presentation_protected_header: PresentationProtectedHeader,
186 payloads: Payloads,
187 proof: Vec<u8>,
188}
189
190impl JwpPresentedDecoder {
191 pub fn decode(jpt: &str, serialization: SerializationType) -> Result<Self, CustomError> {
193 match serialization {
194 SerializationType::COMPACT => {
195 let (
196 encoded_issuer_protected_header,
197 encoded_presentation_protected_header,
198 encoded_payloads,
199 encoded_proof,
200 ) = expect_four!(jpt.splitn(4, '.'));
201 let presentation_protected_header: PresentationProtectedHeader =
202 serde_json::from_slice(&base64url_decode(
203 encoded_presentation_protected_header,
204 ))
205 .map_err(|_| CustomError::SerializationError)?;
206 let issuer_protected_header: IssuerProtectedHeader =
207 serde_json::from_slice(&base64url_decode(encoded_issuer_protected_header))
208 .map_err(|_| CustomError::SerializationError)?;
209 let payloads = Payloads(
210 encoded_payloads
211 .splitn(issuer_protected_header.claims().unwrap().0.len(), "~")
212 .map(|v| {
213 if v == "" {
214 (serde_json::Value::Null, PayloadType::Undisclosed)
215 } else {
216 (
217 serde_json::from_slice(&base64url_decode(v)).unwrap(),
218 PayloadType::Disclosed,
219 )
220 }
221 })
222 .collect(),
223 );
224
225 if !match issuer_protected_header.claims() {
226 Some(claims) => claims.0.len() == payloads.0.len(),
227 None => payloads.0.len() == 0,
228 } {
229 return Err(CustomError::InvalidIssuedJwp);
230 }
231
232 let proof = base64url_decode(encoded_proof);
233
234 Ok(Self {
235 issuer_protected_header,
236 payloads,
237 proof: proof,
238 presentation_protected_header,
239 })
240 }
241 SerializationType::JSON => todo!(),
242 }
243 }
244
245 pub fn verify(&self, key: &Jwk) -> Result<JwpPresented, CustomError> {
247 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header).unwrap();
248 let presentation_header_oct =
249 serde_json::to_vec(&self.presentation_protected_header).unwrap();
250 Self::verify_proof(
251 self.presentation_protected_header.alg(),
252 key,
253 &self.proof,
254 &presentation_header_oct,
255 &issuer_header_oct,
256 &self.payloads,
257 )?;
258 Ok(JwpPresented {
259 issuer_protected_header: self.issuer_protected_header.clone(),
260 presentation_protected_header: self.presentation_protected_header.clone(),
261 payloads: self.payloads.clone(),
262 proof: self.proof.clone(),
263 })
264 }
265
266 pub fn get_issuer_header(&self) -> &IssuerProtectedHeader {
267 &self.issuer_protected_header
268 }
269
270 pub fn get_presentation_header(&self) -> &PresentationProtectedHeader {
271 &self.presentation_protected_header
272 }
273
274 pub fn get_payloads(&self) -> &Payloads {
275 &self.payloads
276 }
277
278 fn verify_proof(
279 alg: PresentationProofAlgorithm,
280 key: &Jwk,
281 proof: &[u8],
282 presentation_header_oct: &[u8],
283 issuer_header_oct: &[u8],
284 payloads: &Payloads,
285 ) -> Result<(), CustomError> {
286 let check = match alg {
287 PresentationProofAlgorithm::BLS12381_SHA256_PROOF
288 | PresentationProofAlgorithm::BLS12381_SHAKE256_PROOF => {
289 BBSplusAlgorithm::verify_presentation_proof(
290 alg,
291 &key,
292 proof,
293 presentation_header_oct,
294 issuer_header_oct,
295 payloads,
296 )
297 }
298 PresentationProofAlgorithm::SU_ES256 => todo!(),
299 PresentationProofAlgorithm::MAC_H256 => todo!(),
300 PresentationProofAlgorithm::MAC_H384 => todo!(),
301 PresentationProofAlgorithm::MAC_H512 => todo!(),
302 PresentationProofAlgorithm::MAC_K25519 => todo!(),
303 PresentationProofAlgorithm::MAC_K448 => todo!(),
304 PresentationProofAlgorithm::MAC_H256K => todo!(),
305 };
306
307 check
308 }
309}
310
311#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
313pub struct JwpPresented {
314 issuer_protected_header: IssuerProtectedHeader,
315 presentation_protected_header: PresentationProtectedHeader,
316 payloads: Payloads,
317 proof: Vec<u8>,
318}
319
320impl JwpPresented {
321 pub fn encode(&self, serialization: SerializationType) -> Result<String, CustomError> {
323 let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header)
327 .map_err(|_| CustomError::SerializationError)?;
328
329 let presentation_header_oct = serde_json::to_vec(&self.presentation_protected_header)
330 .map_err(|_| CustomError::SerializationError)?;
331
332 let jwp = Self::serialize(
333 serialization,
334 &presentation_header_oct,
335 &issuer_header_oct,
336 &self.payloads,
337 &self.proof,
338 );
339
340 Ok(jwp)
341 }
342
343 pub fn get_issuer_protected_header(&self) -> &IssuerProtectedHeader {
344 &self.issuer_protected_header
345 }
346
347 pub fn get_presentation_protected_header(&self) -> &PresentationProtectedHeader {
348 &self.presentation_protected_header
349 }
350
351 pub fn get_claims(&self) -> Option<&Claims> {
352 self.issuer_protected_header.claims()
353 }
354
355 pub fn get_payloads(&self) -> &Payloads {
356 &self.payloads
357 }
358
359 pub fn get_proof(&self) -> &[u8] {
360 &self.proof
361 }
362
363 fn serialize(
364 serialization: SerializationType,
365 presentation_header_oct: &[u8],
366 issuer_header_oct: &[u8],
367 payloads: &Payloads,
368 proof: &[u8],
369 ) -> String {
370 let encoded_issuer_header = base64url_encode(issuer_header_oct);
371 let encoded_presentation_header = base64url_encode(presentation_header_oct);
372 let encoded_proof = base64url_encode(proof);
373
374 let jwp = match serialization {
375 SerializationType::COMPACT => {
376 let encoded_payloads = payloads
377 .0
378 .iter()
379 .map(|p| {
380 if p.1 == PayloadType::Undisclosed {
381 "".to_string()
382 } else {
383 base64url_encode_serializable(&p.0)
384 }
385 })
386 .collect::<Vec<String>>()
387 .join("~");
388 format!(
389 "{}.{}.{}.{}",
390 encoded_issuer_header,
391 encoded_presentation_header,
392 encoded_payloads,
393 encoded_proof
394 )
395 }
396 SerializationType::JSON => todo!(),
397 };
398
399 jwp
400 }
401}