1use ark_bn254::Fr;
2use ark_std::Zero;
3use darkpool_crypto::error::CryptoError;
4use darkpool_crypto::IPoseidonHasher;
5use std::sync::Arc;
6
7pub struct HisokaKdf {
8 hasher: Arc<dyn IPoseidonHasher>,
9}
10
11impl HisokaKdf {
12 pub fn new(hasher: Arc<dyn IPoseidonHasher>) -> Self {
13 Self { hasher }
14 }
15
16 pub fn derive(&self, purpose: &str, master: Fr, nonce: Option<Fr>) -> Result<Fr, CryptoError> {
25 let purpose_fr = self.hasher.string_to_fr(purpose)?;
26
27 let mut inputs = vec![master, purpose_fr];
28
29 if let Some(n) = nonce {
30 if !n.is_zero() {
31 inputs.push(n);
32 }
33 }
34
35 Ok(self.hasher.hash(&inputs))
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use ark_ff::One;
43 use darkpool_crypto::poseidon::NoxHasher;
44
45 fn kdf() -> HisokaKdf {
46 HisokaKdf::new(Arc::new(NoxHasher))
47 }
48
49 #[test]
50 fn derive_is_deterministic() {
51 let master = Fr::one();
52 let k = kdf();
53 let a = k.derive("test.purpose", master, None).unwrap();
54 let b = k.derive("test.purpose", master, None).unwrap();
55 assert_eq!(a, b);
56 }
57
58 #[test]
59 fn different_purposes_yield_different_keys() {
60 let master = Fr::one();
61 let k = kdf();
62 let a = k.derive("purpose.alpha", master, None).unwrap();
63 let b = k.derive("purpose.beta", master, None).unwrap();
64 assert_ne!(a, b);
65 }
66
67 #[test]
68 fn different_nonces_yield_different_keys() {
69 let master = Fr::one();
70 let k = kdf();
71 let a = k.derive("same", master, Some(Fr::from(1u64))).unwrap();
72 let b = k.derive("same", master, Some(Fr::from(2u64))).unwrap();
73 assert_ne!(a, b);
74 }
75
76 #[test]
77 fn none_and_zero_nonce_are_equivalent() {
78 let master = Fr::one();
79 let k = kdf();
80 let a = k.derive("same", master, None).unwrap();
81 let b = k.derive("same", master, Some(Fr::zero())).unwrap();
82 assert_eq!(
83 a, b,
84 "None and Some(Fr::zero()) must produce the same key (M-04 collision)"
85 );
86 }
87
88 #[test]
89 fn nonzero_nonce_differs_from_none() {
90 let master = Fr::one();
91 let k = kdf();
92 let a = k.derive("same", master, None).unwrap();
93 let b = k.derive("same", master, Some(Fr::from(1u64))).unwrap();
94 assert_ne!(a, b);
95 }
96
97 #[test]
98 fn different_masters_yield_different_keys() {
99 let k = kdf();
100 let a = k.derive("same", Fr::from(100u64), None).unwrap();
101 let b = k.derive("same", Fr::from(200u64), None).unwrap();
102 assert_ne!(a, b);
103 }
104
105 #[test]
106 fn output_is_nonzero() {
107 let k = kdf();
108 let result = k.derive("test", Fr::one(), None).unwrap();
109 assert!(!result.is_zero());
110 }
111}