use crypto_bigint::modular::constant_mod::ResidueParams;
use crypto_bigint::{const_residue, U256};
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::crypto::{Random32Bytes, Secp256k1Order};
use crate::errors::{ArithmeticError, Error};
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecretShare([u8; 32]);
impl From<Random32Bytes> for SecretShare {
fn from(value: Random32Bytes) -> Self {
Self(value.to_be_bytes())
}
}
impl From<U256> for SecretShare {
fn from(value: U256) -> Self {
Self(Random32Bytes::from(value).to_be_bytes())
}
}
impl SecretShare {
pub fn as_u256(&self) -> U256 {
U256::from_be_slice(&self.0)
}
pub fn to_be_bytes(&self) -> [u8; 32] {
self.0
}
}
impl TryFrom<&[u8]> for SecretShare {
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(Random32Bytes::try_from(slice)?.to_be_bytes()))
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct SigningShare([u8; 32]);
impl SigningShare {
pub fn generate() -> Self {
Self(Random32Bytes::generate().to_be_bytes())
}
pub fn to_be_bytes(&self) -> [u8; 32] {
self.0
}
}
impl From<Random32Bytes> for SigningShare {
fn from(value: Random32Bytes) -> Self {
Self(value.to_be_bytes())
}
}
impl TryFrom<&[u8]> for SigningShare {
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() == 32 {
Ok(Self(slice.try_into().map_err(|_| Error::Encoding)?))
} else {
Err(Error::Encoding)
}
}
}
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct SubShare {
x: U256,
y: U256,
}
impl SubShare {
pub fn new(x: U256, y: U256) -> Result<Self, ArithmeticError> {
if x < Secp256k1Order::MODULUS && y < Secp256k1Order::MODULUS {
Ok(Self { x, y })
} else {
Err(ArithmeticError::ModulusOverflow)
}
}
pub fn x(&self) -> U256 {
self.x
}
pub fn y(&self) -> U256 {
self.y
}
pub fn as_tuple(&self) -> (U256, U256) {
(self.x, self.y)
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SubShareInterpolator {
gradient: U256,
intercept: U256,
}
impl SubShareInterpolator {
pub fn new(point_a: &SubShare, point_b: &SubShare) -> Self {
let x_1 = point_a.x;
let y_1 = point_a.y;
let x_2 = point_b.x;
let y_2 = point_b.y;
let dy = const_residue!(y_1, Secp256k1Order) - const_residue!(y_2, Secp256k1Order);
let dx = const_residue!(x_1, Secp256k1Order) - const_residue!(x_2, Secp256k1Order);
let gradient = dy * dx.invert().0;
let intercept_mod =
const_residue!(y_1, Secp256k1Order) - (gradient * const_residue!(x_1, Secp256k1Order));
Self {
gradient: gradient.retrieve(),
intercept: intercept_mod.retrieve(),
}
}
pub fn secret(&self) -> U256 {
self.intercept
}
pub fn sub_share(&self, idx: U256) -> Result<SubShare, ArithmeticError> {
if idx < Secp256k1Order::MODULUS && U256::ZERO < idx {
let gradient = self.gradient;
let intercept = self.intercept;
let y_coord = (const_residue!(gradient, Secp256k1Order)
* const_residue!(idx, Secp256k1Order))
+ const_residue!(intercept, Secp256k1Order);
Ok(SubShare {
x: idx,
y: y_coord.retrieve(),
})
} else {
Err(ArithmeticError::ModulusOverflow)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sub_share_interpolator_works() {
let secret_share = U256::ONE;
let sub_share_0 = SubShare::new(U256::ZERO, secret_share).unwrap();
let sub_share_1 = SubShare::new(U256::ONE, U256::from(2u8)).unwrap();
let split_sub_share_interpolator = SubShareInterpolator::new(&sub_share_0, &sub_share_1);
let sub_share_2 = SubShare::new(U256::from(2u8), U256::from(3u8)).unwrap();
assert_eq!(
split_sub_share_interpolator
.sub_share(U256::from(2u8))
.unwrap()
.as_tuple(),
sub_share_2.as_tuple()
);
let reconstruct_sub_share_interpolator =
SubShareInterpolator::new(&sub_share_1, &sub_share_2);
assert_eq!(&reconstruct_sub_share_interpolator.secret(), &secret_share);
}
}