#![allow(clippy::needless_range_loop)]
extern crate alloc;
use alloc::vec::Vec;
wit_bindgen::generate!({
path: "wit",
world: "aethel-core",
});
use exports::aethel::core::identity::{
Credential as WitCredential, DisclosureAttributes, EphemeralProjection as WitProjection,
Guest as IdentityGuest, GuestCredential, GuestMasterIdentity,
MasterIdentity as WitMasterIdentity, MasterIdentityBorrow,
SaapPresentation as WitSaapPresentation, ZkIdentityProof as WitZkProof,
};
use exports::aethel::core::secret_sharing::{Guest as SecretSharingGuest, HtssShare as WitShare};
use aethel::core::types::IdentityError as WitError;
use crate::identity_error::IdentityError;
use crate::{credential, htss, plp, signing};
const COMPONENT_SPLIT_NONCE: &[u8] = b"AETHEL_COMPONENT_HTSS_V1";
struct Component;
impl From<IdentityError> for WitError {
fn from(e: IdentityError) -> Self {
match e {
IdentityError::InvalidInputLength => WitError::InvalidInputLength,
IdentityError::SerializationError => WitError::SerializationError,
IdentityError::RejectionSamplingFailed => WitError::RejectionSamplingFailed,
IdentityError::NormBoundViolation => WitError::NormBoundViolation,
IdentityError::ChallengeMismatch => WitError::ChallengeMismatch,
IdentityError::InvalidAttributeCommitment => WitError::InvalidAttributeCommitment,
IdentityError::ThresholdNotMet => WitError::ThresholdNotMet,
}
}
}
fn secret_as_seed(secret: &[u8]) -> Result<[u8; 32], WitError> {
if secret.len() != 32 {
return Err(WitError::InvalidInputLength);
}
let mut seed = [0u8; 32];
seed.copy_from_slice(secret);
Ok(seed)
}
impl IdentityGuest for Component {
fn plp_project_at_context(
secret: Vec<u8>,
tau: Vec<u8>,
randomness: Vec<u8>,
) -> Result<WitProjection, WitError> {
let seed = secret_as_seed(&secret)?;
if randomness.len() < 32 {
return Err(WitError::InvalidInputLength);
}
let identity = plp::MasterIdentity::from_seed(&seed);
let proj = identity.project_at_context(&tau, &randomness);
Ok(WitProjection {
tau: proj.tau.to_vec(),
matrix_a: proj.matrix_a.coeffs().to_vec(),
public_b: proj.public_b.coeffs().to_vec(),
})
}
fn plp_prove_identity(secret: Vec<u8>, tau: Vec<u8>) -> Result<WitZkProof, WitError> {
let seed = secret_as_seed(&secret)?;
let identity = plp::MasterIdentity::from_seed(&seed);
let proj = plp::EphemeralProjection::for_proving(&tau);
let proof = plp::Prover::prove_identity(&identity, &proj, &seed);
Ok(WitZkProof {
commitment_w: proof.commitment_w.coeffs().to_vec(),
challenge_c: proof.challenge_c.coeffs().to_vec(),
response_z: proof.response_z.coeffs().to_vec(),
})
}
fn plp_verify(
projection: WitProjection,
proof: WitZkProof,
) -> Result<bool, WitError> {
let proj = projection_from_wit(&projection)?;
let zk = zk_proof_from_wit(&proof)?;
Ok(plp::Verifier::verify(&proj, &zk))
}
type MasterIdentity = OwnedIdentity;
type Credential = OwnedCredential;
fn saap_verify_presentation(
issuer_seed: Vec<u8>,
presentation: WitSaapPresentation,
projection: WitProjection,
tau: Vec<u8>,
) -> Result<bool, WitError> {
if presentation.tau != tau {
return Ok(false);
}
let proj = projection_from_wit(&projection)?;
let t_blind = credential::unflatten::<{ credential::CRED_T }>(&presentation.t_blind)?;
if presentation.disclosed_values.len() != credential::CRED_ATTRIBUTES {
return Err(WitError::InvalidInputLength);
}
let mut disclosed_values = [0u64; credential::CRED_ATTRIBUTES];
disclosed_values.copy_from_slice(&presentation.disclosed_values);
let mut tau_padded = [0u8; 32];
let len = tau.len().min(32);
tau_padded[..len].copy_from_slice(&tau[..len]);
let native = credential::SaapPresentation {
tau: tau_padded,
disclosed: presentation.disclosed.bits(),
disclosed_values,
challenge: credential::unflatten::<1>(&presentation.challenge)?[0],
z_r: credential::unflatten::<{ credential::CRED_L }>(&presentation.z_r)?,
z_m: credential::unflatten::<{ credential::CRED_SLOTS }>(&presentation.z_m)?,
z_s: credential::unflatten::<1>(&presentation.z_s)?[0],
z_e: credential::unflatten::<1>(&presentation.z_e)?[0],
};
let params = credential::IssuerParams::from_seed(&issuer_seed);
credential::verify(¶ms, &native, &t_blind, &proj, &tau).map_err(Into::into)
}
fn verify_signature(
public_key: Vec<u8>,
message: Vec<u8>,
signature: Vec<u8>,
) -> Result<bool, WitError> {
signing::verify(&public_key, &message, &signature).map_err(Into::into)
}
}
pub struct OwnedIdentity(signing::Identity);
impl GuestMasterIdentity for OwnedIdentity {
fn generate(entropy: Vec<u8>) -> Result<WitMasterIdentity, WitError> {
let identity = signing::Identity::generate(&entropy)?;
Ok(WitMasterIdentity::new(OwnedIdentity(identity)))
}
fn public_key(&self) -> Vec<u8> {
self.0.public_key()
}
fn sign(&self, message: Vec<u8>) -> Result<Vec<u8>, WitError> {
self.0.sign(&message).map_err(Into::into)
}
fn project_at_context(
&self,
tau: Vec<u8>,
randomness: Vec<u8>,
) -> Result<WitProjection, WitError> {
if randomness.len() < 32 {
return Err(WitError::InvalidInputLength);
}
let identity = plp::MasterIdentity::from_seed(self.0.plp_seed());
let proj = identity.project_at_context(&tau, &randomness);
Ok(WitProjection {
tau: proj.tau.to_vec(),
matrix_a: proj.matrix_a.coeffs().to_vec(),
public_b: proj.public_b.coeffs().to_vec(),
})
}
fn export_sealed(&self, key: Vec<u8>) -> Result<Vec<u8>, WitError> {
self.0.export_sealed(&key).map_err(Into::into)
}
fn import_sealed(sealed: Vec<u8>, key: Vec<u8>) -> Result<WitMasterIdentity, WitError> {
let identity = signing::Identity::import_sealed(&sealed, &key)?;
Ok(WitMasterIdentity::new(OwnedIdentity(identity)))
}
fn prove(&self, tau: Vec<u8>) -> Result<WitZkProof, WitError> {
let seed = self.0.plp_seed();
let identity = plp::MasterIdentity::from_seed(seed);
let proj = plp::EphemeralProjection::for_proving(&tau);
let proof = plp::Prover::prove_identity(&identity, &proj, seed);
Ok(WitZkProof {
commitment_w: proof.commitment_w.coeffs().to_vec(),
challenge_c: proof.challenge_c.coeffs().to_vec(),
response_z: proof.response_z.coeffs().to_vec(),
})
}
}
fn poly_from_coeffs(coeffs: &[u32]) -> Result<plp::Poly, WitError> {
if coeffs.len() != crate::RING_N {
return Err(WitError::SerializationError);
}
let mut poly = plp::Poly::zero();
poly.coeffs.copy_from_slice(coeffs);
Ok(poly)
}
fn projection_from_wit(p: &WitProjection) -> Result<plp::EphemeralProjection, WitError> {
if p.tau.len() != 32 {
return Err(WitError::SerializationError);
}
let mut tau = [0u8; 32];
tau.copy_from_slice(&p.tau);
Ok(plp::EphemeralProjection {
tau,
matrix_a: poly_from_coeffs(&p.matrix_a)?,
public_b: poly_from_coeffs(&p.public_b)?,
})
}
fn zk_proof_from_wit(p: &WitZkProof) -> Result<plp::ZkIdentityProof, WitError> {
Ok(plp::ZkIdentityProof {
commitment_w: poly_from_coeffs(&p.commitment_w)?,
challenge_c: poly_from_coeffs(&p.challenge_c)?,
response_z: poly_from_coeffs(&p.response_z)?,
})
}
impl SecretSharingGuest for Component {
fn htss_split(secret: Vec<u8>) -> Result<Vec<WitShare>, WitError> {
let shares = htss::SecretSharer::split_key_material(&secret, COMPONENT_SPLIT_NONCE)?;
Ok(shares
.into_iter()
.map(|s| WitShare {
index: s.index,
value: s.value,
})
.collect())
}
fn htss_reconstruct(shares: Vec<WitShare>) -> Result<Vec<u8>, WitError> {
let native: Vec<htss::HtssShare> = shares
.into_iter()
.map(|s| htss::HtssShare {
index: s.index,
value: s.value,
})
.collect();
Ok(htss::SecretSharer::reconstruct_key_material(&native)?)
}
}
export!(Component);
pub struct OwnedCredential {
credential: credential::Credential,
issuer_seed: Vec<u8>,
}
impl GuestCredential for OwnedCredential {
fn issue(
holder: MasterIdentityBorrow<'_>,
issuer_seed: Vec<u8>,
attributes: Vec<u64>,
issuance_randomness: Vec<u8>,
) -> Result<WitCredential, WitError> {
if attributes.len() != credential::CRED_ATTRIBUTES {
return Err(WitError::InvalidInputLength);
}
let mut values = [0u64; credential::CRED_ATTRIBUTES];
values.copy_from_slice(&attributes);
let identity = plp::MasterIdentity::from_seed(holder.get::<OwnedIdentity>().0.plp_seed());
let params = credential::IssuerParams::from_seed(&issuer_seed);
let cred =
credential::Credential::issue(¶ms, &identity, &values, &issuance_randomness)?;
Ok(WitCredential::new(OwnedCredential { credential: cred, issuer_seed }))
}
fn present(
&self,
holder: MasterIdentityBorrow<'_>,
tau: Vec<u8>,
projection_randomness: Vec<u8>,
disclosed: DisclosureAttributes,
blinding_randomness: Vec<u8>,
presentation_randomness: Vec<u8>,
) -> Result<WitSaapPresentation, WitError> {
if projection_randomness.len() < 32 {
return Err(WitError::InvalidInputLength);
}
let identity = plp::MasterIdentity::from_seed(holder.get::<OwnedIdentity>().0.plp_seed());
let projection = identity.project_at_context(&tau, &projection_randomness);
let params = credential::IssuerParams::from_seed(&self.issuer_seed);
let blinded =
credential::BlindedCredential::new(¶ms, &self.credential, &blinding_randomness)?;
let presentation = credential::prove(
¶ms,
&blinded,
&identity,
&projection,
&tau,
&projection_randomness,
disclosed.bits(),
&presentation_randomness,
)?;
Ok(WitSaapPresentation {
tau,
disclosed,
disclosed_values: presentation.disclosed_values.to_vec(),
t_blind: credential::commitment_flat(&blinded),
challenge: presentation.challenge.coeffs.to_vec(),
z_r: credential::flatten(&presentation.z_r),
z_m: credential::flatten(&presentation.z_m),
z_s: presentation.z_s.coeffs.to_vec(),
z_e: presentation.z_e.coeffs.to_vec(),
})
}
}