#![forbid(unsafe_code)]
use curve25519_dalek::{EdwardsPoint, Scalar};
use oxicrypto_core::{CryptoError, Vec};
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::{
deserialize_scalar, scalar_base_mult, serialize_element, serialize_scalar, Identifier,
SCALAR_LEN,
};
#[derive(Clone)]
pub struct SecretShare {
identifier: Identifier,
value: Scalar,
}
impl SecretShare {
#[must_use]
pub fn new(identifier: Identifier, value: Scalar) -> Self {
Self { identifier, value }
}
#[must_use = "result must be checked"]
pub fn from_bytes(identifier: Identifier, value_bytes: &[u8]) -> Result<Self, CryptoError> {
Ok(Self {
identifier,
value: deserialize_scalar(value_bytes)?,
})
}
#[must_use]
pub fn identifier(&self) -> Identifier {
self.identifier
}
#[must_use]
pub fn value(&self) -> Scalar {
self.value
}
#[must_use]
pub fn to_bytes(&self) -> [u8; SCALAR_LEN] {
serialize_scalar(&self.value)
}
#[must_use]
pub fn public_share(&self) -> EdwardsPoint {
scalar_base_mult(&self.value)
}
}
impl Drop for SecretShare {
fn drop(&mut self) {
self.value.zeroize();
}
}
impl ZeroizeOnDrop for SecretShare {}
#[derive(Clone)]
pub struct KeyPackage {
secret_share: SecretShare,
public_share: EdwardsPoint,
group_public_key: EdwardsPoint,
}
impl KeyPackage {
#[must_use]
pub fn new(secret_share: SecretShare, group_public_key: EdwardsPoint) -> Self {
let public_share = secret_share.public_share();
Self {
secret_share,
public_share,
group_public_key,
}
}
#[must_use]
pub fn identifier(&self) -> Identifier {
self.secret_share.identifier()
}
#[must_use]
pub fn secret_share(&self) -> &SecretShare {
&self.secret_share
}
#[must_use]
pub fn public_share(&self) -> EdwardsPoint {
self.public_share
}
#[must_use]
pub fn group_public_key(&self) -> EdwardsPoint {
self.group_public_key
}
}
#[derive(Clone, Debug)]
pub struct PublicKeyPackage {
group_public_key: EdwardsPoint,
public_shares: Vec<(Identifier, EdwardsPoint)>,
}
impl PublicKeyPackage {
#[must_use]
pub fn group_public_key(&self) -> EdwardsPoint {
self.group_public_key
}
#[must_use = "result must be checked"]
pub fn group_public_key_bytes(&self) -> Result<[u8; 32], CryptoError> {
serialize_element(&self.group_public_key)
}
#[must_use]
pub fn public_shares(&self) -> &[(Identifier, EdwardsPoint)] {
&self.public_shares
}
#[must_use = "result must be checked"]
pub fn public_share(&self, identifier: Identifier) -> Result<EdwardsPoint, CryptoError> {
self.public_shares
.iter()
.find(|(id, _)| *id == identifier)
.map(|(_, pk)| *pk)
.ok_or(CryptoError::BadInput)
}
}
struct Polynomial {
coefficients: Vec<Scalar>,
}
impl Polynomial {
fn new(secret: Scalar, non_constant: &[Scalar]) -> Self {
let mut coefficients = Vec::with_capacity(non_constant.len() + 1);
coefficients.push(secret);
coefficients.extend_from_slice(non_constant);
Self { coefficients }
}
fn evaluate(&self, x: Scalar) -> Scalar {
let mut value = Scalar::ZERO;
for coeff in self.coefficients.iter().rev() {
value *= x;
value += coeff;
}
value
}
}
impl Drop for Polynomial {
fn drop(&mut self) {
for coeff in &mut self.coefficients {
coeff.zeroize();
}
}
}
fn check_params(max_participants: u16, min_participants: u16) -> Result<(), CryptoError> {
if min_participants == 0 || max_participants == 0 || min_participants > max_participants {
return Err(CryptoError::BadInput);
}
Ok(())
}
fn shard(
secret: Scalar,
polynomial: &Polynomial,
max_participants: u16,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), CryptoError> {
let group_public_key = scalar_base_mult(&secret);
let mut shares = Vec::with_capacity(usize::from(max_participants));
let mut public_shares = Vec::with_capacity(usize::from(max_participants));
for index in 1..=max_participants {
let identifier = Identifier::new(index)?;
let value = polynomial.evaluate(identifier.as_scalar());
let share = SecretShare::new(identifier, value);
public_shares.push((identifier, share.public_share()));
shares.push(share);
}
Ok((
shares,
PublicKeyPackage {
group_public_key,
public_shares,
},
))
}
#[must_use = "key material must be used"]
pub fn trusted_dealer_keygen_with_coefficients(
secret: Scalar,
non_constant_coefficients: &[Scalar],
max_participants: u16,
min_participants: u16,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), CryptoError> {
check_params(max_participants, min_participants)?;
if non_constant_coefficients.len() != usize::from(min_participants - 1) {
return Err(CryptoError::BadInput);
}
let polynomial = Polynomial::new(secret, non_constant_coefficients);
shard(secret, &polynomial, max_participants)
}
#[must_use = "key material must be used"]
pub fn trusted_dealer_keygen<R: rand_core::TryCryptoRng + ?Sized>(
rng: &mut R,
max_participants: u16,
min_participants: u16,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), CryptoError> {
check_params(max_participants, min_participants)?;
let secret = random_scalar(rng)?;
let mut non_constant = Vec::with_capacity(usize::from(min_participants - 1));
for _ in 0..(min_participants - 1) {
non_constant.push(random_scalar(rng)?);
}
let polynomial = Polynomial::new(secret, &non_constant);
let result = shard(secret, &polynomial, max_participants);
non_constant.zeroize();
result
}
fn random_scalar<R: rand_core::TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Scalar, CryptoError> {
let mut wide = [0u8; 64];
rng.try_fill_bytes(&mut wide)
.map_err(|_| CryptoError::Rng)?;
let scalar = Scalar::from_bytes_mod_order_wide(&wide);
wide.zeroize();
Ok(scalar)
}