use serde::{Deserialize, Serialize};
use zkryptium::{
bbsplus::{
keys::{BBSplusPublicKey, BBSplusSecretKey},
proof::BBSplusPoKSignature,
signature::BBSplusSignature,
},
schemes::{
algorithms::{BbsBls12381Sha256, BbsBls12381Shake256},
generics::{PoKSignature, Signature},
},
};
use crate::{
encoding::base64url_decode,
errors::CustomError,
jpt::payloads::Payloads,
jwk::{
alg_parameters::{Algorithm, JwkAlgorithmParameters},
key::Jwk,
utils::{check_alg_curve_compatibility, check_presentation_alg_curve_compatibility},
},
};
use super::algs::{PresentationProofAlgorithm, ProofAlgorithm};
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct BBSplusAlgorithm {}
impl BBSplusAlgorithm {
pub fn generate_issuer_proof(
alg: ProofAlgorithm,
payloads: &Payloads,
key: &Jwk,
issuer_header: &[u8],
) -> Result<Vec<u8>, CustomError> {
let key_params = match &key.key_params {
JwkAlgorithmParameters::EllipticCurve(params) => {
if params.is_private() == false {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
));
}
params
}
_ => {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
}
};
if check_alg_curve_compatibility(Algorithm::Proof(alg.clone()), key_params.crv.clone())
== false
{
Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
} else {
let x: [u8; 96] = base64url_decode(&key_params.x)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let y: [u8; 96] = base64url_decode(&key_params.y)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let pk =
BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
let sk = BBSplusSecretKey::from_bytes(&base64url_decode(
key_params.d.as_ref().ok_or(CustomError::InvalidJwk)?,
))
.map_err(|_| CustomError::SerializationError)?;
let proof = match alg {
ProofAlgorithm::BBS => Signature::<BbsBls12381Sha256>::sign(
Some(&payloads.to_bytes()?),
&sk,
&pk,
Some(issuer_header),
)
.map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
.to_bytes(),
ProofAlgorithm::BBS_SHAKE256 => Signature::<BbsBls12381Shake256>::sign(
Some(&payloads.to_bytes()?),
&sk,
&pk,
Some(issuer_header),
)
.map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
.to_bytes(),
_ => unreachable!(),
};
Ok(proof.to_vec())
}
}
pub fn verify_issuer_proof(
alg: ProofAlgorithm,
key: &Jwk,
proof: &[u8],
issuer_header: &[u8],
payloads: &Payloads,
) -> Result<(), CustomError> {
let key_params = match &key.key_params {
JwkAlgorithmParameters::EllipticCurve(params) => {
if params.is_public() == false {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
));
}
params
}
_ => {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
}
};
if check_alg_curve_compatibility(Algorithm::Proof(alg.clone()), key_params.crv.clone())
== false
{
Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
} else {
let x: [u8; 96] = base64url_decode(&key_params.x)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let y: [u8; 96] = base64url_decode(&key_params.y)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let pk =
BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
let proof = BBSplusSignature::from_bytes(proof.try_into().map_err(|_| {
CustomError::ProofVerificationError("Proof is not valid".to_owned())
})?)
.map_err(|_| CustomError::SerializationError)?;
let check = match alg {
ProofAlgorithm::BBS => {
let proof = Signature::<BbsBls12381Sha256>::BBSplus(proof);
proof.verify(&pk, Some(&payloads.to_bytes()?), Some(issuer_header))
}
ProofAlgorithm::BBS_SHAKE256 => {
let proof = Signature::<BbsBls12381Shake256>::BBSplus(proof);
proof.verify(&pk, Some(&payloads.to_bytes()?), Some(issuer_header))
}
_ => unreachable!(),
};
check.map_err(|e| CustomError::ProofVerificationError(e.to_string()))
}
}
pub fn generate_presentation_proof(
alg: PresentationProofAlgorithm,
signature: &[u8],
payloads: &Payloads,
key: &Jwk,
issuer_header: &[u8],
presentation_header: &[u8],
) -> Result<Vec<u8>, CustomError> {
let key_params = match &key.key_params {
JwkAlgorithmParameters::EllipticCurve(params) => {
if params.is_public() == false {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
));
}
params
}
_ => {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
}
};
if check_presentation_alg_curve_compatibility(alg, key_params.crv.clone()) == false {
Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
} else {
let x: [u8; 96] = base64url_decode(&key_params.x)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let y: [u8; 96] = base64url_decode(&key_params.y)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let pk =
BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
let revealed_message_indexes = payloads.get_disclosed_indexes();
let proof = match alg {
PresentationProofAlgorithm::BBS => {
PoKSignature::<BbsBls12381Sha256>::proof_gen(
&pk,
&signature,
Some(issuer_header),
Some(presentation_header),
Some(&payloads.to_bytes()?),
Some(&revealed_message_indexes),
)
.map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
.to_bytes()
}
PresentationProofAlgorithm::BBS_SHAKE256 => {
PoKSignature::<BbsBls12381Shake256>::proof_gen(
&pk,
&signature,
Some(issuer_header),
Some(presentation_header),
Some(&payloads.to_bytes()?),
Some(&revealed_message_indexes),
)
.map_err(|e| CustomError::ProofGenerationError(e.to_string()))?
.to_bytes()
}
_ => unreachable!(),
};
Ok(proof.to_vec())
}
}
pub fn verify_presentation_proof(
alg: PresentationProofAlgorithm,
key: &Jwk,
proof: &[u8],
presentation_header: &[u8],
issuer_header: &[u8],
payloads: &Payloads,
) -> Result<(), CustomError> {
let key_params = match &key.key_params {
JwkAlgorithmParameters::EllipticCurve(params) => {
if params.is_public() == false {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
));
}
params
}
_ => {
return Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
}
};
if check_presentation_alg_curve_compatibility(alg, key_params.crv.clone()) == false {
Err(CustomError::ProofGenerationError(
"key is not compatible".to_string(),
))
} else {
let x: [u8; 96] = base64url_decode(&key_params.x)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let y: [u8; 96] = base64url_decode(&key_params.y)
.try_into()
.map_err(|_| CustomError::InvalidJwk)?;
let pk =
BBSplusPublicKey::from_coordinates(&x, &y).map_err(|_| CustomError::InvalidJwk)?;
let disclosed_indexes = payloads.get_disclosed_indexes();
let proof = BBSplusPoKSignature::from_bytes(proof.try_into().map_err(|_| {
CustomError::ProofVerificationError("Proof is not valid".to_owned())
})?)
.map_err(|_| CustomError::InvalidJwk)?;
let check = match alg {
PresentationProofAlgorithm::BBS => {
let proof = PoKSignature::<BbsBls12381Sha256>::BBSplus(proof);
proof.proof_verify(
&pk,
Some(&payloads.get_disclosed_payloads().to_bytes()?),
Some(&disclosed_indexes),
Some(issuer_header),
Some(presentation_header),
)
}
PresentationProofAlgorithm::BBS_SHAKE256 => {
let proof = PoKSignature::<BbsBls12381Shake256>::BBSplus(proof);
proof.proof_verify(
&pk,
Some(&payloads.get_disclosed_payloads().to_bytes()?),
Some(&disclosed_indexes),
Some(issuer_header),
Some(presentation_header),
)
}
_ => unreachable!(),
};
check.map_err(|e| CustomError::ProofVerificationError(e.to_string()))
}
}
}