1use crate::{
4 arithmetic::{Matrix, RingElem},
5 consts::{DOMSEP_GENMAT, DOMSEP_GENSEC, MAX_MU, MODULUS_Q_BITS, RING_DEG},
6};
7
8use turboshake::digest::{ExtendableOutput, Update, XofReader};
9use turboshake::{CTurboShake128, CTurboShake256};
10
11fn cbd_diff(raw: u32, half: u32, mask: u32) -> u16 {
19 let a = (raw & mask).count_ones() as u16;
20 let b = ((raw >> half) & mask).count_ones() as u16;
21 a.wrapping_sub(b)
22}
23
24fn cbd<const MU: usize>(buf: &[u8], out: &mut RingElem) {
26 assert_eq!(buf.len(), RING_DEG * MU / 8);
27
28 if MU == 8 {
32 for (i, &byte) in buf.iter().enumerate() {
35 let a = (byte & 0x0F).count_ones() as u16;
36 let b = (byte >> 4).count_ones() as u16;
37 out.0[i] = a.wrapping_sub(b);
38 }
39 } else if MU == 10 {
40 for g in 0..RING_DEG / 4 {
44 let i = 5 * g;
45 let b0 = buf[i] as u32;
46 let b1 = buf[i + 1] as u32;
47 let b2 = buf[i + 2] as u32;
48 let b3 = buf[i + 3] as u32;
49 let b4 = buf[i + 4] as u32;
50 let o = 4 * g;
51 out.0[o] = cbd_diff(b0 | (b1 << 8), 5, 0x1f);
52 out.0[o + 1] = cbd_diff((b1 | (b2 << 8)) >> 2, 5, 0x1f);
53 out.0[o + 2] = cbd_diff((b2 | (b3 << 8)) >> 4, 5, 0x1f);
54 out.0[o + 3] = cbd_diff((b3 | (b4 << 8)) >> 6, 5, 0x1f);
55 }
56 } else if MU == 6 {
57 for g in 0..RING_DEG / 4 {
60 let i = 3 * g;
61 let b0 = buf[i] as u32;
62 let b1 = buf[i + 1] as u32;
63 let b2 = buf[i + 2] as u32;
64 let o = 4 * g;
65 out.0[o] = cbd_diff(b0, 3, 0x07);
66 out.0[o + 1] = cbd_diff((b0 | (b1 << 8)) >> 6, 3, 0x07);
67 out.0[o + 2] = cbd_diff((b1 | (b2 << 8)) >> 4, 3, 0x07);
68 out.0[o + 3] = cbd_diff(b2 >> 2, 3, 0x07);
69 }
70 } else {
71 let half = MU / 2;
74 let mask: u32 = (1 << half) - 1;
75
76 let mut bit_pos = 0;
77 for coeff in out.0.iter_mut() {
78 let byte_idx = bit_pos / 8;
79 let bit_in_byte = bit_pos % 8;
80
81 let mut raw: u32 = buf[byte_idx] as u32;
82 if byte_idx + 1 < buf.len() {
83 raw |= (buf[byte_idx + 1] as u32) << 8;
84 }
85 if byte_idx + 2 < buf.len() {
86 raw |= (buf[byte_idx + 2] as u32) << 16;
87 }
88 raw >>= bit_in_byte;
89
90 let a = (raw & mask).count_ones() as u16;
91 let b = ((raw >> half) & mask).count_ones() as u16;
92 *coeff = a.wrapping_sub(b);
93
94 bit_pos += MU;
95 }
96 }
97}
98
99pub(crate) fn gen_secret_from_seed<const L: usize, const MU: usize>(
102 seed: &[u8; 32],
103) -> Matrix<L, 1> {
104 let mut secret = Matrix::default();
105 let mut backing_buf = [0u8; RING_DEG * MAX_MU / 8];
108 let buf = &mut backing_buf[..RING_DEG * MU / 8];
109
110 for i in 0..L {
112 let mut hasher = CTurboShake256::<DOMSEP_GENSEC>::default();
113 hasher.update(seed);
114 hasher.update(&[i as u8]);
115 let mut reader = hasher.finalize_xof();
116 reader.read(buf);
117 cbd::<MU>(buf, &mut secret.0[i][0]);
118 }
119
120 secret
121}
122
123pub(crate) fn gen_matrix_from_seed<const L: usize>(seed: &[u8; 32]) -> Matrix<L, L> {
127 let mut mat = Matrix::default();
129 let mut buf = [0u8; RING_DEG * MODULUS_Q_BITS / 8];
131
132 for i in 0..L {
134 for j in 0..L {
135 let mut hasher = CTurboShake128::<DOMSEP_GENMAT>::default();
136 hasher.update(seed);
137 hasher.update(&[i as u8]);
138 hasher.update(&[j as u8]);
139 let mut reader = hasher.finalize_xof();
140 reader.read(&mut buf);
141 mat.0[i][j] = RingElem::deserialize(&buf, MODULUS_Q_BITS);
142 }
143 }
144
145 mat
146}
147
148#[cfg(test)]
149mod test {
150 use super::*;
151
152 use rand::RngCore;
153
154 fn cbd_reference<const MU: usize>(buf: &[u8], out: &mut RingElem) {
157 let bit = |i: usize| ((buf[i / 8] >> (i % 8)) & 1) as u16;
158 for (c, coeff) in out.0.iter_mut().enumerate() {
159 let mut a = 0u16;
160 let mut b = 0u16;
161 for k in 0..MU / 2 {
162 a += bit(c * MU + k);
163 b += bit(c * MU + MU / 2 + k);
164 }
165 *coeff = a.wrapping_sub(b);
166 }
167 }
168
169 #[test]
171 fn specialized_cbd_matches_reference() {
172 fn check<const MU: usize>(rng: &mut impl RngCore) {
173 let mut backing_buf = [0u8; RING_DEG * MAX_MU / 8];
174 let buf = &mut backing_buf[..RING_DEG * MU / 8];
175
176 for _ in 0..100 {
177 rng.fill_bytes(buf);
178 let mut fast = RingElem::default();
179 let mut reference = RingElem::default();
180 cbd::<MU>(buf, &mut fast);
181 cbd_reference::<MU>(buf, &mut reference);
182 assert_eq!(fast.0, reference.0);
183 }
184 }
185
186 let mut rng = rand::rng();
187 check::<6>(&mut rng);
188 check::<8>(&mut rng);
189 check::<10>(&mut rng);
190 }
191}