#![allow(clippy::needless_range_loop)]
#![allow(missing_docs)]
extern crate alloc;
use alloc::vec::Vec;
wit_bindgen::generate!({
path: "wit",
world: "aethel-core",
});
use aethel::core::types::IdentityError as WitError;
use exports::aethel::core::identity::{
Credential as WitCredential, DisclosureAttributes, EphemeralProjection as WitProjection,
Guest as IdentityGuest, GuestCredential, GuestIssuerPublicParameters, GuestMasterIdentity,
IssuerPublicParameters as WitIssuerPublicParameters, IssuerPublicParametersBorrow,
MasterIdentity as WitMasterIdentity, MasterIdentityBorrow,
RegisteredPurpose as WitRegisteredPurpose,
SaapPresentation as WitSaapPresentation, ZkIdentityProof as WitZkProof,
};
use exports::aethel::core::secret_sharing::{Guest as SecretSharingGuest, HtssShare as WitShare};
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,
IdentityError::InvalidShareSet => WitError::InvalidShareSet,
IdentityError::WireBadMagic => WitError::SerializationError,
IdentityError::WireBadVersion => WitError::SerializationError,
IdentityError::WireLengthMismatch => WitError::SerializationError,
IdentityError::CoefficientOutOfRange => WitError::SerializationError,
}
}
}
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(),
salt: proj.salt.to_vec(),
public_b: vec_to_coeffs(&proj.public_b),
})
}
fn plp_prove_identity(
secret: Vec<u8>,
tau: Vec<u8>,
randomness: Vec<u8>,
) -> Result<WitZkProof, WitError> {
if randomness.len() < 32 {
return Err(WitError::InvalidInputLength);
}
let seed = secret_as_seed(&secret)?;
let identity = plp::MasterIdentity::from_seed(&seed);
let proj = identity.project_at_context(&tau, &randomness);
let proof = plp::Prover::prove_identity(&identity, &proj, &seed)?;
Ok(WitZkProof {
commitment_w: vec_to_coeffs(&proof.commitment_w),
challenge_c: proof.challenge_c.coeffs().to_vec(),
response_z: vec_to_coeffs(&proof.response_z),
})
}
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;
type IssuerPublicParameters = OwnedIssuerParams;
fn saap_verify_presentation(
issuer: IssuerPublicParametersBorrow<'_>,
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::<{ plp::MODULE_K }>(&presentation.z_s)?,
z_e: credential::unflatten::<{ plp::MODULE_K }>(&presentation.z_e)?,
};
let params = &issuer.get::<OwnedIssuerParams>().0;
credential::verify(params, &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)
}
fn verify_signature_with_purpose(
public_key: Vec<u8>,
purpose: Vec<u8>,
message: Vec<u8>,
signature: Vec<u8>,
) -> Result<bool, WitError> {
signing::verify_with_purpose(&public_key, &purpose, &message, &signature)
.map_err(Into::into)
}
fn registered_purposes() -> Vec<WitRegisteredPurpose> {
signing::purpose::ALL
.iter()
.map(|(name, bytes)| WitRegisteredPurpose {
name: (*name).into(),
bytes: bytes.to_vec(),
})
.collect()
}
fn plp_verify_bytes(
projection: Vec<u8>,
proof: Vec<u8>,
context: Vec<u8>,
) -> Result<bool, WitError> {
crate::wire::verify_projection(&projection, &proof, &context).map_err(Into::into)
}
fn encode_projection(projection: WitProjection) -> Vec<u8> {
match projection_from_wit(&projection) {
Ok(proj) => crate::wire::encode_projection(&proj),
Err(_) => Vec::new(),
}
}
fn encode_proof(proof: WitZkProof) -> Vec<u8> {
match zk_proof_from_wit(&proof) {
Ok(zk) => crate::wire::encode_proof(&zk),
Err(_) => Vec::new(),
}
}
}
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 sign_with_purpose(&self, purpose: Vec<u8>, message: Vec<u8>) -> Result<Vec<u8>, WitError> {
self.0.sign_with_purpose(&purpose, &message).map_err(Into::into)
}
fn project_at_context(
&self,
tau: Vec<u8>,
randomness: Vec<u8>,
) -> Result<WitProjection, WitError> {
let proj = self.0.project_at_context(&tau, &randomness)?;
Ok(WitProjection {
tau: proj.tau.to_vec(),
salt: proj.salt.to_vec(),
public_b: vec_to_coeffs(&proj.public_b),
})
}
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>, randomness: Vec<u8>) -> Result<WitZkProof, WitError> {
let proof = self.0.prove(&tau, &randomness)?;
Ok(WitZkProof {
commitment_w: vec_to_coeffs(&proof.commitment_w),
challenge_c: proof.challenge_c.coeffs().to_vec(),
response_z: vec_to_coeffs(&proof.response_z),
})
}
}
fn vec_to_coeffs(v: &plp::PolyVec) -> alloc::vec::Vec<u32> {
let mut out = alloc::vec::Vec::with_capacity(plp::MODULE_K * crate::RING_N);
for poly in v.iter() {
out.extend_from_slice(poly.coeffs());
}
out
}
fn vec_from_coeffs(coeffs: &[u32]) -> Result<plp::PolyVec, WitError> {
if coeffs.len() != plp::MODULE_K * crate::RING_N {
return Err(WitError::SerializationError);
}
for &c in coeffs {
if c >= plp::Q {
return Err(WitError::SerializationError);
}
}
let mut out = [plp::Poly::zero(); plp::MODULE_K];
for (i, chunk) in coeffs.chunks(crate::RING_N).enumerate() {
out[i].coeffs.copy_from_slice(chunk);
}
Ok(out)
}
fn poly_from_coeffs(coeffs: &[u32]) -> Result<plp::Poly, WitError> {
if coeffs.len() != crate::RING_N {
return Err(WitError::SerializationError);
}
for &c in coeffs {
if c >= plp::Q {
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);
}
if p.salt.len() != 32 {
return Err(WitError::SerializationError);
}
let mut tau = [0u8; 32];
tau.copy_from_slice(&p.tau);
let mut salt = [0u8; 32];
salt.copy_from_slice(&p.salt);
Ok(plp::EphemeralProjection {
tau,
salt,
matrix_a: plp::derive_context_matrix(&tau, &salt),
public_b: vec_from_coeffs(&p.public_b)?,
})
}
fn zk_proof_from_wit(p: &WitZkProof) -> Result<plp::ZkIdentityProof, WitError> {
Ok(plp::ZkIdentityProof {
commitment_w: vec_from_coeffs(&p.commitment_w)?,
challenge_c: poly_from_coeffs(&p.challenge_c)?,
response_z: vec_from_coeffs(&p.response_z)?,
})
}
impl SecretSharingGuest for Component {
fn htss_split(secret: Vec<u8>) -> Result<(Vec<WitShare>, Vec<u8>), WitError> {
let (shares, root) =
htss::SecretSharer::split_key_material(&secret, COMPONENT_SPLIT_NONCE)?;
let wit_shares = shares
.into_iter()
.map(|s| WitShare {
index: s.index,
value: s.value,
path: s.path,
})
.collect();
Ok((wit_shares, root.to_vec()))
}
fn htss_reconstruct(shares: Vec<WitShare>, root: Vec<u8>) -> Result<Vec<u8>, WitError> {
if root.len() != 32 {
return Err(WitError::InvalidInputLength);
}
let mut root_arr = [0u8; 32];
root_arr.copy_from_slice(&root);
let native: Vec<htss::HtssShare> = shares
.into_iter()
.map(|s| htss::HtssShare {
index: s.index,
value: s.value,
path: s.path,
})
.collect();
Ok(htss::SecretSharer::reconstruct_key_material(
&native, &root_arr,
)?)
}
}
export!(Component);
pub struct OwnedCredential {
credential: credential::Credential,
params: credential::IssuerParams,
}
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,
params,
}))
}
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 blinded = credential::BlindedCredential::new(
&self.params,
&self.credential,
&blinding_randomness,
)?;
let presentation = credential::prove(
&self.params,
&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: credential::flatten(&presentation.z_s),
z_e: credential::flatten(&presentation.z_e),
})
}
}
pub struct OwnedIssuerParams(credential::IssuerParams);
impl GuestIssuerPublicParameters for OwnedIssuerParams {
fn derive(issuer_seed: Vec<u8>) -> Result<WitIssuerPublicParameters, WitError> {
let params = credential::IssuerParams::from_seed(&issuer_seed)?;
Ok(WitIssuerPublicParameters::new(OwnedIssuerParams(params)))
}
fn serialize(&self) -> Vec<u8> {
self.0.public_seed().to_vec()
}
fn deserialize(bytes: Vec<u8>) -> Result<WitIssuerPublicParameters, WitError> {
let seed: [u8; credential::PUBLIC_SEED_BYTES] = bytes
.as_slice()
.try_into()
.map_err(|_| WitError::InvalidInputLength)?;
let params = credential::IssuerParams::from_public_seed(&seed);
Ok(WitIssuerPublicParameters::new(OwnedIssuerParams(params)))
}
}