confium_tc_ecies_p256/
shamir.rs1use p256::elliptic_curve::rand_core;
4use p256::elliptic_curve::subtle::CtOption;
5use p256::elliptic_curve::{Field, PrimeField};
6use p256::{FieldBytes, Scalar};
7use sha2::{Digest as _, Sha256};
8use std::ops::{Add, Mul, Sub};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Share {
13 pub x: u32,
15 pub y: Scalar,
17}
18
19pub fn split_secret(secret: &Scalar, t: u32, n: u32) -> Vec<Share> {
21 assert!(t >= 1);
22 assert!(n >= t);
23
24 let mut coeffs: Vec<Scalar> = Vec::with_capacity(t as usize);
25 coeffs.push(*secret);
26 for _ in 1..t {
27 coeffs.push(random_scalar());
28 }
29
30 (1..=n)
31 .map(|i| {
32 let x = u32_to_scalar(i);
33 Share {
34 x: i,
35 y: evaluate_polynomial(&coeffs, &x),
36 }
37 })
38 .collect()
39}
40
41pub fn recover_secret(shares: &[&Share]) -> Result<Scalar, ShamirError> {
43 if shares.is_empty() {
44 return Err(ShamirError::InsufficientShares { have: 0, need: 1 });
45 }
46 let mut seen = std::collections::HashSet::new();
47 for s in shares {
48 if !seen.insert(s.x) {
49 return Err(ShamirError::DuplicateX(s.x));
50 }
51 }
52 let mut sum = Scalar::ZERO;
53 for s_i in shares {
54 let x_i = u32_to_scalar(s_i.x);
55 let mut numerator = Scalar::ONE;
56 let mut denominator = Scalar::ONE;
57 for s_j in shares {
58 if s_j.x == s_i.x {
59 continue;
60 }
61 let x_j = u32_to_scalar(s_j.x);
62 numerator = numerator.mul(&Scalar::ZERO.sub(&x_j));
63 denominator = denominator.mul(&x_i.sub(&x_j));
64 }
65 let denom_inv = invert(&denominator);
66 let lagrange = numerator.mul(&denom_inv);
67 let term = s_i.y.mul(&lagrange);
68 sum = sum.add(&term);
69 }
70 Ok(sum)
71}
72
73fn random_scalar() -> Scalar {
74 Scalar::random(&mut rand_core::UnwrapErr(getrandom::SysRng))
75}
76
77fn evaluate_polynomial(coeffs: &[Scalar], x: &Scalar) -> Scalar {
78 let mut result = Scalar::ZERO;
79 for c in coeffs.iter().rev() {
80 result = result.mul(x);
81 result = result.add(c);
82 }
83 result
84}
85
86fn reduce_to_scalar(mut bytes: [u8; 32]) -> Scalar {
89 loop {
90 if let Some(s) = Option::<Scalar>::from(Scalar::from_repr(FieldBytes::from(bytes))) {
91 return s;
92 }
93 let mut h = Sha256::new();
94 h.update(b"confium-scalar-reduce-v1");
95 h.update(bytes);
96 bytes = h.finalize().into();
97 }
98}
99
100fn u32_to_scalar(v: u32) -> Scalar {
101 let mut arr = [0u8; 32];
102 arr[28..32].copy_from_slice(&v.to_be_bytes());
103 reduce_to_scalar(arr)
104}
105
106fn invert(s: &Scalar) -> Scalar {
107 let ct: CtOption<Scalar> = s.invert();
110 Option::<Scalar>::from(ct).unwrap_or(Scalar::ZERO)
111}
112
113#[derive(Debug, thiserror::Error)]
115pub enum ShamirError {
116 #[error("insufficient shares: have {have}, need {need}")]
118 InsufficientShares {
119 have: usize,
121 need: u32,
123 },
124 #[error("duplicate x: {0}")]
126 DuplicateX(u32),
127}