confium_privacy/
distributed_prg.rs1use hmac::{Hmac, KeyInit, Mac};
8use sha2::Sha256;
9
10type HmacSha256 = Hmac<Sha256>;
11
12#[derive(Debug, Clone)]
14pub struct PrgShare {
15 pub seed: [u8; 32],
17}
18
19impl PrgShare {
20 pub fn next_block(&self, nonce: &[u8]) -> [u8; 32] {
22 let mut mac = HmacSha256::new_from_slice(&self.seed).expect("HMAC key");
23 mac.update(nonce);
24 let result = mac.finalize().into_bytes();
25 let mut out = [0u8; 32];
26 out.copy_from_slice(&result);
27 out
28 }
29}
30
31pub fn combine(shares: &[PrgShare], nonce: &[u8]) -> [u8; 32] {
33 let mut result = [0u8; 32];
34 for share in shares {
35 let block = share.next_block(nonce);
36 for (i, &b) in block.iter().enumerate() {
37 result[i] ^= b;
38 }
39 }
40 result
41}
42
43pub fn distribute(master_seed: &[u8; 32], party_count: usize) -> Vec<PrgShare> {
45 use rand_core::{OsRng, RngCore};
46 let mut shares = Vec::with_capacity(party_count);
47 let mut xor_accum = *master_seed;
48 for _ in 0..(party_count - 1) {
49 let mut seed = [0u8; 32];
50 OsRng.fill_bytes(&mut seed);
51 for (i, &b) in seed.iter().enumerate() {
52 xor_accum[i] ^= b;
53 }
54 shares.push(PrgShare { seed });
55 }
56 shares.push(PrgShare { seed: xor_accum });
57 shares
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn shares_combine_to_master() {
66 let master = [0x42u8; 32];
67 let shares = distribute(&master, 5);
68 let output = combine(&shares, b"nonce");
69 let mut expected = [0u8; 32];
71 for share in &shares {
72 for (i, &b) in share.next_block(b"nonce").iter().enumerate() {
73 expected[i] ^= b;
74 }
75 }
76 assert_eq!(output, expected);
79 }
80
81 #[test]
82 fn deterministic_with_same_nonce() {
83 let master = [1u8; 32];
84 let shares = distribute(&master, 3);
85 let r1 = combine(&shares, b"nonce");
86 let r2 = combine(&shares, b"nonce");
87 assert_eq!(r1, r2);
88 }
89
90 #[test]
91 fn different_nonces_different_outputs() {
92 let master = [1u8; 32];
93 let shares = distribute(&master, 3);
94 let r1 = combine(&shares, b"nonce1");
95 let r2 = combine(&shares, b"nonce2");
96 assert_ne!(r1, r2);
97 }
98
99 #[test]
100 fn share_count_matches_party_count() {
101 let master = [0u8; 32];
102 let shares = distribute(&master, 4);
103 assert_eq!(shares.len(), 4);
104 }
105
106 #[test]
107 fn single_share() {
108 let master = [0u8; 32];
109 let shares = distribute(&master, 1);
110 assert_eq!(shares.len(), 1);
111 let output = combine(&shares, b"n");
112 let expected = shares[0].next_block(b"n");
113 assert_eq!(output, expected);
114 }
115}