#![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 sampling;
#[cfg(feature = "puf")]
pub mod puf;
pub mod ct_verify;
pub mod sdk;
pub mod identity_error;
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]) -> 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 = identity.project_at_context(tau);
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 = identity.project_at_context(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],
) -> Vec<u8> {
use saap::{saap_prove, VectorK as SaapVectorK};
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);
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 {
use saap::{SaapProof, VectorK as SaapVectorK, Polynomial as SaapPoly, verify_saap_proof};
let min_size = 32 + 8 + 64 + 256 * 4 + 4 * 256 * 4 + 32 + 4 * 256 * 4;
if proof_bytes.len() < min_size {
return false;
}
let mut proof = SaapProof::zero();
let mut offset = 0usize;
proof.context_tag.copy_from_slice(&proof_bytes[offset..offset + 32]);
offset += 32;
let mut mask_bytes = [0u8; 8];
mask_bytes.copy_from_slice(&proof_bytes[offset..offset + 8]);
proof.disclosure_mask = u64::from_le_bytes(mask_bytes);
offset += 8;
for i in 0..saap::MAX_ATTRIBUTES {
let mut v = [0u8; 8];
v.copy_from_slice(&proof_bytes[offset..offset + 8]);
proof.attributes.values[i] = u64::from_le_bytes(v);
offset += 8;
}
for i in 0..saap::RING_N {
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
proof.challenge.coeffs[i] = i32::from_le_bytes(bytes);
offset += 4;
}
for k in 0..saap::MODULE_K {
for n in 0..saap::RING_N {
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
proof.z.vec[k].coeffs[n] = i32::from_le_bytes(bytes);
offset += 4;
}
}
proof.commitment_hash.copy_from_slice(&proof_bytes[offset..offset + 32]);
offset += 32;
for k in 0..saap::MODULE_K {
for n in 0..saap::RING_N {
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&proof_bytes[offset..offset + 4]);
proof.commitment_w.vec[k].coeffs[n] = i32::from_le_bytes(bytes);
offset += 4;
}
}
let matrix_a = [SaapVectorK::zero(); saap::MODULE_K];
let attr_commits = [SaapPoly::zero(); saap::MAX_ATTRIBUTES];
verify_saap_proof(&proof, &matrix_a, &attr_commits).is_ok()
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn htss_split(secret: u64, seed: u64) -> Vec<u8> {
use htss::SecretSharer;
let shares = SecretSharer::split_secret(secret, 3, 5, seed);
let mut out = alloc::vec![0u8; shares.len() * 9];
for (i, &(id, val)) in shares.iter().enumerate() {
out[i * 9] = id;
out[i * 9 + 1..i * 9 + 9].copy_from_slice(&val.to_le_bytes());
}
out
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub fn htss_reconstruct(shares_bytes: &[u8]) -> u64 {
use htss::SecretSharer;
if shares_bytes.len() < 9 * 3 {
return 0;
}
let num_shares = shares_bytes.len() / 9;
let mut shares = alloc::vec![];
for i in 0..num_shares {
let id = shares_bytes[i * 9];
let mut val_bytes = [0u8; 8];
val_bytes.copy_from_slice(&shares_bytes[i * 9 + 1..i * 9 + 9]);
let val = u64::from_le_bytes(val_bytes);
shares.push((id, val));
}
SecretSharer::reconstruct_secret(&shares)
}
#[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![],
}
}