jsonprooftoken/jwp/
issued.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::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
32/// Takes the result of a rsplit and ensure we only get 3 parts (JwpIssued)
33/// Errors if we don't
34macro_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/// Used to build a new JSON Web Proof in the Issuer form
45#[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        //Set claims
55        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::BLS12381_SHA256 | ProofAlgorithm::BLS12381_SHAKE256 => {
128                BBSplusAlgorithm::generate_issuer_proof(alg, payloads, key, issuer_header_oct)?
129            }
130            ProofAlgorithm::SU_ES256 => todo!(),
131            ProofAlgorithm::MAC_H256 => todo!(),
132            ProofAlgorithm::MAC_H384 => todo!(),
133            ProofAlgorithm::MAC_H512 => todo!(),
134            ProofAlgorithm::MAC_K25519 => todo!(),
135            ProofAlgorithm::MAC_K448 => todo!(),
136            ProofAlgorithm::MAC_H256K => todo!(),
137        };
138
139        Ok(proof)
140    }
141}
142
143/// Used for both decoding and verifing a JSON Proof Token representing a JWP in the Issuer form
144#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
145pub struct JwpIssuedDecoder {
146    issuer_protected_header: IssuerProtectedHeader,
147    payloads: Payloads,
148    proof: Vec<u8>,
149}
150
151impl JwpIssuedDecoder {
152    /// Decode a JSON Proof Token. The token must represent an Issued JWP, otherwise will return an error.
153    pub fn decode(jpt: &str, serialization: SerializationType) -> Result<Self, CustomError> {
154        match serialization {
155            SerializationType::COMPACT => {
156                let (encoded_issuer_protected_header, encoded_payloads, encoded_proof) =
157                    expect_three!(jpt.splitn(3, '.'));
158                let issuer_protected_header: IssuerProtectedHeader =
159                    serde_json::from_slice(&base64url_decode(encoded_issuer_protected_header))
160                        .map_err(|_| CustomError::SerializationError)?;
161                //TODO: this could not have much sense for now (maybe useful to handle blind signatures?)
162                let payloads = Payloads(
163                    encoded_payloads
164                        .splitn(issuer_protected_header.claims().unwrap().0.len(), "~")
165                        .map(|v| {
166                            if v == "" {
167                                (serde_json::Value::Null, PayloadType::Undisclosed)
168                            } else {
169                                (
170                                    serde_json::from_slice(&base64url_decode(v)).unwrap(),
171                                    PayloadType::Disclosed,
172                                )
173                            }
174                        })
175                        .collect(),
176                );
177
178                if !match issuer_protected_header.claims() {
179                    Some(claims) => claims.0.len() == payloads.0.len(),
180                    None => payloads.0.len() == 0,
181                } {
182                    return Err(CustomError::InvalidIssuedJwp);
183                }
184
185                let proof = base64url_decode(encoded_proof);
186                Ok(Self {
187                    issuer_protected_header,
188                    payloads,
189                    proof: proof,
190                })
191            }
192            SerializationType::JSON => todo!(),
193        }
194    }
195
196    /// Verify the decoded JWP
197    pub fn verify(&self, key: &Jwk) -> Result<JwpIssued, CustomError> {
198        let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header).unwrap();
199
200        Self::verify_proof(
201            self.issuer_protected_header.alg(),
202            key,
203            &self.proof,
204            &issuer_header_oct,
205            &self.payloads,
206        )?;
207
208        Ok(JwpIssued {
209            issuer_protected_header: self.issuer_protected_header.clone(),
210            payloads: self.payloads.clone(),
211            proof: self.proof.clone(),
212        })
213    }
214
215    pub fn get_header(&self) -> &IssuerProtectedHeader {
216        &self.issuer_protected_header
217    }
218
219    pub fn get_payloads(&self) -> &Payloads {
220        &self.payloads
221    }
222
223    fn verify_proof(
224        alg: ProofAlgorithm,
225        key: &Jwk,
226        proof: &[u8],
227        issuer_header_oct: &[u8],
228        payloads: &Payloads,
229    ) -> Result<(), CustomError> {
230        let check = match alg {
231            ProofAlgorithm::BLS12381_SHA256 | ProofAlgorithm::BLS12381_SHAKE256 => {
232                BBSplusAlgorithm::verify_issuer_proof(alg, &key, proof, issuer_header_oct, payloads)
233            }
234            ProofAlgorithm::SU_ES256 => todo!(),
235            ProofAlgorithm::MAC_H256 => todo!(),
236            ProofAlgorithm::MAC_H384 => todo!(),
237            ProofAlgorithm::MAC_H512 => todo!(),
238            ProofAlgorithm::MAC_K25519 => todo!(),
239            ProofAlgorithm::MAC_K448 => todo!(),
240            ProofAlgorithm::MAC_H256K => todo!(),
241        };
242
243        check
244    }
245}
246
247/// Decoded and verified JSON Web Proof in the Issuer form
248#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
249pub struct JwpIssued {
250    issuer_protected_header: IssuerProtectedHeader,
251    payloads: Payloads,
252    proof: Vec<u8>,
253}
254
255impl JwpIssued {
256    pub fn encode(&self, serialization: SerializationType) -> Result<String, CustomError> {
257        // let encoded_issuer_header = base64url_encode_serializable(&self.issuer_protected_header);
258
259        let issuer_header_oct = serde_json::to_vec(&self.issuer_protected_header)
260            .map_err(|_| CustomError::SerializationError)?;
261
262        let jwp = Self::serialize(
263            serialization,
264            &issuer_header_oct,
265            &self.payloads,
266            &self.proof,
267        );
268
269        Ok(jwp)
270    }
271
272    pub fn get_issuer_protected_header(&self) -> &IssuerProtectedHeader {
273        &self.issuer_protected_header
274    }
275
276    pub fn get_claims(&self) -> Option<&Claims> {
277        self.issuer_protected_header.claims()
278    }
279
280    pub fn set_claims(&mut self, claims: Claims) {
281        self.issuer_protected_header.set_claims(Some(claims));
282    }
283
284    pub fn get_payloads(&self) -> &Payloads {
285        &self.payloads
286    }
287
288    pub fn set_payloads(&mut self, payloads: Payloads) {
289        self.payloads = payloads;
290    }
291
292    pub fn get_proof(&self) -> &[u8] {
293        self.proof.as_ref()
294    }
295
296    pub fn set_proof(&mut self, proof: &[u8]) {
297        self.proof = proof.to_vec();
298    }
299
300    fn serialize(
301        serialization: SerializationType,
302        issuer_header_oct: &[u8],
303        payloads: &Payloads,
304        proof: &[u8],
305    ) -> String {
306        let encoded_issuer_header = base64url_encode(issuer_header_oct);
307        let encoded_proof = base64url_encode(proof);
308        let jwp = match serialization {
309            SerializationType::COMPACT => {
310                let encoded_payloads = payloads
311                    .0
312                    .iter()
313                    .map(|p| {
314                        if p.1 == PayloadType::Undisclosed {
315                            "".to_string()
316                        } else {
317                            base64url_encode_serializable(&p.0)
318                        }
319                    })
320                    .collect::<Vec<String>>()
321                    .join("~");
322
323                format!(
324                    "{}.{}.{}",
325                    encoded_issuer_header, encoded_payloads, encoded_proof
326                )
327            }
328            SerializationType::JSON => todo!(),
329        };
330
331        jwp
332    }
333}