1use std::{cell::RefCell, convert::TryFrom};
2
3use bitvec::{array::BitArray, order};
4use decaf377::{Fq, Fr};
5use rand_core::{CryptoRng, RngCore};
6
7use crate::{hash, hkd, Clue, Error, Precision};
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
17pub struct ClueKey(pub [u8; 32]);
18
19pub struct ExpandedClueKey {
22 root_pub: decaf377::Element,
23 root_pub_enc: decaf377::Encoding,
24 subkeys: RefCell<Vec<decaf377::Element>>,
25}
26
27impl ClueKey {
28 pub fn expand(&self) -> Result<ExpandedClueKey, Error> {
34 ExpandedClueKey::new(self)
35 }
36
37 pub fn expand_infallible(&self) -> ExpandedClueKey {
42 let mut counter = 0u32;
43 loop {
44 counter += 1;
45 let ck_fq_incremented = Fq::from_le_bytes_mod_order(&self.0) + Fq::from(counter);
46 let ck = ClueKey(ck_fq_incremented.to_bytes());
47 if let Ok(eck) = ck.expand() {
48 return eck;
49 }
50 }
51 }
52}
53
54impl ExpandedClueKey {
55 pub fn new(clue_key: &ClueKey) -> Result<Self, Error> {
56 let root_pub_enc = decaf377::Encoding(clue_key.0);
57 let root_pub = root_pub_enc
58 .vartime_decompress()
59 .map_err(|_| Error::InvalidAddress)?;
60
61 Ok(ExpandedClueKey {
62 root_pub,
63 root_pub_enc,
64 subkeys: RefCell::new(Vec::new()),
65 })
66 }
67
68 fn ensure_at_least(&self, precision: usize) -> Result<(), Error> {
70 let current_precision = self.subkeys.borrow().len();
71
72 if precision <= current_precision {
74 return Ok(());
75 }
76
77 let mut expanded_keys = (current_precision..precision)
78 .map(|i| hkd::derive_public(&self.root_pub, &self.root_pub_enc, i as u8))
79 .collect::<Vec<_>>();
80
81 self.subkeys.borrow_mut().append(&mut expanded_keys);
82
83 Ok(())
84 }
85
86 #[allow(non_snake_case)]
97 pub fn create_clue_deterministic(
98 &self,
99 precision: Precision,
100 rseed: [u8; 32],
101 ) -> Result<Clue, Error> {
102 let precision_bits = precision.bits() as usize;
103 self.ensure_at_least(precision_bits)?;
105
106 let r = {
107 let hash = blake2b_simd::Params::default()
108 .personal(b"decaf377-fmd.rdv")
109 .to_state()
110 .update(&self.root_pub_enc.0)
111 .update(&rseed)
112 .finalize();
113
114 Fr::from_le_bytes_mod_order(hash.as_bytes())
115 };
116 let z = {
117 let hash = blake2b_simd::Params::default()
118 .personal(b"decaf377-fmd.zdv")
119 .to_state()
120 .update(&self.root_pub_enc.0)
121 .update(&rseed)
122 .finalize();
123
124 Fr::from_le_bytes_mod_order(hash.as_bytes())
125 };
126
127 let P = r * decaf377::Element::GENERATOR;
128 let P_encoding = P.vartime_compress();
129 let Q = z * decaf377::Element::GENERATOR;
130 let Q_encoding = Q.vartime_compress();
131
132 let mut ctxts = BitArray::<[u8; 3], order::Lsb0>::ZERO;
133 let Xs = self.subkeys.borrow();
134
135 for i in 0..precision_bits {
136 let rXi = (r * Xs[i]).vartime_compress();
137 let key_i = hash::to_bit(&P_encoding.0, &rXi.0, &Q_encoding.0);
138 let ctxt_i = key_i ^ 1u8;
139 ctxts.set(i, ctxt_i != 0);
140 }
141
142 let m = hash::to_scalar(&P_encoding.0, precision_bits as u8, ctxts.as_raw_slice());
143 let y = (z - m) * r.inverse().expect("random element is nonzero");
144
145 let mut buf = [0u8; 68];
146 buf[0..32].copy_from_slice(&P_encoding.0[..]);
147 buf[32..64].copy_from_slice(&y.to_bytes()[..]);
148 buf[64] = precision_bits as u8;
149 buf[65..68].copy_from_slice(ctxts.as_raw_slice());
150
151 Ok(Clue(buf))
152 }
153
154 #[allow(non_snake_case)]
164 pub fn create_clue<R: RngCore + CryptoRng>(
165 &self,
166 precision: Precision,
167 mut rng: R,
168 ) -> Result<Clue, Error> {
169 let mut rseed = [0u8; 32];
170 rng.fill_bytes(&mut rseed);
171 self.create_clue_deterministic(precision, rseed)
172 }
173}
174
175impl TryFrom<&[u8]> for ClueKey {
176 type Error = Error;
177
178 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
179 if bytes.len() == 32 {
180 let mut arr = [0u8; 32];
181 arr.copy_from_slice(&bytes[0..32]);
182 Ok(ClueKey(arr))
183 } else {
184 Err(Error::InvalidClueKey)
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_clue_key_infallible_expand() {
195 let valid_ck = ClueKey(decaf377::Element::GENERATOR.vartime_compress().0);
196 let ck_fq_invalid = Fq::from_le_bytes_mod_order(&valid_ck.0) + Fq::from(1u64);
197 let invalid_ck = ClueKey(ck_fq_invalid.to_bytes());
198 let _eck = invalid_ck.expand_infallible();
199 }
200}