jsonprooftoken/jwp/
presented.rs

1// Copyright 2023 Fondazione LINKS
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
35/// Takes the result of a rsplit and ensure we only get 4 parts (JwpPresented)
36/// Errors if we don't
37macro_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/// Used to build a new JSON Web Proof in the Presentation form from an verified Issued JWP
50#[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    // Getter for issuer_protected_header
77    pub fn get_issuer_protected_header(&self) -> &IssuerProtectedHeader {
78        &self.issuer_protected_header
79    }
80
81    // Getter for presentation_protected_header
82    pub fn get_presentation_protected_header(&self) -> Option<&PresentationProtectedHeader> {
83        self.presentation_protected_header.as_ref()
84    }
85
86    // Getter for payloads
87    pub fn get_payloads(&self) -> &Payloads {
88        &self.payloads
89    }
90
91    // Getter for issuer_proof
92    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/// Used for both decoding and verifing a JSON Proof Token representing a JWP in the Presentation form
182#[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    /// Decode a JSON Proof Token. The token must represent a Presented JWP, otherwise will return an error.
192    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    /// Verify the decoded JWP
246    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/// Decoded and verified JSON Web Proof in the Presentation form
312#[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    /// Encode the currently crafted JWP
322    pub fn encode(&self, serialization: SerializationType) -> Result<String, CustomError> {
323        // let encoded_issuer_header = base64url_encode_serializable(&self.issuer_protected_header);
324        // let encoded_presentation_header = base64url_encode_serializable(&self.presentation_protected_header);
325
326        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}