jsonprooftoken/jpa/
bbs_plus.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};
16use zkryptium::{
17    bbsplus::{
18        keys::{BBSplusPublicKey, BBSplusSecretKey},
19        proof::BBSplusPoKSignature,
20        signature::BBSplusSignature,
21    },
22    schemes::{
23        algorithms::{BbsBls12381Sha256, BbsBls12381Shake256},
24        generics::{PoKSignature, Signature},
25    },
26};
27
28use crate::{
29    encoding::base64url_decode,
30    errors::CustomError,
31    jpt::payloads::Payloads,
32    jwk::{
33        alg_parameters::{Algorithm, JwkAlgorithmParameters},
34        key::Jwk,
35        utils::{check_alg_curve_compatibility, check_presentation_alg_curve_compatibility},
36    },
37};
38
39use super::algs::{PresentationProofAlgorithm, ProofAlgorithm};
40
41#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
42pub struct BBSplusAlgorithm {}
43
44impl BBSplusAlgorithm {
45    pub fn generate_issuer_proof(
46        alg: ProofAlgorithm,
47        payloads: &Payloads,
48        key: &Jwk,
49        issuer_header: &[u8],
50    ) -> Result<Vec<u8>, CustomError> {
51        let key_params = match &key.key_params {
52            JwkAlgorithmParameters::EllipticCurve(params) => {
53                if params.is_private() == false {
54                    return Err(CustomError::ProofGenerationError(
55                        "key is not compatible".to_string(),
56                    ));
57                }
58                params
59            }
60            _ => {
61                return Err(CustomError::ProofGenerationError(
62                    "key is not compatible".to_string(),
63                ))
64            }
65        };
66
67        if check_alg_curve_compatibility(Algorithm::Proof(alg.clone()), key_params.crv.clone())
68            == false
69        {
70            Err(CustomError::ProofGenerationError(
71                "key is not compatible".to_string(),
72            ))
73        } else {
74            let x: [u8; 96] = base64url_decode(&key_params.x)
75                .try_into()
76                .map_err(|_| CustomError::InvalidJwk)?;
77            let y: [u8; 96] = base64url_decode(&key_params.y)
78                .try_into()
79                .map_err(|_| CustomError::InvalidJwk)?;
80
81            let pk =
82                BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
83
84            let sk = BBSplusSecretKey::from_bytes(&base64url_decode(
85                key_params.d.as_ref().ok_or(CustomError::InvalidJwk)?,
86            ))
87            .map_err(|_| CustomError::SerializationError)?;
88
89            let proof = match alg {
90                ProofAlgorithm::BBS => Signature::<BbsBls12381Sha256>::sign(
91                    Some(&payloads.to_bytes()?),
92                    &sk,
93                    &pk,
94                    Some(issuer_header),
95                )
96                .map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
97                .to_bytes(),
98                ProofAlgorithm::BBS_SHAKE256 => Signature::<BbsBls12381Shake256>::sign(
99                    Some(&payloads.to_bytes()?),
100                    &sk,
101                    &pk,
102                    Some(issuer_header),
103                )
104                .map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
105                .to_bytes(),
106                _ => unreachable!(),
107            };
108
109            Ok(proof.to_vec())
110        }
111    }
112
113    pub fn verify_issuer_proof(
114        alg: ProofAlgorithm,
115        key: &Jwk,
116        proof: &[u8],
117        issuer_header: &[u8],
118        payloads: &Payloads,
119    ) -> Result<(), CustomError> {
120        let key_params = match &key.key_params {
121            JwkAlgorithmParameters::EllipticCurve(params) => {
122                if params.is_public() == false {
123                    return Err(CustomError::ProofGenerationError(
124                        "key is not compatible".to_string(),
125                    ));
126                }
127                params
128            }
129            _ => {
130                return Err(CustomError::ProofGenerationError(
131                    "key is not compatible".to_string(),
132                ))
133            }
134        };
135
136        if check_alg_curve_compatibility(Algorithm::Proof(alg.clone()), key_params.crv.clone())
137            == false
138        {
139            Err(CustomError::ProofGenerationError(
140                "key is not compatible".to_string(),
141            ))
142        } else {
143            let x: [u8; 96] = base64url_decode(&key_params.x)
144                .try_into()
145                .map_err(|_| CustomError::InvalidJwk)?;
146            let y: [u8; 96] = base64url_decode(&key_params.y)
147                .try_into()
148                .map_err(|_| CustomError::InvalidJwk)?;
149
150            let pk =
151                BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
152            let proof = BBSplusSignature::from_bytes(proof.try_into().map_err(|_| {
153                CustomError::ProofVerificationError("Proof is not valid".to_owned())
154            })?)
155            .map_err(|_| CustomError::SerializationError)?;
156            let check = match alg {
157                ProofAlgorithm::BBS => {
158                    let proof = Signature::<BbsBls12381Sha256>::BBSplus(proof);
159                    proof.verify(&pk, Some(&payloads.to_bytes()?), Some(issuer_header))
160                }
161                ProofAlgorithm::BBS_SHAKE256 => {
162                    let proof = Signature::<BbsBls12381Shake256>::BBSplus(proof);
163                    proof.verify(&pk, Some(&payloads.to_bytes()?), Some(issuer_header))
164                }
165                _ => unreachable!(),
166            };
167
168            check.map_err(|e| CustomError::ProofVerificationError(e.to_string()))
169        }
170    }
171
172    pub fn generate_presentation_proof(
173        alg: PresentationProofAlgorithm,
174        signature: &[u8],
175        payloads: &Payloads,
176        key: &Jwk,
177        issuer_header: &[u8],
178        presentation_header: &[u8],
179    ) -> Result<Vec<u8>, CustomError> {
180        let key_params = match &key.key_params {
181            JwkAlgorithmParameters::EllipticCurve(params) => {
182                if params.is_public() == false {
183                    return Err(CustomError::ProofGenerationError(
184                        "key is not compatible".to_string(),
185                    ));
186                }
187                params
188            }
189            _ => {
190                return Err(CustomError::ProofGenerationError(
191                    "key is not compatible".to_string(),
192                ))
193            }
194        };
195
196        if check_presentation_alg_curve_compatibility(alg, key_params.crv.clone()) == false {
197            Err(CustomError::ProofGenerationError(
198                "key is not compatible".to_string(),
199            ))
200        } else {
201            let x: [u8; 96] = base64url_decode(&key_params.x)
202                .try_into()
203                .map_err(|_| CustomError::InvalidJwk)?;
204            let y: [u8; 96] = base64url_decode(&key_params.y)
205                .try_into()
206                .map_err(|_| CustomError::InvalidJwk)?;
207
208            let pk =
209                BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
210            let revealed_message_indexes = payloads.get_disclosed_indexes();
211            let proof = match alg {
212                PresentationProofAlgorithm::BBS => {
213                    PoKSignature::<BbsBls12381Sha256>::proof_gen(
214                        &pk,
215                        &signature,
216                        Some(issuer_header),
217                        Some(presentation_header),
218                        Some(&payloads.to_bytes()?),
219                        Some(&revealed_message_indexes),
220                    )
221                    .map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
222                    .to_bytes()
223                }
224                PresentationProofAlgorithm::BBS_SHAKE256 => {
225                    PoKSignature::<BbsBls12381Shake256>::proof_gen(
226                        &pk,
227                        &signature,
228                        Some(issuer_header),
229                        Some(presentation_header),
230                        Some(&payloads.to_bytes()?),
231                        Some(&revealed_message_indexes),
232                    )
233                    .map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
234                    .to_bytes()
235                }
236                _ => unreachable!(),
237            };
238
239            Ok(proof.to_vec())
240        }
241    }
242
243    pub fn verify_presentation_proof(
244        alg: PresentationProofAlgorithm,
245        key: &Jwk,
246        proof: &[u8],
247        presentation_header: &[u8],
248        issuer_header: &[u8],
249        payloads: &Payloads,
250    ) -> Result<(), CustomError> {
251        let key_params = match &key.key_params {
252            JwkAlgorithmParameters::EllipticCurve(params) => {
253                if params.is_public() == false {
254                    return Err(CustomError::ProofGenerationError(
255                        "key is not compatible".to_string(),
256                    ));
257                }
258                params
259            }
260            _ => {
261                return Err(CustomError::ProofGenerationError(
262                    "key is not compatible".to_string(),
263                ))
264            }
265        };
266
267        if check_presentation_alg_curve_compatibility(alg, key_params.crv.clone()) == false {
268            Err(CustomError::ProofGenerationError(
269                "key is not compatible".to_string(),
270            ))
271        } else {
272            let x: [u8; 96] = base64url_decode(&key_params.x)
273                .try_into()
274                .map_err(|_| CustomError::InvalidJwk)?;
275            let y: [u8; 96] = base64url_decode(&key_params.y)
276                .try_into()
277                .map_err(|_| CustomError::InvalidJwk)?;
278
279            let pk =
280                BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
281            let disclosed_indexes = payloads.get_disclosed_indexes();
282            let proof = BBSplusPoKSignature::from_bytes(proof.try_into().map_err(|_| {
283                CustomError::ProofVerificationError("Proof is not valid".to_owned())
284            })?)
285            .map_err(|_| CustomError::InvalidJwk)?;
286            let check = match alg {
287                PresentationProofAlgorithm::BBS => {
288                    let proof = PoKSignature::<BbsBls12381Sha256>::BBSplus(proof);
289                    proof.proof_verify(
290                        &pk,
291                        Some(&payloads.get_disclosed_payloads().to_bytes()?),
292                        Some(&disclosed_indexes),
293                        Some(issuer_header),
294                        Some(presentation_header),
295                    )
296                }
297                PresentationProofAlgorithm::BBS_SHAKE256 => {
298                    let proof = PoKSignature::<BbsBls12381Shake256>::BBSplus(proof);
299                    proof.proof_verify(
300                        &pk,
301                        Some(&payloads.get_disclosed_payloads().to_bytes()?),
302                        Some(&disclosed_indexes),
303                        Some(issuer_header),
304                        Some(presentation_header),
305                    )
306                }
307                _ => unreachable!(),
308            };
309
310            check.map_err(|e| CustomError::ProofVerificationError(e.to_string()))
311        }
312    }
313}