confium_tc_elgamal_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
53 let mut sum = Scalar::ZERO;
54 for s_i in shares {
55 let x_i = u32_to_scalar(s_i.x);
56 let mut numerator = Scalar::ONE;
57 let mut denominator = Scalar::ONE;
58 for s_j in shares {
59 if s_j.x == s_i.x {
60 continue;
61 }
62 let x_j = u32_to_scalar(s_j.x);
63 numerator = numerator.mul(&Scalar::ZERO.sub(&x_j));
64 denominator = denominator.mul(&x_i.sub(&x_j));
65 }
66 let denom_inv = invert(&denominator);
67 let lagrange = numerator.mul(&denom_inv);
68 let term = s_i.y.mul(&lagrange);
69 sum = sum.add(&term);
70 }
71 Ok(sum)
72}
73
74fn random_scalar() -> Scalar {
75 Scalar::random(&mut rand_core::UnwrapErr(getrandom::SysRng))
76}
77
78fn evaluate_polynomial(coeffs: &[Scalar], x: &Scalar) -> Scalar {
79 let mut result = Scalar::ZERO;
80 for c in coeffs.iter().rev() {
81 result = result.mul(x);
82 result = result.add(c);
83 }
84 result
85}
86
87fn reduce_to_scalar(mut bytes: [u8; 32]) -> Scalar {
90 loop {
91 if let Some(s) = Option::<Scalar>::from(Scalar::from_repr(FieldBytes::from(bytes))) {
92 return s;
93 }
94 let mut h = Sha256::new();
95 h.update(b"confium-scalar-reduce-v1");
96 h.update(bytes);
97 bytes = h.finalize().into();
98 }
99}
100
101fn u32_to_scalar(v: u32) -> Scalar {
102 let mut arr = [0u8; 32];
103 arr[28..32].copy_from_slice(&v.to_be_bytes());
104 reduce_to_scalar(arr)
105}
106
107fn invert(s: &Scalar) -> Scalar {
108 let ct: CtOption<Scalar> = s.invert();
111 Option::<Scalar>::from(ct).unwrap_or(Scalar::ZERO)
112}
113
114#[derive(Debug, thiserror::Error)]
116pub enum ShamirError {
117 #[error("insufficient shares: have {have}, need {need}")]
119 InsufficientShares {
120 have: usize,
122 need: u32,
124 },
125 #[error("duplicate x: {0}")]
127 DuplicateX(u32),
128}