diff --git a/benches/bench.rs b/benches/bench.rs index 7974c77..36feaf1 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -19,8 +19,8 @@ mod poly_benches { "Polynomial multiplication", move |b, &°| { let rand_factors = || { - let lhs = Poly::random(deg, &mut rng).unwrap(); - let rhs = Poly::random(deg, &mut rng).unwrap(); + let lhs = Poly::random(deg, &mut rng); + let rhs = Poly::random(deg, &mut rng); (lhs, rhs) }; b.iter_with_setup(rand_factors, |(lhs, rhs)| &lhs * &rhs) @@ -36,7 +36,7 @@ mod poly_benches { "Polynomial interpolation", move |b, &°| { let rand_samples = || (0..=deg).map(|i| (i, rng.gen::())).collect::>(); - b.iter_with_setup(rand_samples, |samples| Poly::interpolate(samples).unwrap()) + b.iter_with_setup(rand_samples, Poly::interpolate) }, &[5, 10, 20, 40], ); diff --git a/examples/threshold_enc.rs b/examples/threshold_enc.rs index 5946672..ba9f183 100644 --- a/examples/threshold_enc.rs +++ b/examples/threshold_enc.rs @@ -27,12 +27,12 @@ impl SecretSociety { // decrypt a message must exceed this `threshold`. fn new(n_actors: usize, threshold: usize) -> Self { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(threshold, &mut rng).unwrap(); + let sk_set = SecretKeySet::random(threshold, &mut rng); let pk_set = sk_set.public_keys(); let actors = (0..n_actors) .map(|id| { - let sk_share = sk_set.secret_key_share(id).unwrap(); + let sk_share = sk_set.secret_key_share(id); let pk_share = pk_set.public_key_share(id); Actor::new(id, sk_share, pk_share) }).collect(); diff --git a/examples/threshold_sig.rs b/examples/threshold_sig.rs index aed25d0..4e392d1 100644 --- a/examples/threshold_sig.rs +++ b/examples/threshold_sig.rs @@ -45,12 +45,12 @@ impl ChatNetwork { // before it can be added to the `chat_log`. fn new(n_nodes: usize, threshold: usize) -> Self { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(threshold, &mut rng).unwrap(); + let sk_set = SecretKeySet::random(threshold, &mut rng); let pk_set = sk_set.public_keys(); let nodes = (0..n_nodes) .map(|id| { - let sk_share = sk_set.secret_key_share(id).unwrap(); + let sk_share = sk_set.secret_key_share(id); let pk_share = pk_set.public_key_share(id); Node::new(id, sk_share, pk_share) }).collect(); diff --git a/src/lib.rs b/src/lib.rs index 95e0afc..e0ad902 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,8 @@ pub mod serde_impl; use std::env; use std::fmt; use std::hash::{Hash, Hasher}; -use std::mem::size_of_val; -use std::ptr::{copy_nonoverlapping, write_volatile}; +use std::mem::{size_of, size_of_val}; +use std::ptr::copy_nonoverlapping; use byteorder::{BigEndian, ByteOrder}; use errno::errno; @@ -62,6 +62,14 @@ lazy_static! { Ok(s) => s.parse().unwrap_or(true), _ => true, }; + + // The size in bytes of a single `Fr` field element. + static ref FR_SIZE: usize = size_of::(); +} + +// Overwrites a single field element with zeros. +pub(crate) fn clear_fr(fr_ptr: *mut u8) { + unsafe { memzero(fr_ptr, *FR_SIZE) }; } /// Marks a type as containing one or more secret prime field elements. @@ -256,55 +264,50 @@ pub struct SecretKey(Box); /// /// # Panics /// -/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of -/// `SecretKey`.` +/// Panics if we have reached the system's locked memory limit when locking the secret field +/// element in RAM. impl Default for SecretKey { fn default() -> Self { let mut fr = Fr::zero(); - match SecretKey::from_mut_ptr(&mut fr as *mut Fr) { - Ok(sk) => sk, - Err(e) => panic!("Failed to create default `SecretKey`: {}", e), - } + SecretKey::try_from_mut(&mut fr) + .unwrap_or_else(|e| panic!("Failed to create default `SecretKey`: {}", e)) } } -/// Creates a random `SecretKey`. +/// Creates a random `SecretKey` from a given RNG. If you do not need to specify your own RNG, you +/// should use `SecretKey::random()` or `SecretKey::try_random()` as your constructor instead. /// /// # Panics /// -/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of -/// `SecretKey`. +/// Panics if we have reached the system's locked memory limit when locking the secret field +/// element in RAM. impl Rand for SecretKey { fn rand(rng: &mut R) -> Self { let mut fr = Fr::rand(rng); - match SecretKey::from_mut_ptr(&mut fr as *mut Fr) { - Ok(sk) => sk, - Err(e) => panic!("Failed to create random `SecretKey`: {}", e), - } + SecretKey::try_from_mut(&mut fr) + .unwrap_or_else(|e| panic!("Failed to create random `SecretKey`: {}", e)) } } -/// Creates a new `SecretKey` by cloning another key's prime field element. +/// Creates a new `SecretKey` by cloning another `SecretKey`'s prime field element. /// /// # Panics /// -/// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of -/// `SecretKey`. +/// Panics if we have reached the system's locked memory limit when locking the secret field +/// element into RAM. impl Clone for SecretKey { fn clone(&self) -> Self { let mut fr = *self.0; - match SecretKey::from_mut_ptr(&mut fr as *mut Fr) { - Ok(sk) => sk, - Err(e) => panic!("Failed to clone a new `SecretKey`: {}", e), - } + SecretKey::try_from_mut(&mut fr) + .unwrap_or_else(|e| panic!("Failed to clone `SecretKey`: {}", e)) } } -// A volatile overwrite of the prime field element's memory. +// Zeroes out and unlocks the memory allocated from the `SecretKey`'s field element. // // # Panics // -// Panics if we were unable to `munlock` the prime field element memory after it has been cleared. +// Panics if we fail to unlock the memory containing the field element. impl Drop for SecretKey { fn drop(&mut self) { self.zero_secret_memory(); @@ -362,52 +365,100 @@ impl ContainsSecret for SecretKey { fn zero_secret_memory(&self) { let ptr = &*self.0 as *const Fr as *mut u8; - let n_bytes = size_of_val(&*self.0); - unsafe { - memzero(ptr, n_bytes); - } + clear_fr(ptr); } } impl SecretKey { - /// Creates a new `SecretKey` given a mutable raw pointer to a prime - /// field element. This constructor takes a pointer to avoid any - /// unnecessary stack copying/moving of secrets. The field element will - /// be copied bytewise onto the heap, the resulting `Box` is then - /// stored in the `SecretKey`. + /// Creates a new `SecretKey` from a mutable reference to a field element. This constructor + /// takes a reference to avoid any unnecessary stack copying/moving of secrets (i.e. the field + /// element). The field element is copied bytewise onto the heap, the resulting `Box` is + /// stored in the returned `SecretKey`. + /// + /// This constructor is identical to `SecretKey::try_from_mut()` in every way except that this + /// constructor will panic if locking memory into RAM fails, whereas + /// `SecretKey::try_from_mut()` returns an `Err`. /// - /// *WARNING* this constructor will overwrite the pointed to `Fr` element - /// with zeros after it has been copied onto the heap. + /// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it + /// has been copied onto the heap. + /// + /// # Panics + /// + /// Panics if we reach the system's locked memory limit when locking the secret field element + /// into RAM. + pub fn from_mut(fr: &mut Fr) -> Self { + SecretKey::try_from_mut(fr) + .unwrap_or_else(|e| panic!("Falied to create `SecretKey`: {}", e)) + } + + /// Creates a new `SecretKey` from a mutable reference to a field element. This constructor + /// takes a reference to avoid any unnecessary stack copying/moving of secrets (i.e. the field + /// element). The field element is copied bytewise onto the heap, the resulting `Box` is + /// stored in the returned `SecretKey`. + /// + /// This constructor is identical to `SecretKey::from_mut()` in every way except that this + /// constructor will return an `Err` if locking memory into RAM fails, whereas + /// `SecretKey::from_mut()` will panic. + /// + /// *WARNING* this constructor will overwrite the referenced `Fr` element with zeros after it + /// has been copied onto the heap. /// /// # Errors /// - /// Returns an `Error::MlockFailed` if we have reached the systems's - /// locked memory limit. - #[cfg_attr(feature = "cargo-clippy", allow(not_unsafe_ptr_arg_deref))] - pub fn from_mut_ptr(fr_ptr: *mut Fr) -> Result { + /// Returns an `Error::MlockFailed` if we reached the system's locked memory limit when locking + /// the secret field element into RAM. + pub fn try_from_mut(fr: &mut Fr) -> Result { + let fr_ptr = fr as *mut Fr; let mut boxed_fr = Box::new(Fr::zero()); unsafe { copy_nonoverlapping(fr_ptr, &mut *boxed_fr as *mut Fr, 1); - write_volatile(fr_ptr, Fr::zero()); } + clear_fr(fr_ptr as *mut u8); let sk = SecretKey(boxed_fr); sk.mlock_secret_memory()?; Ok(sk) } - /// Creates a new random instance of `SecretKey`. This is used - /// as a wrapper around: `let sk: SecretKey = rand::random();`. + /// Creates a new random instance of `SecretKey`. If you want to use/define your own random + /// number generator, you should use the constructor: `SecretKey::rand()`. If you do not need + /// to specify your own RNG, you should use the `SecretKey::random()` and + /// `SecretKey::try_random()` constructors, which use + /// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as + /// their RNG. + /// + /// This constructor panics if it is unable to lock `SecretKey` memory into RAM, otherwise it + /// is identical to the constructor: `SecretKey::try_random()` (which instead of panicing + /// returns an `Err`). /// /// # Panics /// - /// Panics if we have hit the system's locked memory limit when - /// `mlock`ing the new instance of `SecretKey`. + /// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of + /// `SecretKey`. pub fn random() -> Self { - use rand::thread_rng; - let mut rng = thread_rng(); + let mut rng = rand::thread_rng(); SecretKey::rand(&mut rng) } + /// Creates a new random instance of `SecretKey`. If you want to use/define your own random + /// number generator, you should use the constructor: `SecretKey::rand()`. If you do not need + /// to specify your own RNG, you should use the `SecretKey::random()` and + /// `SecretKey::try_random()` constructors, which use + /// [`rand::thead_rng()`](https://docs.rs/rand/0.4.3/rand/fn.thread_rng.html) internally as + /// their RNG. + /// + /// This constructor returns an `Err` if it is unable to lock `SecretKey` memory into RAM, + /// otherwise it is identical to the constructor: `SecretKey::random()` (which will panic + /// instead of returning an `Err`). + /// + /// # Errors + /// + /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. + pub fn try_random() -> Result { + let mut rng = rand::thread_rng(); + let mut fr = Fr::rand(&mut rng); + SecretKey::try_from_mut(&mut fr) + } + /// Returns the matching public key. pub fn public_key(&self) -> PublicKey { PublicKey(G1Affine::one().mul(*self.0)) @@ -454,21 +505,48 @@ impl fmt::Debug for SecretKeyShare { } impl SecretKeyShare { - /// Creates a secret key share from an existing value. This constructor - /// takes a pointer to avoid any unnecessary stack copying/moving of - /// secrets. The field element will be copied bytewise onto the heap, - /// the resulting `Box` is then stored in the `SecretKey` which is then - /// wrapped in a `SecretKeyShare`. + /// Creates a new `SecretKeyShare` from a mutable reference to a field element. This + /// constructor takes a reference to avoid any unnecessary stack copying/moving of secrets + /// field elements. The field element will be copied bytewise onto the heap, the resulting + /// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`. /// - /// *WARNING* this constructor will overwrite the pointed to `Fr` element - /// with zeros once it has been copied into a new `SecretKeyShare`. + /// This constructor is identical to `SecretKeyShare::try_from_mut()` in every way except that + /// this constructor will panic if locking memory into RAM fails, whereas + /// `SecretKeyShare::try_from_mut()` will return an `Err`. + /// + /// *WARNING* this constructor will overwrite the pointed to `Fr` element with zeros once it + /// has been copied into a new `SecretKeyShare`. + /// + /// # Panics + /// + /// Panics if we reach the systems locked memory limit. + pub fn from_mut(fr: &mut Fr) -> Self { + match SecretKey::try_from_mut(fr) { + Ok(sk) => SecretKeyShare(sk), + Err(e) => panic!( + "Failed to create `SecretKeyShare` from field element: {}", + e + ), + } + } + + /// Creates a new `SecretKeyShare` from a mutable reference to a field element. This + /// constructor takes a reference to avoid any unnecessary stack copying/moving of secrets + /// field elements. The field element will be copied bytewise onto the heap, the resulting + /// `Box` is stored in the `SecretKey` which is then wrapped in a `SecretKeyShare`. + /// + /// This constructor is identical to `SecretKeyShare::from_mut()` in every way except that this + /// constructor will return an `Err` if locking memory into RAM fails, whereas + /// `SecretKeyShare::from_mut()` will panic. + /// + /// *WARNING* this constructor will overwrite the pointed to `Fr` element with zeros once it + /// has been copied into a new `SecretKeyShare`. /// /// # Errors /// - /// Returns an `Error::MlockFailed` if we have reached the systems's - /// locked memory limit. - pub fn from_mut_ptr(fr_ptr: *mut Fr) -> Result { - SecretKey::from_mut_ptr(fr_ptr).map(SecretKeyShare) + /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. + pub fn try_from_mut(fr: &mut Fr) -> Result { + SecretKey::try_from_mut(fr).map(SecretKeyShare) } /// Returns the matching public key share. @@ -621,10 +699,26 @@ impl From for SecretKeySet { impl SecretKeySet { /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively - /// sign and decrypt. - pub fn random(threshold: usize, rng: &mut R) -> Result { - let poly = Poly::random(threshold, rng)?; - Ok(SecretKeySet { poly }) + /// sign and decrypt. This constuctor is identical to the `SecretKey::try_random()` in every + /// way except that this constructor panics if locking secret values into RAM fails. + /// + /// # Panics + /// + /// Panics if we reach the system's locked memory limit. + pub fn random(threshold: usize, rng: &mut R) -> Self { + SecretKeySet::try_random(threshold, rng) + .unwrap_or_else(|e| panic!("Failed to create random `SecretKeySet`: {}", e)) + } + + /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively + /// sign and decrypt. This constuctor is identical to the `SecretKey::random()` in every + /// way except that this constructor return an `Err` if locking secret values into RAM fails. + /// + /// # Errors + /// + /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. + pub fn try_random(threshold: usize, rng: &mut R) -> Result { + Poly::try_random(threshold, rng).map(SecretKeySet::from) } /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full @@ -633,10 +727,30 @@ impl SecretKeySet { self.poly.degree() } - /// Returns the `i`-th secret key share. - pub fn secret_key_share(&self, i: T) -> Result { + /// Returns the `i`-th secret key share. This method is identical to the + /// `.try_secret_key_share()` in every way except that this method panics if + /// locking secret values into memory fails, whereas `.try_secret_key_share()` + /// returns an `Err`. + /// + /// # Panics + /// + /// Panics if we reach the system's locked memory limit. + pub fn secret_key_share(&self, i: T) -> SecretKeyShare { + self.try_secret_key_share(i) + .unwrap_or_else(|e| panic!("Failed to create `SecretKeyShare`: {}", e)) + } + + /// Returns the `i`-th secret key share. This method is identical to the method + /// `.secret_key_share()` in every way except that this method returns an `Err` if + /// locking secret values into memory fails, whereas `.secret_key_share()` will + /// panic. + /// + /// # Errors + /// + /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. + pub fn try_secret_key_share(&self, i: T) -> Result { let mut fr = self.poly.evaluate(into_fr_plus_1(i)); - SecretKeyShare::from_mut_ptr(&mut fr as *mut Fr) + SecretKeyShare::try_from_mut(&mut fr) } /// Returns the corresponding public key set. That information can be shared publicly. @@ -646,11 +760,16 @@ impl SecretKeySet { } } - /// Returns the secret master key. + /// Returns the secret master key. Panics if mlocking fails. + /// + /// # Panics + /// + /// Panics if we have hit the system's locked memory limit when `mlock`ing the new instance of + /// `SecretKey`. #[cfg(test)] - fn secret_key(&self) -> Result { + fn secret_key(&self) -> SecretKey { let mut fr = self.poly.evaluate(0); - SecretKey::from_mut_ptr(&mut fr as *mut Fr) + SecretKey::from_mut(&mut fr) } } @@ -757,7 +876,7 @@ mod tests { #[test] fn test_threshold_sig() { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(3, &mut rng).expect("Failed to create `SecretKeySet`"); + let sk_set = SecretKeySet::random(3, &mut rng); let pk_set = sk_set.public_keys(); let pk_master = pk_set.public_key(); @@ -767,21 +886,10 @@ mod tests { assert_ne!(pk_master, pk_set.public_key_share(2).0); // Make sure we don't hand out the main secret key to anyone. - let sk_master = sk_set - .secret_key() - .expect("Failed to create master `SecretKey`"); - let sk_share_0 = sk_set - .secret_key_share(0) - .expect("Failed to create first `SecretKeyShare`") - .0; - let sk_share_1 = sk_set - .secret_key_share(1) - .expect("Failed to create second `SecretKeyShare`") - .0; - let sk_share_2 = sk_set - .secret_key_share(2) - .expect("Failed to create third `SecretKeyShare`") - .0; + let sk_master = sk_set.secret_key(); + let sk_share_0 = sk_set.secret_key_share(0).0; + let sk_share_1 = sk_set.secret_key_share(1).0; + let sk_share_2 = sk_set.secret_key_share(2).0; assert_ne!(sk_master, sk_share_0); assert_ne!(sk_master, sk_share_1); assert_ne!(sk_master, sk_share_2); @@ -792,10 +900,7 @@ mod tests { let sigs: BTreeMap<_, _> = [5, 8, 7, 10] .iter() .map(|&i| { - let sig = sk_set - .secret_key_share(i) - .unwrap_or_else(|_| panic!("Failed to create `SecretKeyShare` #{}", i)) - .sign(msg); + let sig = sk_set.secret_key_share(i).sign(msg); (i, sig) }).collect(); @@ -812,10 +917,7 @@ mod tests { let sigs2: BTreeMap<_, _> = [42, 43, 44, 45] .iter() .map(|&i| { - let sig = sk_set - .secret_key_share(i) - .unwrap_or_else(|_| panic!("Failed to create `SecretKeyShare` #{}", i)) - .sign(msg); + let sig = sk_set.secret_key_share(i).sign(msg); (i, sig) }).collect(); let sig2 = pk_set.combine_signatures(&sigs2).expect("signatures match"); @@ -832,11 +934,11 @@ mod tests { assert!(ciphertext.verify()); // Bob can decrypt the message. - let decrypted = sk_bob.decrypt(&ciphertext).expect("valid ciphertext"); + let decrypted = sk_bob.decrypt(&ciphertext).expect("invalid ciphertext"); assert_eq!(msg[..], decrypted[..]); // Eve can't. - let decrypted_eve = sk_eve.decrypt(&ciphertext).expect("valid ciphertext"); + let decrypted_eve = sk_eve.decrypt(&ciphertext).expect("invalid ciphertext"); assert_ne!(msg[..], decrypted_eve[..]); // Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate. @@ -849,7 +951,7 @@ mod tests { #[test] fn test_threshold_enc() { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(3, &mut rng).expect("Failed to create to `SecretKeySet`"); + let sk_set = SecretKeySet::random(3, &mut rng); let pk_set = sk_set.public_keys(); let msg = b"Totally real news"; let ciphertext = pk_set.public_key().encrypt(&msg[..]); @@ -860,9 +962,8 @@ mod tests { .map(|&i| { let dec_share = sk_set .secret_key_share(i) - .unwrap_or_else(|_| panic!("Failed to create `SecretKeyShare` #{}", i)) .decrypt_share(&ciphertext) - .expect("ciphertext is valid"); + .expect("ciphertext is invalid"); (i, dec_share) }).collect(); diff --git a/src/poly.rs b/src/poly.rs index f893bb4..5b63358 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -19,7 +19,7 @@ use std::borrow::Borrow; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; -use std::mem::{size_of, size_of_val}; +use std::mem::size_of_val; use std::{cmp, iter, ops}; use errno::errno; @@ -28,7 +28,7 @@ use pairing::bls12_381::{Fr, G1Affine, G1}; use pairing::{CurveAffine, CurveProjective, Field}; use rand::Rng; -use super::{ContainsSecret, Error, IntoFr, Result, SHOULD_MLOCK_SECRETS}; +use super::{clear_fr, ContainsSecret, Error, IntoFr, Result, FR_SIZE, SHOULD_MLOCK_SECRETS}; /// A univariate polynomial in the prime field. #[derive(Serialize, Deserialize, PartialEq, Eq)] @@ -46,10 +46,8 @@ pub struct Poly { /// `Poly`. impl Clone for Poly { fn clone(&self) -> Self { - match Poly::new(self.coeff.clone()) { - Ok(poly) => poly, - Err(e) => panic!("Failed to clone `Poly`: {}", e), - } + Poly::try_from(self.coeff.clone()) + .unwrap_or_else(|e| panic!("Failed to clone `Poly`: {}", e)) } } @@ -213,7 +211,7 @@ impl<'a, B: Borrow> ops::Mul for &'a Poly { fn mul(self, rhs: B) -> Self::Output { let rhs = rhs.borrow(); if rhs.coeff.is_empty() || self.coeff.is_empty() { - return Poly::zero().expect("failed to create zero Poly"); + return Poly::zero(); } let mut coeff = vec![Fr::zero(); self.coeff.len() + rhs.borrow().coeff.len() - 1]; let mut s; // TODO: Mlock and zero on drop. @@ -224,9 +222,8 @@ impl<'a, B: Borrow> ops::Mul for &'a Poly { coeff[i + j].add_assign(&s); } } - Poly::new(coeff).unwrap_or_else(|e| { - panic!("Failed to create a new `Poly` during muliplication: {}", e); - }) + Poly::try_from(coeff) + .unwrap_or_else(|e| panic!("Failed to create a new `Poly` during muliplication: {}", e)) } } @@ -247,7 +244,7 @@ impl> ops::MulAssign for Poly { impl ops::MulAssign for Poly { fn mul_assign(&mut self, rhs: Fr) { if rhs.is_zero() { - *self = Poly::zero().expect("failed to create zero Poly"); + *self = Poly::zero(); } else { for c in &mut self.coeff { c.mul_assign(&rhs); @@ -322,6 +319,19 @@ impl Drop for Poly { } } +/// Creates a new `Poly` instance from a vector of prime field elements representing the +/// coefficients of the polynomial. We lock the region of the heap where the polynomial +/// coefficients are allocated. +/// +/// # Panics +/// +/// Panics if we have reached the system's locked memory limit. +impl From> for Poly { + fn from(coeffs: Vec) -> Self { + Poly::try_from(coeffs).unwrap_or_else(|e| panic!("Failed to create `Poly`: {}", e)) + } +} + impl ContainsSecret for Poly { fn mlock_secret_memory(&self) -> Result<()> { if !*SHOULD_MLOCK_SECRETS { @@ -378,80 +388,167 @@ impl ContainsSecret for Poly { impl Poly { /// Creates a new `Poly` instance from a vector of prime field elements representing the - /// coefficients of the polynomial. The `mlock` system call is applied to the region of the - /// heap where the field elements are allocated. + /// coefficients of the polynomial. We lock the region of the heap where the polynomial + /// coefficients are allocated. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn new(coeff: Vec) -> Result { + pub fn try_from(coeff: Vec) -> Result { let poly = Poly { coeff }; poly.mlock_secret_memory()?; Ok(poly) } - /// Creates a random polynomial. + /// Creates a random polynomial. This constructor is identical to the `Poly::try_random()` + /// constructor in every way except that this constructor will panic if locking the polynomial + /// coefficients into RAM fails. + /// + /// # Panics + /// + /// Panics if we have reached the system's locked memory limit. + pub fn random(degree: usize, rng: &mut R) -> Self { + Poly::try_random(degree, rng) + .unwrap_or_else(|e| panic!("Failed to create random `Poly`: {}", e)) + } + + /// Creates a random polynomial. This constructor is identical to the `Poly::random()` + /// constructor in every way except that this constructor will return an `Err` if locking the + /// polynomial coefficients into RAM fails. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn random(degree: usize, rng: &mut R) -> Result { + pub fn try_random(degree: usize, rng: &mut R) -> Result { let coeff: Vec = (0..=degree).map(|_| rng.gen()).collect(); - Poly::new(coeff) + Poly::try_from(coeff) } /// Returns the polynomial with constant value `0`. /// - /// # Errors + /// This constructor does not return a `Result` because the polynomial's `coeff` vector is + /// empty, which does not require memory locking. Memory locking will occur when the first + /// coefficient is added to the `coeff` vector. + pub fn zero() -> Self { + Poly { coeff: vec![] } + } + + /// Returns the polynomial with constant value `1`. This constructor is identical to + /// `Poly::try_one()` in every way except that this constructor panics if locking the `coeff` + /// vector into RAM fails, whereas `Poly::try_one()` returns an `Err`. /// - /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn zero() -> Result { - Poly::new(vec![]) + /// # Panics + /// + /// Panics if we have reached the system's locked memory limit. + pub fn one() -> Self { + Poly::try_one() + .unwrap_or_else(|e| panic!("Failed to create constant `Poly` of value 1: {}", e)) } - /// Returns the polynomial with constant value `1`. + /// Returns the polynomial with constant value `1`. This constructor is identical to + /// `Poly::one()` in every way except that this constructor returns `Err` if locking the + /// `coeff` vector into RAM fails, whereas `Poly::one()` panics. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn one() -> Result { - Self::monomial(0) + pub fn try_one() -> Result { + Poly::try_constant(Fr::one()) + } + + /// Returns the polynomial with constant value `c`. Panics if memory locking fails. + /// + /// # Panics + /// + /// Panics if we have reached the systems's locked memory limit. + pub fn constant(c: Fr) -> Self { + // We create a raw pointer to the field element within this method's stack frame so we can + // overwrite that portion of memory with zeros once we have copied the element onto the + // heap as part of the vector of polynomial coefficients. + let fr_ptr = &c as *const Fr as *mut u8; + let poly = Poly::try_from(vec![c]) + .unwrap_or_else(|e| panic!("Failed to create constant `Poly`: {}", e)); + clear_fr(fr_ptr); + poly } - /// Returns the polynomial with constant value `c`. + /// Returns the polynomial with constant value `c`. This constructor is identical to + /// `Poly::constant()` in every way except that this constructor returns an `Err` if locking + /// the polynomial coefficients into RAM fails. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn constant(c: Fr) -> Result { - let ptr = &c as *const Fr as *mut u8; - let res = Poly::new(vec![c]); - unsafe { - memzero(ptr, size_of::()); - } + pub fn try_constant(c: Fr) -> Result { + // We create a raw pointer to the field element within this method's stack frame so we can + // overwrite that portion of memory with zeros once we have copied the element onto the + // heap as part of polynomials `coeff` vector. + let fr_ptr = &c as *const Fr as *mut u8; + let res = Poly::try_from(vec![c]); + clear_fr(fr_ptr); res } - /// Returns the identity function, i.e. the polynomial "`x`". + /// Returns the identity function, i.e. the polynomial "`x`". Panics if mlocking fails. + /// + /// # Panics + /// + /// Panics if we have reached the system's locked memory limit. + pub fn identity() -> Self { + Poly::monomial(1) + } + + /// Returns the identity function, i.e. the polynomial `x`. Returns an `Err` if mlocking + /// fails. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn identity() -> Result { - Self::monomial(1) + pub fn try_identity() -> Result { + Poly::try_monomial(1) } - /// Returns the (monic) monomial "`x.pow(degree)`" + /// Returns the (monic) monomial: `x.pow(degree)`. Panics if mlocking fails. + /// + /// # Panics + /// + /// Panics if we have reached the systems's locked memory limit. + pub fn monomial(degree: usize) -> Self { + Poly::try_monomial(degree).unwrap_or_else(|e| { + panic!( + "Failed to create monomial `Poly` of degree {}: {}", + degree, e + ) + }) + } + + /// Returns the (monic) monomial: `x.pow(degree)`. Returns an `Err` if mlocking fails. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn monomial(degree: usize) -> Result { + pub fn try_monomial(degree: usize) -> Result { let coeff: Vec = iter::repeat(Fr::zero()) .take(degree) .chain(iter::once(Fr::one())) .collect(); - Poly::new(coeff) + Poly::try_from(coeff) + } + + /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values + /// `(x, f(x))`. + /// + /// # Panics + /// + /// Panics if we have reached the systems's locked memory limit. + pub fn interpolate(samples_repr: I) -> Self + where + I: IntoIterator, + T: IntoFr, + U: IntoFr, + { + Poly::try_interpolate(samples_repr) + .unwrap_or_else(|e| panic!("Failed to interpolate `Poly`: {}", e)) } /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values @@ -460,7 +557,7 @@ impl Poly { /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn interpolate(samples_repr: I) -> Result + pub fn try_interpolate(samples_repr: I) -> Result where I: IntoIterator, T: IntoFr, @@ -468,7 +565,7 @@ impl Poly { { let convert = |(x, y): (T, U)| (x.into_fr(), y.into_fr()); let samples: Vec<(Fr, Fr)> = samples_repr.into_iter().map(convert).collect(); - Self::compute_interpolation(&samples) + Poly::compute_interpolation(&samples) } /// Returns the degree. @@ -516,19 +613,17 @@ impl Poly { /// /// # Errors /// - /// Returns an `Error::MlockFailed` if we hit the system's locked memory limit and failed to - /// `mlock` the new `Poly` instance. + /// Returns an `Error::MlockFailed` if we hit the system's locked memory limit. fn compute_interpolation(samples: &[(Fr, Fr)]) -> Result { - let mut poly; // Interpolates on the first `i` samples. - let mut base; // Is zero on the first `i` samples. if samples.is_empty() { - return Poly::zero(); - } else { - poly = Poly::constant(samples[0].1)?; - let mut minus_s0 = samples[0].0; - minus_s0.negate(); - base = Poly::new(vec![minus_s0, Fr::one()])?; + return Ok(Poly::zero()); } + // Interpolates on the first `i` samples. + let mut poly = Poly::try_constant(samples[0].1)?; + let mut minus_s0 = samples[0].0; + minus_s0.negate(); + // Is zero on the first `i` samples. + let mut base = Poly::try_from(vec![minus_s0, Fr::one()])?; // We update `base` so that it is always zero on all previous samples, and `poly` so that // it has the correct values on the previous samples. @@ -545,7 +640,7 @@ impl Poly { // Finally, multiply `base` by X - x, so that it is zero at `x`, too, now. let mut minus_x = *x; minus_x.negate(); - base *= Poly::new(vec![minus_x, Fr::one()])?; + base *= Poly::try_from(vec![minus_x, Fr::one()])?; } Ok(poly) } @@ -555,7 +650,7 @@ impl Poly { if !*SHOULD_MLOCK_SECRETS { return Ok(()); } - let n_bytes_truncated = len * size_of::(); + let n_bytes_truncated = *FR_SIZE * len; if n_bytes_truncated == 0 { return Ok(()); } @@ -580,7 +675,7 @@ impl Poly { if !*SHOULD_MLOCK_SECRETS { return Ok(()); } - let n_bytes_extended = len * size_of::(); + let n_bytes_extended = *FR_SIZE * len; if n_bytes_extended == 0 { return Ok(()); } @@ -788,12 +883,26 @@ impl ContainsSecret for BivarPoly { } impl BivarPoly { + /// Creates a random polynomial. + /// + /// # Panics + /// + /// Panics if we have hit the system's locked memory limit. + pub fn random(degree: usize, rng: &mut R) -> Self { + BivarPoly::try_random(degree, rng).unwrap_or_else(|e| { + panic!( + "Failed to create random `BivarPoly` of degree {}: {}", + degree, e + ) + }) + } + /// Creates a random polynomial. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. - pub fn random(degree: usize, rng: &mut R) -> Result { + pub fn try_random(degree: usize, rng: &mut R) -> Result { let poly = BivarPoly { degree, coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(), @@ -824,13 +933,23 @@ impl BivarPoly { result } + /// Returns the `x`-th row, as a univariate polynomial. + /// + /// # Panics + /// + /// Panics if we have reached the system's locked memory limit. + pub fn row(&self, x: T) -> Poly { + self.try_row(x) + .unwrap_or_else(|e| panic!("Failed to create `Poly` from row of `BivarPoly: {}`", e)) + } + /// Returns the `x`-th row, as a univariate polynomial. /// /// # Errors /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit when /// creating the new `Poly` instance. - pub fn row(&self, x: T) -> Result { + pub fn try_row(&self, x: T) -> Result { let x_pow = self.powers(x); let coeff: Vec = (0..=self.degree) .map(|i| { @@ -843,7 +962,7 @@ impl BivarPoly { } result }).collect(); - Poly::new(coeff) + Poly::try_from(coeff) } /// Returns the corresponding commitment. That information can be shared publicly. @@ -985,8 +1104,8 @@ mod tests { #[test] fn poly() { // The polynomial 5 X³ + X - 2. - let x_pow_3 = Poly::monomial(3).expect("Failed to create monic polynomial of degree 3"); - let x_pow_1 = Poly::monomial(1).expect("Failed to create monic polynomial of degree 1"); + let x_pow_3 = Poly::monomial(3); + let x_pow_1 = Poly::monomial(1); let poly = x_pow_3 * 5 + x_pow_1 - 2; let coeff: Vec<_> = [-2, 1, 0, 5].into_iter().map(IntoFr::into_fr).collect(); @@ -995,7 +1114,7 @@ mod tests { for &(x, y) in &samples { assert_eq!(y.into_fr(), poly.evaluate(x)); } - let interp = Poly::interpolate(samples).expect("Failed to interpolate `Poly`"); + let interp = Poly::interpolate(samples); assert_eq!(interp, poly); } @@ -1010,10 +1129,8 @@ mod tests { // generates random bivariate polynomials and publicly commits to them. In partice, the // dealers can e.g. be any `faulty_num + 1` nodes. let bi_polys: Vec = (0..dealer_num) - .map(|_| { - BivarPoly::random(faulty_num, &mut rng) - .expect("Failed to create random `BivarPoly`") - }).collect(); + .map(|_| BivarPoly::random(faulty_num, &mut rng)) + .collect(); let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect(); let mut sec_keys = vec![Fr::zero(); node_num]; @@ -1024,9 +1141,7 @@ mod tests { for (bi_poly, bi_commit) in bi_polys.iter().zip(&pub_bi_commits) { for m in 1..=node_num { // Node `m` receives its row and verifies it. - let row_poly = bi_poly - .row(m) - .unwrap_or_else(|_| panic!("Failed to create row #{}", m)); + let row_poly = bi_poly.row(m); let row_commit = bi_commit.row(m); assert_eq!(row_poly.commitment(), row_commit); // Node `s` receives the `s`-th value and verifies it. @@ -1039,10 +1154,8 @@ mod tests { } // A cheating dealer who modified the polynomial would be detected. - let x_pow_2 = - Poly::monomial(2).expect("Failed to create monic polynomial of degree 2"); - let five = Poly::constant(5.into_fr()) - .expect("Failed to create polynomial with constant 5"); + let x_pow_2 = Poly::monomial(2); + let five = Poly::constant(5.into_fr()); let wrong_poly = row_poly.clone() + x_pow_2 * five; assert_ne!(wrong_poly.commitment(), row_commit); @@ -1057,8 +1170,7 @@ mod tests { .iter() .map(|&i| (i, bi_poly.evaluate(m, i))) .collect(); - let my_row = - Poly::interpolate(received).expect("Failed to create `Poly` via interpolation"); + let my_row = Poly::interpolate(received); assert_eq!(bi_poly.evaluate(m, 0), my_row.evaluate(0)); assert_eq!(row_poly, my_row); @@ -1073,11 +1185,9 @@ mod tests { // The whole first column never gets added up in practice, because nobody has all the // information. We do it anyway here; entry `0` is the secret key that is not known to // anyone, neither a dealer, nor a node: - let mut sec_key_set = Poly::zero().expect("Failed to create empty `Poly`"); + let mut sec_key_set = Poly::zero(); for bi_poly in &bi_polys { - sec_key_set += bi_poly - .row(0) - .expect("Failed to create `Poly` from row #0 for `BivarPoly`"); + sec_key_set += bi_poly.row(0); } for m in 1..=node_num { assert_eq!(sec_key_set.evaluate(m), sec_keys[m - 1]); @@ -1085,9 +1195,7 @@ mod tests { // The sum of the first rows of the public commitments is the commitment to the secret key // set. - let mut sum_commit = Poly::zero() - .expect("Failed to create empty `Poly`") - .commitment(); + let mut sum_commit = Poly::zero().commitment(); for bi_commit in &pub_bi_commits { sum_commit += bi_commit.row(0); }