adss/
lib.rs

1//! The `adss` crate defines functionality for performing secret
2//! sharing with established security guarantees. We use this framework
3//! as it allows for specifying the random coins that are used for
4//! establishing the lagrange polynomial coefficients explicitly. A
5//! description of the Adept Secret Sharing framework is provided in
6//! the paper by [Bellare et al.](https://eprint.iacr.org/2020/800).
7
8use star_sharks::Sharks;
9use std::convert::{TryFrom, TryInto};
10use std::error::Error;
11use std::fmt;
12use strobe_rs::{SecParam, Strobe};
13use zeroize::{Zeroize, ZeroizeOnDrop};
14
15mod strobe_rng;
16use strobe_rng::StrobeRng;
17
18/// The length of a serialized `AccessStructure`, in bytes.
19pub const ACCESS_STRUCTURE_LENGTH: usize = 4;
20
21/// The length of a message authentication code used in `Share`, in bytes.
22pub const MAC_LENGTH: usize = 64;
23
24/// The `AccessStructure` struct defines the policy under which shares
25/// can be recovered. Currently, this policy is simply whether there are
26/// `threshold` number of independent shares.
27#[derive(Debug, Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
28pub struct AccessStructure {
29  threshold: u32,
30}
31
32/// Append a `u32` in little-endian coding
33pub fn store_u32(u: u32, out: &mut Vec<u8>) {
34  out.extend(u.to_le_bytes());
35}
36
37/// Attempt to parse a little-endian value from a byte serialization
38pub fn load_u32(bytes: &[u8]) -> Option<u32> {
39  if bytes.len() != 4 {
40    return None;
41  }
42
43  let mut bits: [u8; 4] = [0u8; 4];
44  bits.copy_from_slice(bytes);
45  Some(u32::from_le_bytes(bits))
46}
47
48/// Append a chunk of data
49///
50/// Extends the output `Vec` with the passed slice, prepending
51/// a 4-byte, little-endian length header so it can be parsed
52/// out later.
53pub fn store_bytes(s: &[u8], out: &mut Vec<u8>) {
54  store_u32(s.len() as u32, out);
55  out.extend(s);
56}
57
58/// Parse the next data chunk out of a byte slice
59///
60/// This reads a 4-byte, little-endian length header and
61/// then returns a new slice with the data bounded by
62/// that header.
63///
64/// Returns `None` if there is insufficient data for
65/// the complete chunk.
66pub fn load_bytes(bytes: &[u8]) -> Option<&[u8]> {
67  if bytes.len() < 4 {
68    return None;
69  }
70
71  let len: u32 = load_u32(&bytes[..4])?;
72  if bytes.len() < (4 + len) as usize {
73    return None;
74  }
75
76  Some(&bytes[4..4 + len as usize])
77}
78
79/// An `AccessStructure` defines how a message is to be split among multiple parties
80///
81/// In particular this determines how many shares will be issued and what threshold of the shares
82/// are needed to reconstruct the original `Commune`.
83impl AccessStructure {
84  /// Convert this `AccessStructure` to a byte array.
85  pub fn to_bytes(&self) -> [u8; ACCESS_STRUCTURE_LENGTH] {
86    self.threshold.to_le_bytes()
87  }
88
89  /// Parse a serialized `AccessStructure` from a byte slice.
90  ///
91  /// Returns `None` if a valid structure was not found, for
92  /// example if the slice was too short.
93  pub fn from_bytes(bytes: &[u8]) -> Option<AccessStructure> {
94    let threshold = load_u32(bytes)?;
95    Some(AccessStructure { threshold })
96  }
97}
98
99#[allow(non_snake_case)]
100impl From<AccessStructure> for Sharks {
101  fn from(A: AccessStructure) -> Sharks {
102    Sharks(A.threshold)
103  }
104}
105
106/// A a unique instance of sharing across multiple parties
107///
108/// A `Commune` consists of an access structure defining the
109/// parameters of the sharing, a secret message which will be shared,
110/// "random coins" which provide strong but possibly non-uniform
111/// entropy and an optional STROBE transcript which can include
112/// extra data which will be authenticated.
113#[allow(non_snake_case)]
114#[derive(Clone, ZeroizeOnDrop)]
115pub struct Commune {
116  /// `A` is an `AccessStructure` defining the sharing
117  A: AccessStructure,
118  /// `M` is the message to be shared
119  M: Vec<u8>,
120  /// `R` are the "random coins" which may not be uniform
121  R: Vec<u8>,
122  /// `T` is a `Strobe` transcript which forms optional tags to be authenticated
123  T: Option<Strobe>,
124}
125
126/// The `Share` struct holds the necessary data that is encoded in a
127/// single secret share. A share itself reveals nothing about the
128/// encoded secret data until it is grouped with a set of shares that
129/// satisfy the policy in the associated `AccessStructure`.
130#[allow(non_snake_case)]
131#[derive(Clone, Eq, PartialEq, Zeroize)]
132pub struct Share {
133  A: AccessStructure,
134  S: star_sharks::Share,
135  /// C is the encrypted message
136  C: Vec<u8>,
137  /// D is the encrypted randomness
138  D: Vec<u8>,
139  /// J is a MAC showing knowledge of A, M, R, and T
140  J: [u8; MAC_LENGTH],
141  T: (),
142}
143
144impl fmt::Debug for Share {
145  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146    f.debug_struct("Share").field("S", &self.S).finish()
147  }
148}
149
150impl Share {
151  pub fn to_bytes(&self) -> Vec<u8> {
152    let mut out: Vec<u8> = Vec::new();
153
154    // A: AccessStructure
155    out.extend(self.A.to_bytes());
156
157    // S: star_sharks::Share
158    store_bytes(&Vec::from(&self.S), &mut out);
159
160    // C: Vec<u8>
161    store_bytes(&self.C, &mut out);
162
163    // D: Vec<u8>
164    store_bytes(&self.D, &mut out);
165
166    // J: [u8; 64]
167    out.extend(self.J);
168
169    out
170  }
171
172  pub fn from_bytes(bytes: &[u8]) -> Option<Share> {
173    let mut slice = bytes;
174
175    // A: AccessStructure
176    let a = AccessStructure::from_bytes(&slice[..ACCESS_STRUCTURE_LENGTH])?;
177    slice = &slice[ACCESS_STRUCTURE_LENGTH..];
178
179    // S: star_sharks::Share
180    let sb = load_bytes(slice)?;
181    slice = &slice[4 + sb.len()..];
182
183    // C: Vec<u8>
184    let c = load_bytes(slice)?;
185    slice = &slice[4 + c.len()..];
186
187    // D: Vec<u8>
188    let d = load_bytes(slice)?;
189    slice = &slice[4 + d.len()..];
190
191    // J: [u8; 64]
192    let j: [u8; MAC_LENGTH] = slice.try_into().ok()?;
193
194    Some(Share {
195      A: a,
196      S: star_sharks::Share::try_from(sb).ok()?,
197      C: c.to_vec(),
198      D: d.to_vec(),
199      J: j,
200      T: (),
201    })
202  }
203}
204
205#[allow(non_snake_case)]
206impl Commune {
207  pub fn new(
208    threshold: u32,
209    message: Vec<u8>,
210    randomness: Vec<u8>,
211    transcript: Option<Strobe>,
212  ) -> Self {
213    Commune {
214      A: AccessStructure { threshold },
215      M: message,
216      R: randomness,
217      T: transcript,
218    }
219  }
220
221  /// The `share` function samples a single secret share for the
222  /// specified `message`.
223  pub fn share(self) -> Result<Share, Box<dyn Error>> {
224    // H4κ = (A, M, R, T)
225    let mut transcript = self
226      .T
227      .clone()
228      .unwrap_or_else(|| Strobe::new(b"adss", SecParam::B128));
229    transcript.ad(&self.A.to_bytes(), false);
230    transcript.ad(&self.M, false);
231    transcript.key(&self.R, false);
232
233    // J is a MAC which authenticates A, M, R, and T
234    let mut J = [0u8; 64];
235    transcript.send_mac(&mut J, false);
236
237    // K is the derived key used to encrypt the message and our "random coins"
238    let mut K = [0u8; 16];
239    transcript.prf(&mut K, false);
240
241    // L is the randomness to be fed to secret sharing polynomial generation
242    let mut L: StrobeRng = transcript.into();
243
244    let mut key = Strobe::new(b"adss encrypt", SecParam::B128);
245    key.key(&K, false);
246
247    // C is the encrypted message
248    let mut C: Vec<u8> = vec![0; self.M.len()];
249    C.copy_from_slice(&self.M);
250    key.send_enc(&mut C, false);
251
252    // D is the encrypted randomness
253    let mut D: Vec<u8> = vec![0; self.R.len()];
254    D.copy_from_slice(&self.R);
255    key.send_enc(&mut D, false);
256
257    // Generate a random share
258    let mut K_vec: Vec<u8> = K.to_vec();
259    K_vec.extend(vec![0u8; 16]);
260    let polys = Sharks::from(self.A.clone()).dealer_rng(&K_vec, &mut L)?;
261    let S = polys.gen(&mut rand::rngs::OsRng);
262    Ok(Share {
263      A: self.A.clone(),
264      S,
265      C,
266      D,
267      J,
268      T: (),
269    })
270  }
271
272  pub fn get_message(&self) -> Vec<u8> {
273    self.M.clone()
274  }
275
276  fn verify(&self, J: &[u8; MAC_LENGTH]) -> Result<(), Box<dyn Error>> {
277    let mut transcript = self
278      .clone()
279      .T
280      .clone()
281      .unwrap_or_else(|| Strobe::new(b"adss", SecParam::B128));
282    transcript.ad(&self.A.to_bytes(), false);
283    transcript.ad(&self.M, false);
284    transcript.key(&self.R, false);
285
286    transcript
287      .recv_mac(J)
288      .map_err(|_| "Mac validation failed".into())
289  }
290}
291
292#[allow(non_snake_case)]
293/// The `recover` function attempts to recover a secret shared value
294/// from a set of shares that meet the threshold requirements.
295pub fn recover<'a, T>(shares: T) -> Result<Commune, Box<dyn Error>>
296where
297  T: IntoIterator<Item = &'a Share>,
298  T::IntoIter: Iterator<Item = &'a Share>,
299{
300  let mut shares = shares.into_iter().peekable();
301  let s = &(*shares.peek().ok_or("no shares passed")?).clone();
302  let shares: Vec<star_sharks::Share> = shares.cloned().map(|s| s.S).collect();
303  let key = Sharks::from(s.A.clone()).recover(&shares)?;
304  let K = key[..16].to_vec();
305
306  let mut key = Strobe::new(b"adss encrypt", SecParam::B128);
307  key.key(&K, false);
308
309  // M is the message
310  let mut M: Vec<u8> = vec![0; s.C.len()];
311  M.copy_from_slice(&s.C);
312  key.recv_enc(&mut M, false);
313
314  // R are the "random coins"
315  let mut R: Vec<u8> = vec![0; s.D.len()];
316  R.copy_from_slice(&s.D);
317  key.recv_enc(&mut R, false);
318
319  let c = Commune {
320    A: s.A.clone(),
321    M,
322    R,
323    T: None,
324  };
325
326  c.verify(&s.J.clone())?;
327  Ok(c)
328}
329
330#[cfg(test)]
331mod tests {
332  use core::iter;
333
334  use crate::*;
335
336  #[test]
337  fn serialization_u32() {
338    for &i in &[0, 10, 100, u32::MAX] {
339      let mut out: Vec<u8> = Vec::new();
340      store_u32(i, &mut out);
341      assert_eq!(load_u32(out.as_slice()), Some(i));
342    }
343  }
344
345  #[test]
346  fn serialization_empty_bytes() {
347    let mut out: Vec<u8> = Vec::new();
348    store_bytes(Vec::new().as_slice(), &mut out);
349    assert_eq!(load_bytes(out.as_slice()), Some(&[] as &[u8]));
350  }
351
352  #[test]
353  fn serialization_bytes() {
354    let mut out: Vec<u8> = Vec::new();
355    let bytes: &[u8] = &[0, 1, 10, 100];
356    store_bytes(bytes, &mut out);
357    assert_eq!(load_bytes(out.as_slice()), Some(bytes));
358  }
359
360  #[test]
361  fn serialization_access_structure() {
362    for i in &[0, 10, 100, u32::MAX] {
363      let a = AccessStructure { threshold: *i };
364      let s: &[u8] = &a.to_bytes()[..];
365      assert_eq!(AccessStructure::from_bytes(s), Some(a));
366    }
367  }
368
369  #[test]
370  fn serialization_share() {
371    let c = Commune {
372      A: AccessStructure { threshold: 50 },
373      M: vec![1, 2, 3, 4],
374      R: vec![5, 6, 7, 8],
375      T: None,
376    };
377
378    for share in iter::repeat_with(|| c.clone().share()).take(150) {
379      let share = share.unwrap();
380      let s = share.to_bytes();
381      assert_eq!(Share::from_bytes(s.as_slice()), Some(share));
382
383      // Test shares that are erroneously truncated
384      assert_eq!(Share::from_bytes(&s[..s.len() - 7]), None);
385    }
386  }
387
388  #[test]
389  fn it_works() {
390    let c = Commune {
391      A: AccessStructure { threshold: 50 },
392      M: vec![1, 2, 3, 4],
393      R: vec![5, 6, 7, 8],
394      T: None,
395    };
396
397    let shares: Vec<Share> = iter::repeat_with(|| c.clone().share().unwrap())
398      .take(150)
399      .collect();
400
401    let recovered = recover(&shares).unwrap();
402
403    assert_eq!(c.M, recovered.M);
404  }
405}