use crate::{AliceId, AliceSig};
use alice_crypto::{self as crypto, Key};
use zeroize::Zeroize;
pub fn id_fingerprint(id: &AliceId) -> crypto::Hash {
crypto::hash(id.as_bytes())
}
pub fn keyed_token_hash(key: &[u8; 32], message: &[u8]) -> crypto::Hash {
crypto::keyed_hash(key, message)
}
pub fn seal_session(key: &Key, token: &[u8]) -> Result<Vec<u8>, crypto::CipherError> {
crypto::seal(key, token)
}
pub fn open_session(key: &Key, sealed: &[u8]) -> Result<Vec<u8>, crypto::CipherError> {
crypto::open(key, sealed)
}
pub fn derive_session_key(id_a: &AliceId, id_b: &AliceId, shared_secret: &[u8]) -> Key {
let mut context = [0u8; 64];
for (i, byte) in context[..32].iter_mut().enumerate() {
*byte = id_a.0[i] ^ id_b.0[i];
}
let secret_hash = crypto::hash(shared_secret);
context[32..64].copy_from_slice(secret_hash.as_bytes());
let raw = crypto::derive_key("alice-auth-session-v1", &context);
Key::from_bytes(raw)
}
pub fn sig_fingerprint(sig: &AliceSig) -> crypto::Hash {
crypto::hash(sig.as_bytes())
}
pub fn split_seed(
identity: &crate::Identity,
n: u8,
k: u8,
) -> Result<Vec<crypto::Shard>, crypto::SssError> {
crypto::split(&identity.seed(), n, k)
}
pub fn recover_identity(shards: &[crypto::Shard]) -> Result<crate::Identity, crypto::SssError> {
let mut seed_vec = crypto::recover(shards)?;
if seed_vec.len() != 32 {
seed_vec.zeroize();
return Err(crypto::SssError::EmptySecret);
}
let mut seed = [0u8; 32];
seed.copy_from_slice(&seed_vec);
seed_vec.zeroize();
let identity = crate::Identity::from_seed(&seed);
seed.zeroize();
Ok(identity)
}
pub fn derive_child(parent: &crate::Identity, index: u32) -> crate::Identity {
let mut context = [0u8; 36];
context[..32].copy_from_slice(&parent.seed());
context[32..36].copy_from_slice(&index.to_le_bytes());
let mut child_seed = crypto::derive_key("alice-auth-hd-child-v1", &context);
context.zeroize();
let identity = crate::Identity::from_seed(&child_seed);
child_seed.zeroize();
identity
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id_fingerprint_deterministic() {
let id = AliceId::new([42u8; 32]);
let h1 = id_fingerprint(&id);
let h2 = id_fingerprint(&id);
assert_eq!(h1.as_bytes(), h2.as_bytes());
}
#[test]
fn test_session_seal_open() {
let key = Key::generate().unwrap();
let token = b"session-token-16";
let sealed = seal_session(&key, token).unwrap();
let opened = open_session(&key, &sealed).unwrap();
assert_eq!(&opened, token);
}
#[test]
fn test_wrong_key_fails() {
let k1 = Key::generate().unwrap();
let k2 = Key::generate().unwrap();
let sealed = seal_session(&k1, b"secret").unwrap();
assert!(open_session(&k2, &sealed).is_err());
}
#[test]
fn test_derive_session_key_symmetric() {
let id_a = AliceId::new([1u8; 32]);
let id_b = AliceId::new([2u8; 32]);
let secret = b"shared-secret-bytes";
let k1 = derive_session_key(&id_a, &id_b, secret);
let k2 = derive_session_key(&id_a, &id_b, secret);
assert_eq!(k1.as_bytes(), k2.as_bytes());
}
#[test]
fn test_id_fingerprint_different_ids() {
let a = id_fingerprint(&AliceId::new([1u8; 32]));
let b = id_fingerprint(&AliceId::new([2u8; 32]));
assert_ne!(a.as_bytes(), b.as_bytes());
}
#[test]
fn test_sig_fingerprint_deterministic() {
let sig = AliceSig::new([0xAB; 64]);
let h1 = sig_fingerprint(&sig);
let h2 = sig_fingerprint(&sig);
assert_eq!(h1.as_bytes(), h2.as_bytes());
}
#[test]
fn test_sig_fingerprint_different_sigs() {
let a = sig_fingerprint(&AliceSig::new([1u8; 64]));
let b = sig_fingerprint(&AliceSig::new([2u8; 64]));
assert_ne!(a.as_bytes(), b.as_bytes());
}
#[test]
fn test_keyed_token_hash_deterministic() {
let key = [0x42u8; 32];
let h1 = keyed_token_hash(&key, b"message");
let h2 = keyed_token_hash(&key, b"message");
assert_eq!(h1.as_bytes(), h2.as_bytes());
}
#[test]
fn test_keyed_token_hash_different_keys() {
let h1 = keyed_token_hash(&[1u8; 32], b"msg");
let h2 = keyed_token_hash(&[2u8; 32], b"msg");
assert_ne!(h1.as_bytes(), h2.as_bytes());
}
#[test]
fn test_derive_key_different_secrets() {
let id_a = AliceId::new([1u8; 32]);
let id_b = AliceId::new([2u8; 32]);
let k1 = derive_session_key(&id_a, &id_b, &[0xAAu8; 32]);
let k2 = derive_session_key(&id_a, &id_b, &[0xBBu8; 32]);
assert_ne!(k1.as_bytes(), k2.as_bytes());
}
#[test]
fn test_derive_key_short_secret() {
let id_a = AliceId::new([1u8; 32]);
let id_b = AliceId::new([2u8; 32]);
let k = derive_session_key(&id_a, &id_b, b"short");
assert_eq!(k.as_bytes().len(), 32);
}
#[test]
fn test_seal_empty_payload() {
let key = Key::generate().unwrap();
let sealed = seal_session(&key, b"").unwrap();
let opened = open_session(&key, &sealed).unwrap();
assert!(opened.is_empty());
}
#[test]
fn test_split_recover_identity() {
let id = crate::Identity::gen().unwrap();
let original_pub = id.id();
let shards = split_seed(&id, 5, 3).unwrap();
assert_eq!(shards.len(), 5);
let recovered =
recover_identity(&[shards[0].clone(), shards[2].clone(), shards[4].clone()]).unwrap();
assert_eq!(recovered.id(), original_pub);
}
#[test]
fn test_recovered_identity_can_sign() {
let id = crate::Identity::gen().unwrap();
let shards = split_seed(&id, 3, 2).unwrap();
let recovered = recover_identity(&[shards[0].clone(), shards[1].clone()]).unwrap();
let sig = recovered.sign(b"test message");
assert!(crate::ok(&id.id(), b"test message", &sig));
}
#[test]
fn test_split_different_combinations() {
let id = crate::Identity::gen().unwrap();
let shards = split_seed(&id, 5, 3).unwrap();
let r1 =
recover_identity(&[shards[0].clone(), shards[1].clone(), shards[2].clone()]).unwrap();
let r2 =
recover_identity(&[shards[2].clone(), shards[3].clone(), shards[4].clone()]).unwrap();
assert_eq!(r1.id(), r2.id());
}
#[test]
fn test_derive_child_deterministic() {
let parent = crate::Identity::gen().unwrap();
let c1 = derive_child(&parent, 0);
let c2 = derive_child(&parent, 0);
assert_eq!(c1.id(), c2.id());
}
#[test]
fn test_derive_child_different_indices() {
let parent = crate::Identity::gen().unwrap();
let c0 = derive_child(&parent, 0);
let c1 = derive_child(&parent, 1);
let c2 = derive_child(&parent, 2);
assert_ne!(c0.id(), c1.id());
assert_ne!(c1.id(), c2.id());
assert_ne!(c0.id(), parent.id());
}
#[test]
fn test_derive_child_can_sign() {
let parent = crate::Identity::gen().unwrap();
let child = derive_child(&parent, 42);
let sig = child.sign(b"child signed");
assert!(crate::ok(&child.id(), b"child signed", &sig));
assert!(!crate::ok(&parent.id(), b"child signed", &sig));
}
#[test]
fn test_derive_child_recoverable_from_parent_seed() {
let parent = crate::Identity::gen().unwrap();
let child = derive_child(&parent, 7);
let parent_recovered = crate::Identity::from_seed(&parent.seed());
let child_recovered = derive_child(&parent_recovered, 7);
assert_eq!(child.id(), child_recovered.id());
}
}