#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![warn(clippy::all)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "wasm")]
use alloc::vec::Vec;
pub mod plp;
pub mod htss;
pub mod saap;
pub mod signing;
pub mod credential;
pub mod sampling;
#[cfg(feature = "puf")]
pub mod puf;
pub mod ct_verify;
pub mod sdk;
pub mod identity_error;
#[cfg(feature = "component")]
pub mod component;
pub use plp::{EphemeralProjection, MasterIdentity, Prover, Verifier, ZkIdentityProof};
pub use htss::{HypercubeNetwork, NodeAddress, SecretSharer, ZkProofSegment};
pub use saap::{SaapProof, SaapValidationError};
pub use sampling::{PlpProof, RejectionError, VectorK};
pub use identity_error::IdentityError;
pub const RING_N: usize = 256;
pub const MODULUS_Q: i64 = 8_380_417;
pub const MODULE_K: usize = 4;
pub const PARAM_ETA: i64 = 2;
pub const PARAM_GAMMA1: i64 = 131_072;
pub const PARAM_BETA: i64 = 78;
pub const REJECTION_BOUND: i64 = PARAM_GAMMA1 - PARAM_BETA;
pub const FIXED_ITERATION_CEILING: usize = 16;
pub const EIAB_MAGIC: &[u8; 4] = b"ATH1";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AethelError {
SerializationError,
VerificationFailed,
RejectionSamplingFailed,
PufReconstructionFailed,
InvalidInputLength,
SaapError(SaapValidationError),
}
impl From<SaapValidationError> for AethelError {
fn from(e: SaapValidationError) -> Self {
AethelError::SaapError(e)
}
}
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn plp_project_at_context(seed: &[u8], tau: &[u8], randomness: &[u8]) -> Vec<u8> {
if seed.len() < 32 || randomness.len() < 32 {
return alloc::vec![];
}
let mut seed_arr = [0u8; 32];
seed_arr.copy_from_slice(&seed[..32]);
let identity = MasterIdentity::from_seed(&seed_arr);
let proj = identity.project_at_context(tau, randomness);
let mut out = alloc::vec![0u8; 32 + 256 * 4 + 256 * 4];
out[..32].copy_from_slice(&proj.tau);
for (i, &c) in proj.matrix_a.coeffs.iter().enumerate() {
let offset = 32 + i * 4;
out[offset..offset + 4].copy_from_slice(&c.to_le_bytes());
}
for (i, &c) in proj.public_b.coeffs.iter().enumerate() {
let offset = 32 + 256 * 4 + i * 4;
out[offset..offset + 4].copy_from_slice(&c.to_le_bytes());
}
out
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn plp_prove_identity(seed: &[u8], tau: &[u8]) -> Vec<u8> {
if seed.len() < 32 {
return alloc::vec![];
}
let mut seed_arr = [0u8; 32];
seed_arr.copy_from_slice(&seed[..32]);
let identity = MasterIdentity::from_seed(&seed_arr);
let proj = plp::EphemeralProjection::for_proving(tau);
let proof = Prover::prove_identity(&identity, &proj, &seed_arr);
let mut out = alloc::vec![0u8; 256 * 4 * 3];
for (i, &c) in proof.commitment_w.coeffs.iter().enumerate() {
let offset = i * 4;
out[offset..offset + 4].copy_from_slice(&c.to_le_bytes());
}
for (i, &c) in proof.challenge_c.coeffs.iter().enumerate() {
let offset = 256 * 4 + i * 4;
out[offset..offset + 4].copy_from_slice(&c.to_le_bytes());
}
for (i, &c) in proof.response_z.coeffs.iter().enumerate() {
let offset = 256 * 4 * 2 + i * 4;
out[offset..offset + 4].copy_from_slice(&c.to_le_bytes());
}
out
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn plp_verify(projection_bytes: &[u8], proof_bytes: &[u8]) -> bool {
use plp::Poly;
let proj_size = 32 + 256 * 4 + 256 * 4;
let proof_size = 256 * 4 * 3;
if projection_bytes.len() < proj_size || proof_bytes.len() < proof_size {
return false;
}
let mut tau = [0u8; 32];
tau.copy_from_slice(&projection_bytes[..32]);
let mut matrix_a = Poly::zero();
for i in 0..256 {
let offset = 32 + i * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&projection_bytes[offset..offset + 4]);
matrix_a.coeffs[i] = u32::from_le_bytes(bytes);
}
let mut public_b = Poly::zero();
for i in 0..256 {
let offset = 32 + 256 * 4 + i * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&projection_bytes[offset..offset + 4]);
public_b.coeffs[i] = u32::from_le_bytes(bytes);
}
let proj = EphemeralProjection { tau, matrix_a, public_b };
let mut commitment_w = Poly::zero();
for i in 0..256 {
let offset = i * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
commitment_w.coeffs[i] = u32::from_le_bytes(bytes);
}
let mut challenge_c = Poly::zero();
for i in 0..256 {
let offset = 256 * 4 + i * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
challenge_c.coeffs[i] = u32::from_le_bytes(bytes);
}
let mut response_z = Poly::zero();
for i in 0..256 {
let offset = 256 * 4 * 2 + i * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
response_z.coeffs[i] = u32::from_le_bytes(bytes);
}
let proof = ZkIdentityProof { commitment_w, challenge_c, response_z };
Verifier::verify(&proj, &proof)
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn saap_prove_wasm(
credential: &[u8],
disclosure_mask: u64,
tau: &[u8],
secret_key_bytes: &[u8],
randomness: &[u8],
) -> Vec<u8> {
use saap::{saap_prove, VectorK as SaapVectorK};
if randomness.len() < 32 {
return alloc::vec![];
}
let mut sk = SaapVectorK::zero();
let coeff_size = saap::MODULE_K * saap::RING_N * 4;
if secret_key_bytes.len() >= coeff_size {
for k in 0..saap::MODULE_K {
for n in 0..saap::RING_N {
let offset = (k * saap::RING_N + n) * 4;
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&secret_key_bytes[offset..offset + 4]);
sk.vec[k].coeffs[n] = i32::from_le_bytes(bytes);
}
}
}
let proof = saap_prove(credential, disclosure_mask, tau, &sk, randomness);
let mut out = alloc::vec![];
out.extend_from_slice(&proof.context_tag);
out.extend_from_slice(&proof.disclosure_mask.to_le_bytes());
for &v in proof.attributes.values.iter() {
out.extend_from_slice(&v.to_le_bytes());
}
for &c in proof.challenge.coeffs.iter() {
out.extend_from_slice(&c.to_le_bytes());
}
for k in 0..saap::MODULE_K {
for &c in proof.z.vec[k].coeffs.iter() {
out.extend_from_slice(&c.to_le_bytes());
}
}
out.extend_from_slice(&proof.commitment_hash);
for k in 0..saap::MODULE_K {
for &c in proof.commitment_w.vec[k].coeffs.iter() {
out.extend_from_slice(&c.to_le_bytes());
}
}
out
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn saap_verify_wasm(_proof_bytes: &[u8], _tau: &[u8]) -> bool {
false
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn htss_split(secret: &[u8], nonce: &[u8]) -> Vec<u8> {
htss::SecretSharer::split_key_material_bytes(secret, nonce).unwrap_or_default()
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn htss_reconstruct(shares_bytes: &[u8]) -> Vec<u8> {
htss::SecretSharer::reconstruct_key_material_bytes(shares_bytes).unwrap_or_default()
}
#[cfg(all(feature = "wasm", feature = "puf"))]
#[wasm_bindgen]
pub fn puf_enroll(sram_response: &[u8]) -> Vec<u8> {
use puf::BchFuzzyExtractor;
if sram_response.len() < 128 {
return alloc::vec![];
}
let mut sram = [0u8; 128];
sram.copy_from_slice(&sram_response[..128]);
let (key, helper) = BchFuzzyExtractor::enroll(&sram);
let mut out = alloc::vec![0u8; 128];
out[..64].copy_from_slice(&key);
out[64..].copy_from_slice(&helper);
out
}
#[cfg(all(feature = "wasm", feature = "puf"))]
#[wasm_bindgen]
pub fn puf_reconstruct(sram_response: &[u8], helper_data: &[u8]) -> Vec<u8> {
use puf::{BchFuzzyExtractor, puf_seed_to_vector_k};
use sampling::{MODULE_K as SK_MODULE_K, RING_N as SK_RING_N};
if sram_response.len() < 128 || helper_data.len() < 64 {
return alloc::vec![];
}
let mut sram = [0u8; 128];
sram.copy_from_slice(&sram_response[..128]);
let mut helper = [0u8; 64];
helper.copy_from_slice(&helper_data[..64]);
match BchFuzzyExtractor::reconstruct(&sram, &helper) {
Some(key) => {
let v = puf_seed_to_vector_k(&key);
let mut out = alloc::vec![0u8; SK_MODULE_K * SK_RING_N * 4];
for k in 0..SK_MODULE_K {
for n in 0..SK_RING_N {
let offset = (k * SK_RING_N + n) * 4;
out[offset..offset + 4].copy_from_slice(&v.vec[k].coeffs[n].to_le_bytes());
}
}
out
}
None => alloc::vec![],
}
}