Skip to main content

shamir_algorithm/
lib.rs

1/// # Shamir Secret Sharing Library
2///
3/// This library implements Shamir's Secret Sharing algorithm, allowing a secret to be split into multiple shares
4/// such that a minimum number of shares are required to reconstruct the original secret.
5/// The implementation uses Galois Field arithmetic over GF(256) for the polynomial operations.
6///
7/// # Example
8///
9/// ```rust
10/// use shamir_algorithm::ShamirSS;
11/// use std::collections::BTreeMap;
12///
13/// let secret = b"Hello, world!";
14/// let n = 5;
15/// let k = 3;
16///
17/// let shares = ShamirSS::split(n, k, secret.to_vec()).unwrap();
18/// // Use at least k shares to reconstruct
19/// let mut parts = BTreeMap::new();
20/// for i in 1..=k {
21///     parts.insert(i, shares[&i].clone());
22/// }
23/// let reconstructed = ShamirSS::join(parts).unwrap();
24/// assert_eq!(reconstructed, secret);
25/// ```
26
27use std::{
28    collections::{BTreeMap, HashSet},
29    fmt::Debug,
30};
31
32/// Shamir Secret Sharing implementation.
33/// This struct provides static methods for splitting and joining secrets using Shamir's algorithm.
34#[derive(Debug, Clone)]
35pub struct ShamirSS;
36
37/// Implementation of Shamir's Secret Sharing methods.
38impl ShamirSS {
39
40    /// Splits a secret into `n` shares, requiring at least `k` shares to reconstruct the secret.
41    ///
42    /// The secret is treated as a sequence of bytes, and each byte is split independently using
43    /// polynomial interpolation over GF(256).
44    ///
45    /// # Arguments
46    ///
47    /// * `n` - The total number of shares to generate. Must be between `k` and 255.
48    /// * `k` - The minimum number of shares required to reconstruct the secret. Must be greater than 1.
49    /// * `secret` - The secret data as a vector of bytes.
50    ///
51    /// # Returns
52    ///
53    /// A `Result` containing a `BTreeMap` where keys are share indices (1 to n) and values are the share data,
54    /// or an error string if the parameters are invalid.
55    ///
56    /// # Errors
57    ///
58    /// Returns an error if `k <= 1`, `n < k`, or `n > 255`.
59    pub fn split(n: i32, k: i32, secret: Vec<u8>) -> Result<BTreeMap<i32, Vec<u8>>, String> {
60        if !(k > 1) {
61            return Err("Not k > 1".to_string());
62        }
63        if !(n >= k) {
64            return Err("Not n >= k".to_string());
65        }
66        if !(n <= 255) {
67            return Err("Not n <= 255".to_string());
68        }
69
70        let seclen = secret.len();
71        let mut values: Vec<Vec<u8>> = vec![vec![0u8; seclen]; n as usize];
72        let degree = k - 1;
73        for i in 0..=secret.len() - 1 {
74            let p = GFC256::generate(degree, secret[i]);
75            for x in 1..=n {
76                let index = x - 1;
77                let element = GFC256::eval(p.clone(), x as u8);
78                values[index as usize][i] = element;
79            }
80        }
81        let mut parts: BTreeMap<i32, Vec<u8>> = BTreeMap::new();
82        for i in 0..=n - 1 {
83            let mut tmp = vec![0u8; secret.len()];
84
85            for ii in 0..=secret.len() - 1 {
86                tmp[ii] = values[i as usize][ii as usize];
87            }
88            parts.insert(i + 1, tmp);
89        }
90
91        Ok(parts)
92    }
93
94    /// Reconstructs the original secret from a set of shares.
95    ///
96    /// At least `k` shares (where `k` is the threshold used in `split`) are required to successfully
97    /// reconstruct the secret. Providing fewer shares or invalid shares will result in an error.
98    ///
99    /// # Arguments
100    ///
101    /// * `parts` - A `BTreeMap` containing share indices as keys and share data as values.
102    ///   The indices should correspond to those generated by `split`.
103    ///
104    /// # Returns
105    ///
106    /// A `Result` containing the reconstructed secret as a vector of bytes, or an error string.
107    ///
108    /// # Errors
109    ///
110    /// Returns an error if no parts are provided or if the parts have inconsistent lengths.
111    pub fn join(parts: BTreeMap<i32, Vec<u8>>) -> Result<Vec<u8>, String> {
112        let p = parts.clone();
113
114        if !(parts.len() > 0) {
115            return Err("No parts provided".to_string());
116        }
117        let mut h = HashSet::new();
118        for value in p.values() {
119            let l = value.clone().len();
120            if l != 0 {
121                h.insert(l);
122            }
123        }
124        if h.len() != 1 {
125            return Err("Varying lengths of part values".to_string());
126        }
127        let len = h.iter().next().unwrap();
128        let partslen = parts.len();
129        let mut secret = vec![0u8; len.clone()];
130
131        for i in 0..=secret.len() - 1 {
132            let mut points = vec![vec![0u8; 2]; partslen as usize];
133            let mut j = 0;
134
135            let mut iter = parts.iter();
136
137            while let Some(item) = iter.next() {
138                //println!("{}", item.0);
139                points[j][0] = *item.0 as u8;
140                points[j][1] = item.1[i];
141
142                j = j + 1;
143            }
144
145            secret[i] = GFC256::interpolate(points);
146        }
147
148        Ok(secret.clone())
149    }
150}
151
152/// Galois Field operations over GF(256) for polynomial arithmetic in Shamir's algorithm.
153struct GFC256;
154
155/// Implementation of Galois Field arithmetic operations.
156impl GFC256 {
157    fn add(a: u8, b: u8) -> u8 {
158        a ^ b
159    }
160    fn sub(a: u8, b: u8) -> u8 {
161        Self::add(a, b)
162    }
163    fn mul(a: u8, b: u8) -> u8 {
164        if a == 0 || b == 0 {
165            return 0;
166        }
167
168        let exp = LOG[a as usize] as i32 + LOG[b as usize] as i32;
169
170        EXP[exp as usize]
171    }
172    fn div(a: u8, b: u8) -> u8 {
173        Self::mul(a, EXP[255 - LOG[b as usize] as usize])
174    }
175
176    /// Evaluates a polynomial at a given point using Horner's method.
177    fn eval(p: Vec<u8>, x: u8) -> u8 {
178        let mut result: u8 = 0u8;
179
180        // Horner's method
181        for i in (0..=p.len() - 1).rev() {
182            let val = p[i];
183            result = Self::add(Self::mul(result, x), val);
184        }
185
186        result
187    }
188    fn degree(p: Vec<u8>) -> i32 {
189        let to = p.len() - 1;
190
191        for i in (1..=to).rev() {
192            if p[i] != 0 {
193                return i as i32;
194            }
195        }
196        return 0;
197    }
198    fn generate(degree: i32, x: u8) -> Vec<u8> {
199        let d: i32 = degree + 1;
200        let mut p: Vec<u8>;
201
202        loop {
203            p = (0..d).map(|_| rand::random::<u8>()).collect();
204            let dg = Self::degree(p.clone());
205            if dg == degree {
206                break;
207            }
208        }
209        p[0] = x;
210
211        p
212    }
213
214    /// Performs Lagrange interpolation to find the y-value at x=0 given a set of points.
215    fn interpolate(points: Vec<Vec<u8>>) -> u8 {
216        let x: u8 = 0;
217        let mut y: u8 = 0;
218        let len = points.len() - 1;
219        for i in 0..=len {
220            let ax: u8 = points[i][0];
221            let ay: u8 = points[i][1];
222            let mut li: u8 = 1;
223            for j in 0..=len {
224                let bx = points[j][0];
225                if i != j {
226                    li = Self::mul(li, Self::div(Self::sub(x, bx), Self::sub(ax, bx)));
227                }
228            }
229            let mul = Self::mul(li, ay);
230            y = Self::add(y, mul);
231        }
232        y
233    }
234}
235
236static LOG: [u8; 256] = [
237    0xff, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, 0xc6, 0x4b, 0xc7, 0x1b, 0x68, 0x33, 0xee, 0xdf, 0x03,
238    0x64, 0x04, 0xe0, 0x0e, 0x34, 0x8d, 0x81, 0xef, 0x4c, 0x71, 0x08, 0xc8, 0xf8, 0x69, 0x1c, 0xc1,
239    0x7d, 0xc2, 0x1d, 0xb5, 0xf9, 0xb9, 0x27, 0x6a, 0x4d, 0xe4, 0xa6, 0x72, 0x9a, 0xc9, 0x09, 0x78,
240    0x65, 0x2f, 0x8a, 0x05, 0x21, 0x0f, 0xe1, 0x24, 0x12, 0xf0, 0x82, 0x45, 0x35, 0x93, 0xda, 0x8e,
241    0x96, 0x8f, 0xdb, 0xbd, 0x36, 0xd0, 0xce, 0x94, 0x13, 0x5c, 0xd2, 0xf1, 0x40, 0x46, 0x83, 0x38,
242    0x66, 0xdd, 0xfd, 0x30, 0xbf, 0x06, 0x8b, 0x62, 0xb3, 0x25, 0xe2, 0x98, 0x22, 0x88, 0x91, 0x10,
243    0x7e, 0x6e, 0x48, 0xc3, 0xa3, 0xb6, 0x1e, 0x42, 0x3a, 0x6b, 0x28, 0x54, 0xfa, 0x85, 0x3d, 0xba,
244    0x2b, 0x79, 0x0a, 0x15, 0x9b, 0x9f, 0x5e, 0xca, 0x4e, 0xd4, 0xac, 0xe5, 0xf3, 0x73, 0xa7, 0x57,
245    0xaf, 0x58, 0xa8, 0x50, 0xf4, 0xea, 0xd6, 0x74, 0x4f, 0xae, 0xe9, 0xd5, 0xe7, 0xe6, 0xad, 0xe8,
246    0x2c, 0xd7, 0x75, 0x7a, 0xeb, 0x16, 0x0b, 0xf5, 0x59, 0xcb, 0x5f, 0xb0, 0x9c, 0xa9, 0x51, 0xa0,
247    0x7f, 0x0c, 0xf6, 0x6f, 0x17, 0xc4, 0x49, 0xec, 0xd8, 0x43, 0x1f, 0x2d, 0xa4, 0x76, 0x7b, 0xb7,
248    0xcc, 0xbb, 0x3e, 0x5a, 0xfb, 0x60, 0xb1, 0x86, 0x3b, 0x52, 0xa1, 0x6c, 0xaa, 0x55, 0x29, 0x9d,
249    0x97, 0xb2, 0x87, 0x90, 0x61, 0xbe, 0xdc, 0xfc, 0xbc, 0x95, 0xcf, 0xcd, 0x37, 0x3f, 0x5b, 0xd1,
250    0x53, 0x39, 0x84, 0x3c, 0x41, 0xa2, 0x6d, 0x47, 0x14, 0x2a, 0x9e, 0x5d, 0x56, 0xf2, 0xd3, 0xab,
251    0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, 0xe3, 0xa5,
252    0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18, 0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07,
253];
254
255static EXP: [u8; 510] = [
256    0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, 0x13, 0x35,
257    0x5f, 0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, 0xf7, 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa,
258    0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, 0x26, 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31,
259    0x53, 0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd,
260    0x4c, 0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88,
261    0x83, 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, 0x9a,
262    0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, 0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3,
263    0xfe, 0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, 0xec, 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0,
264    0xfb, 0x16, 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41,
265    0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75,
266    0x9f, 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80,
267    0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23, 0x65, 0xaf, 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54,
268    0xfc, 0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca,
269    0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, 0xa8, 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e,
270    0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17,
271    0x39, 0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6, 0x01,
272    0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, 0x13, 0x35, 0x5f,
273    0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, 0xf7, 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, 0xe5,
274    0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, 0x26, 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, 0x53,
275    0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd, 0x4c,
276    0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88, 0x83,
277    0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, 0x9a, 0xb5,
278    0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, 0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, 0xfe,
279    0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, 0xec, 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0, 0xfb,
280    0x16, 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41, 0xc3,
281    0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75, 0x9f,
282    0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80, 0x9b,
283    0xb6, 0xc1, 0x58, 0xe8, 0x23, 0x65, 0xaf, 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, 0xfc,
284    0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca, 0x45,
285    0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, 0xa8, 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e, 0x12,
286    0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17, 0x39,
287    0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6,
288];
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn it_works() {
296        let secret = b"Hello Shamir Shared Secret!!!!!";
297        let numparts = 5;
298        let miniumparts = 3;
299
300
301
302        let keys = ShamirSS::split(numparts, miniumparts, secret.to_vec());
303        assert!(keys.is_ok());
304        let keys = keys.unwrap();
305        let mut parts:BTreeMap<i32,Vec<u8>>=BTreeMap::new();
306        for (key, value) in &keys {
307            // Copy only entries with keys less than or equal to 3
308            if *key <= miniumparts {
309                parts.insert(*key, value.clone());
310            }
311        }
312        let nshared = ShamirSS::join(parts);
313        assert!(nshared.is_ok());
314        let shared = nshared.unwrap();
315        assert_eq!(shared, secret.to_vec());
316
317    }
318}