From 02565716e0f8a012b4f509a4c8f40555f8317684 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Thu, 16 Oct 2025 20:22:38 +0530 Subject: [PATCH 01/17] upgrade deps --- Cargo.toml | 22 +++++++++++----------- README.md | 32 ++++++++++++++++---------------- benches/benchmarks.rs | 3 ++- fuzz/Cargo.toml | 2 +- src/lib.rs | 5 ++++- src/math.rs | 6 +++--- 6 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3200ae8..6bd34b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,36 @@ [package] name = "sharks" -version = "0.5.0" +version = "0.6.0" authors = ["Aitor Ruano "] description = "Fast, small and secure Shamir's Secret Sharing library crate" -homepage = "https://github.com/c0dearm/sharks" -repository = "https://github.com/c0dearm/sharks" +homepage = "https://github.com/multifactor/gf256sss" +repository = "https://github.com/multifactor/gf256sss" readme = "README.md" keywords = ["shamir", "secret", "sharing", "share", "crypto"] categories = ["algorithms", "cryptography", "mathematics"] license = "MIT/Apache-2.0" -edition = "2018" +edition = "2021" [badges] maintenance = { status = "actively-developed" } -codecov = { repository = "c0dearm/sharks" } +codecov = { repository = "multifactor/gf256sss" } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["std", "zeroize_memory"] -std = ["rand/std", "rand/std_rng"] +std = ["rand/std", "rand/std_rng", "rand/os_rng"] fuzzing = ["std", "arbitrary"] zeroize_memory = ["zeroize"] [dependencies] -rand = { version = "0.8", default-features = false } -hashbrown = "0.9" -arbitrary = { version = "0.4.7", features = ["derive"], optional = true } +rand = { version = "0.9.2", default-features = false, optional = true } +hashbrown = "0.16" +arbitrary = { version = "1.4", features = ["derive"], optional = true } zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } [dev-dependencies] -criterion = "0.3" -rand_chacha = "0.3" +criterion = "0.7" +rand_chacha = "0.9" [[bench]] name = "benchmarks" diff --git a/README.md b/README.md index 565f0fa..8ebe7b4 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # Sharks -[![Rust](https://github.com/c0dearm/sharks/workflows/Rust/badge.svg?branch=master)](https://github.com/c0dearm/sharks/actions) -[![Crates](https://img.shields.io/crates/v/sharks.svg)](https://crates.io/crates/sharks) -[![Docs](https://docs.rs/sharks/badge.svg)](https://docs.rs/sharks) -[![Codecov](https://codecov.io/gh/c0dearm/sharks/branch/master/graph/badge.svg)](https://codecov.io/gh/c0dearm/sharks) -[![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/c0dearm/sharks/blob/master/COPYRIGHT) +[![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) +[![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) +[![Docs](https://docs.rs/gf256sss/badge.svg)](https://docs.rs/gf256sss) +[![Codecov](https://codecov.io/gh/multifactor/gf256sss/branch/master/graph/badge.svg)](https://codecov.io/gh/multifactor/gf256sss) +[![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/multifactor/gf256sss/blob/master/COPYRIGHT) Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate Documentation: -- [API reference (docs.rs)](https://docs.rs/sharks) +- [API reference (docs.rs)](https://docs.rs/gf256sss) ## Usage @@ -17,17 +17,17 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -sharks = "0.4" +gf256sss = "0.4" ``` If your environment doesn't support `std`: ```toml [dependencies] -sharks = { version = "0.4", default-features = false } +gf256sss = { version = "0.4", default-features = false } ``` -To get started using Sharks, see the [Rust docs](https://docs.rs/sharks) +To get started using gf256sss, see the [Rust docs](https://docs.rs/gf256sss) ## Features @@ -54,15 +54,15 @@ You can run them with `cargo test` and `cargo bench`. ### Benchmark results [min mean max] -| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes | -| ----------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | -| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [1.4321 us 1.4339 us 1.4357 us] | [1.3385 ns 1.3456 ns 1.3552 ns] | [228.77 us 232.17 us 236.23 us] | [24.688 ns 25.083 ns 25.551 ns] | [22.832 ns 22.910 ns 22.995 ns] | -| Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz | [1.3439 us 1.3499 us 1.3562 us] | [1.5416 ns 1.5446 ns 1.5481 ns] | [197.46 us 198.37 us 199.22 us] | [20.455 ns 20.486 ns 20.518 ns] | [18.726 ns 18.850 ns 18.993 ns] | -| Apple M1 ARM (Macbook Air) | [3.3367 us 3.3629 us 3.4058 us] | [741.75 ps 742.65 ps 743.52 ps] | [210.14 us 210.23 us 210.34 us] | [27.567 ns 27.602 ns 27.650 ns] | [26.716 ns 26.735 ns 26.755 ns] | +| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes | +| ---------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | +| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [1.4321 us 1.4339 us 1.4357 us] | [1.3385 ns 1.3456 ns 1.3552 ns] | [228.77 us 232.17 us 236.23 us] | [24.688 ns 25.083 ns 25.551 ns] | [22.832 ns 22.910 ns 22.995 ns] | +| Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz | [1.3439 us 1.3499 us 1.3562 us] | [1.5416 ns 1.5446 ns 1.5481 ns] | [197.46 us 198.37 us 199.22 us] | [20.455 ns 20.486 ns 20.518 ns] | [18.726 ns 18.850 ns 18.993 ns] | +| Apple M1 ARM (Macbook Air) | [3.3367 us 3.3629 us 3.4058 us] | [741.75 ps 742.65 ps 743.52 ps] | [210.14 us 210.23 us 210.34 us] | [27.567 ns 27.602 ns 27.650 ns] | [26.716 ns 26.735 ns 26.755 ns] | # Contributing -If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/c0dearm/sharks/issues/new). +If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/multifactor/gf256sss/issues/new). To introduce your changes into the codebase, submit a Pull Request. @@ -70,7 +70,7 @@ Many thanks! # License -Sharks is distributed under the terms of both the MIT license and the +gf256sss is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index b80eb17..d3cc5e9 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,5 +1,6 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; use std::convert::TryFrom; +use std::hint::black_box; use sharks::{Share, Sharks}; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f7e4f39..2ef7e09 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -4,7 +4,7 @@ name = "sharks-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false -edition = "2018" +edition = "2021" [package.metadata] cargo-fuzz = true diff --git a/src/lib.rs b/src/lib.rs index ecc6cef..74b8c24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,9 @@ use hashbrown::HashSet; use field::GF256; pub use share::Share; +#[cfg(feature = "std")] +use rand::{rngs::StdRng, SeedableRng}; + /// Tuple struct which implements methods to generate shares and recover secrets over a 256 bits Galois Field. /// Its only parameter is the minimum shares threshold. /// @@ -114,7 +117,7 @@ impl Sharks { /// let shares: Vec = dealer.take(3).collect(); #[cfg(feature = "std")] pub fn dealer(&self, secret: &[u8]) -> impl Iterator { - let mut rng = rand::thread_rng(); + let mut rng = StdRng::from_os_rng(); self.dealer_rng(secret, &mut rng) } diff --git a/src/math.rs b/src/math.rs index 193382a..290e218 100644 --- a/src/math.rs +++ b/src/math.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; use super::field::GF256; use super::share::Share; @@ -95,7 +95,7 @@ pub fn reshare(shares: &[Share], index: usize) -> Share { pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec { let k = k as usize; let mut poly = Vec::with_capacity(k); - let between = Uniform::new_inclusive(1, 255); + let between = Uniform::new_inclusive(1, 255).unwrap(); for _ in 1..k { poly.push(GF256(between.sample(rng))); @@ -124,7 +124,7 @@ pub fn get_evaluator(polys: Vec>) -> impl Iterator { #[cfg(test)] mod tests { - use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; + use super::{GF256, Share, get_evaluator, interpolate, random_polynomial}; use alloc::{vec, vec::Vec}; use rand_chacha::rand_core::SeedableRng; From 32008207ce5ce84d7f6bef272cb1f64dfb1fd942 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 01:46:18 +0530 Subject: [PATCH 02/17] make field generic --- Cargo.toml | 1 + src/field.rs | 295 ++++++++++++++++++++++++++++++++++++--------------- src/lib.rs | 75 +++++++------ src/math.rs | 34 ++++-- src/share.rs | 26 ++--- 5 files changed, 292 insertions(+), 139 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bd34b9..6f55895 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } [dev-dependencies] criterion = "0.7" rand_chacha = "0.9" +rstest = "0.26.1" [[bench]] name = "benchmarks" diff --git a/src/field.rs b/src/field.rs index 05b4960..382c3c8 100644 --- a/src/field.rs +++ b/src/field.rs @@ -10,123 +10,162 @@ use arbitrary::Arbitrary; #[cfg(feature = "zeroize_memory")] use zeroize::Zeroize; -const LOG_TABLE: [u8; 256] = [ - 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, - 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, - 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, - 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, - 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, - 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, - 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, - 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, - 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, - 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, - 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, - 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, - 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, - 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, - 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, - 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf, -]; +const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { + let mut result = 0u16; + let mut a_val = a as u16; + let mut b_val = b as u16; + + while b_val > 0 { + if (b_val & 1) == 1 { + result ^= a_val; + } + let carry = (a_val & 0x80) != 0; + a_val <<= 1; + if carry { + a_val ^= poly; + } + b_val >>= 1; + } + + (result & 0xFF) as u8 +} -const EXP_TABLE: [u8; 512] = [ - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, - 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, - 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, - 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, - 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, - 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, - 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, - 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, - 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, - 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, - 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, - 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, - 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, - 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, - 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, - 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, - 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, - 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x9d, - 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, - 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, - 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, - 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, - 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, - 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, - 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, - 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, - 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, - 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, - 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, - 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, - 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 0x2c, - 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, 0x02, +const PRIMITIVE_POLYS: &[u16] = &[ + 0x11B, 0x11D, 0x12B, 0x12D, 0x14F, 0x15B, 0x15D, 0x163, 0x169, 0x16B, 0x187, 0x18D, 0x19F, + 0x1AF, 0x1C3, 0x1FD, ]; +#[inline] +const fn is_primitive(poly: u16) -> bool { + let mut i = 0; + + while i < PRIMITIVE_POLYS.len() { + if PRIMITIVE_POLYS[i] == poly { + return true; + } + i += 1; + } + false +} + #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "fuzzing", derive(Arbitrary))] #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] #[cfg_attr(feature = "zeroize_memory", zeroize(drop))] -pub struct GF256(pub u8); +pub struct GF256(pub u8); + +pub struct Tables { + pub log: [u8; 256], + pub exp: [u8; 512], +} + +impl Tables { + pub const fn new() -> Self { + assert!(is_primitive(POLY), "POLY must be primitive"); + + let mut log = [0u8; 256]; + let mut exp = [0u8; 512]; + let gen = 0x02_u8; + + let mut i = 0usize; + let mut x = 1u8; + while i < 255 { + exp[i] = x; + log[x as usize] = i as u8; + x = gf256_mul(x, gen, POLY); + i += 1; + } + + let mut j = 255usize; + while j < 512 { + exp[j] = exp[j - 255]; + j += 1; + } + + Self { log, exp } + } +} + +impl GF256 { + const POLY_CHECK: () = assert!(is_primitive(POLY), "POLY must be primitive"); + pub const TABLES: Tables = Tables::new(); + + pub fn add(self, other: Self) -> Self { + let _ = Self::POLY_CHECK; // const check will be amortized by the compiler + Self(self.0 ^ other.0) + } + + pub fn sub(self, other: Self) -> Self { + let _ = Self::POLY_CHECK; + Self(self.0 ^ other.0) + } + + pub fn mul(self, other: Self) -> Self { + let log_x = Self::TABLES.log[self.0 as usize] as usize; + let log_y = Self::TABLES.log[other.0 as usize] as usize; + + if self.0 == 0 || other.0 == 0 { + Self(0) + } else { + Self(Self::TABLES.exp[log_x + log_y]) + } + } + + pub fn div(self, other: Self) -> Self { + let log_x = Self::TABLES.log[self.0 as usize] as usize; + let log_y = Self::TABLES.log[other.0 as usize] as usize; + + if self.0 == 0 { + Self(0) + } else { + Self(Self::TABLES.exp[log_x + 255 - log_y]) + } + } +} #[allow(clippy::suspicious_arithmetic_impl)] -impl Add for GF256 { - type Output = GF256; +impl Add for GF256 { + type Output = Self; fn add(self, other: Self) -> Self::Output { - Self(self.0 ^ other.0) + self.add(other) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Sub for GF256 { +impl Sub for GF256 { type Output = Self; fn sub(self, other: Self) -> Self::Output { - Self(self.0 ^ other.0) + self.sub(other) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for GF256 { +impl Mul for GF256 { type Output = Self; fn mul(self, other: Self) -> Self::Output { - let log_x = LOG_TABLE[self.0 as usize] as usize; - let log_y = LOG_TABLE[other.0 as usize] as usize; - - if self.0 == 0 || other.0 == 0 { - Self(0) - } else { - Self(EXP_TABLE[log_x + log_y]) - } + self.mul(other) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Div for GF256 { +impl Div for GF256 { type Output = Self; fn div(self, other: Self) -> Self::Output { - let log_x = LOG_TABLE[self.0 as usize] as usize; - let log_y = LOG_TABLE[other.0 as usize] as usize; - - if self.0 == 0 { - Self(0) - } else { - Self(EXP_TABLE[log_x + 255 - log_y]) - } + self.div(other) } } -impl Sum for GF256 { +impl Sum for GF256 { fn sum>(iter: I) -> Self { iter.fold(Self(0), |acc, x| acc + x) } } -impl Product for GF256 { +impl Product for GF256 { fn product>(iter: I) -> Self { iter.fold(Self(1), |acc, x| acc * x) } @@ -134,8 +173,68 @@ impl Product for GF256 { #[cfg(test)] mod tests { - use super::{EXP_TABLE, GF256, LOG_TABLE}; + use super::GF256; use alloc::vec; + use rstest::rstest; + + const POLY: u16 = 0x11d_u16; + const LOG_TABLE: [u8; 256] = [ + 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, + 0x4b, 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, + 0x4c, 0x71, 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, + 0x12, 0x82, 0x45, 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, + 0x4d, 0xe4, 0x72, 0xa6, 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, + 0xb3, 0x10, 0x91, 0x22, 0x88, 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, + 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, + 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, + 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, + 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, + 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 0x37, 0x3f, 0xd1, 0x5b, 0x95, + 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 0xf2, 0x56, 0xd3, 0xab, + 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 0x1f, 0x2d, 0x43, + 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, 0x6c, 0xa1, + 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 0xcb, + 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, + 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, + 0xaf, + ]; + const EXP_TABLE: [u8; 512] = [ + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, + 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, + 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, + 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, + 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, + 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, + 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, + 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, + 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, + 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, + 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, + 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, + 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, + 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, + 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, + 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, + 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, + 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, + 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, + 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, + 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, + 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, + 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, + 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, + 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, + 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, + 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, + 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, + 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, + 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, + 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, + 0x01, 0x02, + ]; #[test] fn add_works() { @@ -157,7 +256,10 @@ mod tests { ]; for (i, a) in answers.iter().enumerate() { - assert_eq!((GF256(LOG_TABLE[i]) + GF256(EXP_TABLE[i])).0, *a); + assert_eq!( + (GF256::(LOG_TABLE[i]) + GF256::(EXP_TABLE[i])).0, + *a + ); } } @@ -186,7 +288,10 @@ mod tests { ]; for (i, a) in answers.iter().enumerate() { - assert_eq!((GF256(LOG_TABLE[i]) * GF256(EXP_TABLE[i])).0, *a); + assert_eq!( + (GF256::(LOG_TABLE[i]) * GF256::(EXP_TABLE[i])).0, + *a + ); } } @@ -210,19 +315,39 @@ mod tests { ]; for (i, a) in answers.iter().enumerate() { - assert_eq!((GF256(LOG_TABLE[i]) / GF256(EXP_TABLE[i])).0, *a); + assert_eq!( + (GF256::(LOG_TABLE[i]) / GF256::(EXP_TABLE[i])).0, + *a + ); } } #[test] fn sum_works() { - let values = vec![GF256(0x53), GF256(0xCA), GF256(0)]; - assert_eq!(values.into_iter().sum::().0, 0x99); + let values = vec![GF256::(0x53), GF256::(0xCA), GF256::(0)]; + assert_eq!(values.into_iter().sum::>().0, 0x99); } #[test] fn product_works() { - let values = vec![GF256(1), GF256(1), GF256(4)]; - assert_eq!(values.into_iter().product::().0, 4); + let values = vec![GF256::(1), GF256::(1), GF256::(4)]; + assert_eq!(values.into_iter().product::>().0, 4); + } + + #[rstest] + #[case(0x11B)] + #[case(0x11D)] + #[case(0x12B)] + #[case(0x12D)] + fn primitive_poly(#[case] poly: u16) { + assert!(super::is_primitive(poly)); + } + + #[rstest] + #[case(0x100)] + #[case(0x102)] + #[case(0x103)] + fn non_primitive_poly(#[case] poly: u16) { + assert!(!super::is_primitive(poly)); } } diff --git a/src/lib.rs b/src/lib.rs index 74b8c24..f1bdf64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ //! ``` //! use sharks::{ Sharks, Share }; //! +//! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares //! let sharks = Sharks(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] @@ -11,7 +12,7 @@ //! # { //! let dealer = sharks.dealer(&[1, 2, 3, 4]); //! // Get 10 shares -//! let shares: Vec = dealer.take(10).collect(); +//! let shares: Vec> = dealer.take(10).collect(); //! // Recover the original secret! //! let secret = sharks.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); @@ -21,15 +22,16 @@ //! Usage example (no std): //! ``` //! use sharks::{ Sharks, Share }; -//! use rand_chacha::rand_core::SeedableRng; +//! use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; //! +//! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares //! let sharks = Sharks(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -//! let dealer = sharks.dealer_rng(&[1, 2, 3, 4], &mut rng); +//! let dealer = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng); //! // Get 10 shares -//! let shares: Vec = dealer.take(10).collect(); +//! let shares: Vec> = dealer.take(10).collect(); //! // Recover the original secret! //! let secret = sharks.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); @@ -57,6 +59,7 @@ use rand::{rngs::StdRng, SeedableRng}; /// Usage example: /// ``` /// # use sharks::{ Sharks, Share }; +/// # const POLY: u16 = 0x11d_u16; /// // Set a minimum threshold of 10 shares /// let sharks = Sharks(10); /// // Obtain an iterator over the shares for secret [1, 2, 3, 4] @@ -64,7 +67,7 @@ use rand::{rngs::StdRng, SeedableRng}; /// # { /// let dealer = sharks.dealer(&[1, 2, 3, 4]); /// // Get 10 shares -/// let shares: Vec = dealer.take(10).collect(); +/// let shares: Vec> = dealer.take(10).collect(); /// // Recover the original secret! /// let secret = sharks.recover(shares.as_slice()).unwrap(); /// assert_eq!(secret, vec![1, 2, 3, 4]); @@ -83,18 +86,19 @@ impl Sharks { /// Example: /// ``` /// # use sharks::{ Sharks, Share }; - /// # use rand_chacha::rand_core::SeedableRng; + /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks(3); /// // Obtain an iterator over the shares for secret [1, 2] /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// let dealer = sharks.dealer_rng(&[1, 2], &mut rng); + /// let dealer = sharks.dealer_rng::(&[1, 2], &mut rng); /// // Get 3 shares - /// let shares: Vec = dealer.take(3).collect(); - pub fn dealer_rng( + /// let shares: Vec> = dealer.take(3).collect(); + pub fn dealer_rng( &self, secret: &[u8], rng: &mut R, - ) -> impl Iterator { + ) -> impl Iterator> { let mut polys = Vec::with_capacity(secret.len()); for chunk in secret { @@ -110,13 +114,14 @@ impl Sharks { /// Example: /// ``` /// # use sharks::{ Sharks, Share }; + /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks(3); /// // Obtain an iterator over the shares for secret [1, 2] - /// let dealer = sharks.dealer(&[1, 2]); + /// let dealer = sharks.dealer::(&[1, 2]); /// // Get 3 shares - /// let shares: Vec = dealer.take(3).collect(); + /// let shares: Vec> = dealer.take(3).collect(); #[cfg(feature = "std")] - pub fn dealer(&self, secret: &[u8]) -> impl Iterator { + pub fn dealer(&self, secret: &[u8]) -> impl Iterator> { let mut rng = StdRng::from_os_rng(); self.dealer_rng(secret, &mut rng) } @@ -128,10 +133,11 @@ impl Sharks { /// Example: /// ``` /// # use sharks::{ Sharks, Share }; - /// # use rand_chacha::rand_core::SeedableRng; + /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks(3); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let mut shares: Vec = sharks.dealer_rng(&[1], &mut rng).take(3).collect(); + /// # let mut shares: Vec> = sharks.dealer_rng::(&[1], &mut rng).take(3).collect(); /// // Recover original secret from shares /// let mut secret = sharks.recover(&shares); /// // Secret correctly recovered @@ -141,14 +147,14 @@ impl Sharks { /// secret = sharks.recover(&shares); /// // Not enough shares to recover secret /// assert!(secret.is_err()); - pub fn recover<'a, T>(&self, shares: T) -> Result, &str> + pub fn recover<'a, T, const POLY: u16>(&self, shares: T) -> Result, &str> where - T: IntoIterator, - T::IntoIter: Iterator, + T: IntoIterator>, + T::IntoIter: Iterator>, { let mut share_length: Option = None; let mut keys: HashSet = HashSet::new(); - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); for share in shares.into_iter() { if share_length.is_none() { @@ -177,10 +183,11 @@ impl Sharks { /// Example: /// ``` /// # use sharks::{ Sharks, Share }; - /// # use rand_chacha::rand_core::SeedableRng; + /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks(2); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let shares: Vec = sharks.dealer_rng(&[1, 2, 3, 4], &mut rng).take(3).collect(); + /// # let shares: Vec> = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng).take(3).collect(); /// // Recover original shares from original shares up to threshold shares /// let recovered_shares = sharks.recover_shares( /// [Some(&shares[0]), None, Some(&shares[2])], @@ -194,14 +201,18 @@ impl Sharks { /// let recovered_shares = sharks.recover_shares([Some(&shares[0]), None, None], 3); /// // Not enough shares to recover shares /// assert!(recovered_shares.is_err()); - pub fn recover_shares<'a, T>(&self, shares: T, n: usize) -> Result, &str> + pub fn recover_shares<'a, T, const POLY: u16>( + &self, + shares: T, + n: usize, + ) -> Result>, &str> where - T: IntoIterator>, - T::IntoIter: Iterator>, + T: IntoIterator>>, + T::IntoIter: Iterator>>, { let mut share_length: Option = None; let mut keys: HashSet = HashSet::new(); - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); let mut count = 0; for share in shares.into_iter() { @@ -247,9 +258,11 @@ mod tests { use super::{Share, Sharks}; use alloc::{vec, vec::Vec}; + const POLY: u16 = 0x11d_u16; + impl Sharks { #[cfg(not(feature = "std"))] - fn make_shares(&self, secret: &[u8]) -> impl Iterator { + fn make_shares(&self, secret: &[u8]) -> impl Iterator> { use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; let mut rng = ChaCha8Rng::from_seed([0x90; 32]); @@ -257,7 +270,7 @@ mod tests { } #[cfg(feature = "std")] - fn make_shares(&self, secret: &[u8]) -> impl Iterator { + fn make_shares(&self, secret: &[u8]) -> impl Iterator> { self.dealer(secret) } } @@ -265,7 +278,7 @@ mod tests { #[test] fn test_insufficient_shares_err() { let sharks = Sharks(255); - let shares: Vec = sharks.make_shares(&[1]).take(254).collect(); + let shares: Vec> = sharks.make_shares(&[1]).take(254).collect(); let secret = sharks.recover(&shares); assert!(secret.is_err()); } @@ -273,7 +286,7 @@ mod tests { #[test] fn test_duplicate_shares_err() { let sharks = Sharks(255); - let mut shares: Vec = sharks.make_shares(&[1]).take(255).collect(); + let mut shares: Vec> = sharks.make_shares(&[1]).take(255).collect(); shares[1] = Share { x: shares[0].x.clone(), y: shares[0].y.clone(), @@ -285,7 +298,7 @@ mod tests { #[test] fn test_integration_works() { let sharks = Sharks(255); - let shares: Vec = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); + let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); let secret = sharks.recover(&shares).unwrap(); assert_eq!(secret, vec![1, 2, 3, 4]); } @@ -293,7 +306,7 @@ mod tests { #[test] fn test_reshare_works() { let sharks = Sharks(3); - let shares: Vec = sharks.make_shares(&[1, 2, 3, 4]).take(4).collect(); + let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(4).collect(); let recovered_shares = sharks .recover_shares( diff --git a/src/math.rs b/src/math.rs index 290e218..1a26ed5 100644 --- a/src/math.rs +++ b/src/math.rs @@ -11,7 +11,7 @@ use super::share::Share; // The expected `shares` argument format is the same as the output by the `get_evaluator´ function. // Where each (key, value) pair corresponds to one share, where the key is the `x` and the value is a vector of `y`, // where each element corresponds to one of the secret's byte chunks. -pub fn interpolate(shares: &[Share]) -> Vec { +pub fn interpolate(shares: &[Share]) -> Vec { (0..shares[0].y.len()) .map(|s| { shares @@ -21,17 +21,21 @@ pub fn interpolate(shares: &[Share]) -> Vec { .iter() .filter(|s_j| s_j.x != s_i.x) .map(|s_j| s_j.x.clone() / (s_j.x.clone() - s_i.x.clone())) - .product::() + .product::>() * s_i.y[s].clone() }) - .sum::() + .sum::>() .0 }) .collect() } /// Takes N sample points and returns the value at a given x using Lagrange interpolation over GF(256). -pub fn interpolate_polynomial(x_samples: &[GF256], y_samples: &[GF256], x: GF256) -> GF256 { +pub fn interpolate_polynomial( + x_samples: &[GF256], + y_samples: &[GF256], + x: GF256, +) -> GF256 { assert!( x_samples.len() == y_samples.len(), "sample length mistmatch" @@ -60,7 +64,7 @@ pub fn interpolate_polynomial(x_samples: &[GF256], y_samples: &[GF256], x: GF256 result } -pub fn reshare(shares: &[Share], index: usize) -> Share { +pub fn reshare(shares: &[Share], index: usize) -> Share { // assert that atleast 2 shares exist assert!( shares.len() >= 2 && shares.len() <= 255, @@ -92,7 +96,11 @@ pub fn reshare(shares: &[Share], index: usize) -> Share { // Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, 255]`. // Coefficient degrees go from higher to lower in the returned vector order. -pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec { +pub fn random_polynomial( + s: GF256, + k: u8, + rng: &mut R, +) -> Vec> { let k = k as usize; let mut poly = Vec::with_capacity(k); let between = Uniform::new_inclusive(1, 255).unwrap(); @@ -109,7 +117,9 @@ pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec>) -> impl Iterator { +pub fn get_evaluator( + polys: Vec>>, +) -> impl Iterator> { (1..=u8::max_value()).map(GF256).map(move |x| Share { x: x.clone(), y: polys @@ -124,21 +134,23 @@ pub fn get_evaluator(polys: Vec>) -> impl Iterator { #[cfg(test)] mod tests { - use super::{GF256, Share, get_evaluator, interpolate, random_polynomial}; + use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; use alloc::{vec, vec::Vec}; use rand_chacha::rand_core::SeedableRng; + const POLY: u16 = 0x11d_u16; + #[test] fn random_polynomial_works() { let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - let poly = random_polynomial(GF256(1), 3, &mut rng); + let poly = random_polynomial::<_, POLY>(GF256(1), 3, &mut rng); assert_eq!(poly.len(), 3); assert_eq!(poly[2], GF256(1)); } #[test] fn evaluator_works() { - let iter = get_evaluator(vec![vec![GF256(3), GF256(2), GF256(5)]]); + let iter = get_evaluator::(vec![vec![GF256(3), GF256(2), GF256(5)]]); let values: Vec<_> = iter.take(2).map(|s| (s.x.clone(), s.y.clone())).collect(); assert_eq!( values, @@ -151,7 +163,7 @@ mod tests { let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); let poly = random_polynomial(GF256(185), 10, &mut rng); let iter = get_evaluator(vec![poly]); - let shares: Vec = iter.take(10).collect(); + let shares: Vec> = iter.take(10).collect(); let root = interpolate(&shares); assert_eq!(root, vec![185]); } diff --git a/src/share.rs b/src/share.rs index c9d1592..3721d6a 100644 --- a/src/share.rs +++ b/src/share.rs @@ -14,14 +14,15 @@ use zeroize::Zeroize; /// ``` /// use sharks::{Sharks, Share}; /// use core::convert::TryFrom; -/// # use rand_chacha::rand_core::SeedableRng; +/// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # fn send_to_printer(_: Vec) {} /// # fn ask_shares() -> Vec> {vec![vec![1, 2], vec![2, 3], vec![3, 4]]} /// /// // Transmit the share bytes to a printer +/// # const POLY: u16 = 0x11d_u16; /// let sharks = Sharks(3); /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -/// let dealer = sharks.dealer_rng(&[1, 2, 3], &mut rng); +/// let dealer = sharks.dealer_rng::(&[1, 2, 3], &mut rng); /// /// // Get 5 shares and print paper keys /// for s in dealer.take(5) { @@ -30,20 +31,20 @@ use zeroize::Zeroize; /// /// // Get share bytes from an external source and recover secret /// let shares_bytes: Vec> = ask_shares(); -/// let shares: Vec = shares_bytes.iter().map(|s| Share::try_from(s.as_slice()).unwrap()).collect(); +/// let shares: Vec> = shares_bytes.iter().map(|s| Share::::try_from(s.as_slice()).unwrap()).collect(); /// let secret = sharks.recover(&shares).unwrap(); #[derive(Clone)] #[cfg_attr(feature = "fuzzing", derive(Arbitrary, Debug))] #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] #[cfg_attr(feature = "zeroize_memory", zeroize(drop))] -pub struct Share { - pub x: GF256, - pub y: Vec, +pub struct Share { + pub x: GF256, + pub y: Vec>, } /// Obtains a byte vector from a `Share` instance -impl From<&Share> for Vec { - fn from(s: &Share) -> Vec { +impl From<&Share> for Vec { + fn from(s: &Share) -> Vec { let mut bytes = Vec::with_capacity(s.y.len() + 1); bytes.push(s.x.0); bytes.extend(s.y.iter().map(|p| p.0)); @@ -52,10 +53,10 @@ impl From<&Share> for Vec { } /// Obtains a `Share` instance from a byte slice -impl core::convert::TryFrom<&[u8]> for Share { +impl core::convert::TryFrom<&[u8]> for Share { type Error = &'static str; - fn try_from(s: &[u8]) -> Result { + fn try_from(s: &[u8]) -> Result, Self::Error> { if s.len() < 2 { Err("A Share must be at least 2 bytes long") } else { @@ -72,9 +73,10 @@ mod tests { use alloc::{vec, vec::Vec}; use core::convert::TryFrom; + const POLY: u16 = 0x11d_u16; #[test] fn vec_from_share_works() { - let share = Share { + let share = Share:: { x: GF256(1), y: vec![GF256(2), GF256(3)], }; @@ -85,7 +87,7 @@ mod tests { #[test] fn share_from_u8_slice_works() { let bytes = [1, 2, 3]; - let share = Share::try_from(&bytes[..]).unwrap(); + let share = Share::::try_from(&bytes[..]).unwrap(); assert_eq!(share.x, GF256(1)); assert_eq!(share.y, vec![GF256(2), GF256(3)]); } From cf6b1c609284fa90926f51f411e510d65af01018 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 01:46:41 +0530 Subject: [PATCH 03/17] fix benchmarks and fuzz tests --- Cargo.toml | 22 ++++++++++++++++++++-- benches/benchmarks.rs | 20 +++++++++++--------- fuzz/Cargo.toml | 4 ++-- fuzz/fuzz_targets/deserialize_share.rs | 3 ++- fuzz/fuzz_targets/generate_shares.rs | 5 +++-- fuzz/fuzz_targets/recover.rs | 6 +++--- fuzz/fuzz_targets/serialize_share.rs | 3 ++- 7 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f55895..67bbb56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,28 @@ [package] name = "sharks" version = "0.6.0" -authors = ["Aitor Ruano "] +authors = [ + "Aitor Ruano ", + "Multifactor, Inc. ", +] description = "Fast, small and secure Shamir's Secret Sharing library crate" homepage = "https://github.com/multifactor/gf256sss" repository = "https://github.com/multifactor/gf256sss" +documentation = "https://docs.rs/gf256sss" readme = "README.md" keywords = ["shamir", "secret", "sharing", "share", "crypto"] categories = ["algorithms", "cryptography", "mathematics"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" edition = "2021" +rust-version = "1.64" +include = [ + "src/**", + "README.md", + "CHANGELOG.md", + "COPYRIGHT", + "LICENSE-APACHE", + "LICENSE-MIT", +] [badges] maintenance = { status = "actively-developed" } @@ -36,3 +49,8 @@ rstest = "0.26.1" [[bench]] name = "benchmarks" harness = false + +[package.metadata.docs.rs] +features = ["std"] +all-features = false +rustdoc-args = ["--cfg", "docsrs"] diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index d3cc5e9..343270f 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -4,32 +4,34 @@ use std::hint::black_box; use sharks::{Share, Sharks}; -fn dealer(c: &mut Criterion) { +const POLY: u16 = 0x11d_u16; + +fn dealer(c: &mut Criterion) { let sharks = Sharks(255); - let mut dealer = sharks.dealer(&[1]); + let mut dealer = sharks.dealer::(&[1]); c.bench_function("obtain_shares_dealer", |b| { - b.iter(|| sharks.dealer(black_box(&[1]))) + b.iter(|| sharks.dealer::(black_box(&[1]))) }); c.bench_function("step_shares_dealer", |b| b.iter(|| dealer.next())); } -fn recover(c: &mut Criterion) { +fn recover(c: &mut Criterion) { let sharks = Sharks(255); - let shares: Vec = sharks.dealer(&[1]).take(255).collect(); + let shares: Vec> = sharks.dealer::(&[1]).take(255).collect(); c.bench_function("recover_secret", |b| { b.iter(|| sharks.recover(black_box(shares.as_slice()))) }); } -fn share(c: &mut Criterion) { +fn share(c: &mut Criterion) { let bytes_vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let bytes = bytes_vec.as_slice(); - let share = Share::try_from(bytes).unwrap(); + let share = Share::::try_from(bytes).unwrap(); c.bench_function("share_from_bytes", |b| { - b.iter(|| Share::try_from(black_box(bytes))) + b.iter(|| Share::::try_from(black_box(bytes))) }); c.bench_function("share_to_bytes", |b| { @@ -37,5 +39,5 @@ fn share(c: &mut Criterion) { }); } -criterion_group!(benches, dealer, recover, share); +criterion_group!(benches, dealer::, recover::, share::); criterion_main!(benches); diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2ef7e09..f9bf171 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.3" -arbitrary = { version = "0.4.2", features = ["derive"] } +libfuzzer-sys = "0.4" +arbitrary = { version = "1.4", features = ["derive"] } [dependencies.sharks] path = ".." diff --git a/fuzz/fuzz_targets/deserialize_share.rs b/fuzz/fuzz_targets/deserialize_share.rs index 74be5d1..95abb1d 100644 --- a/fuzz/fuzz_targets/deserialize_share.rs +++ b/fuzz/fuzz_targets/deserialize_share.rs @@ -4,5 +4,6 @@ use libfuzzer_sys::fuzz_target; use sharks::Share; fuzz_target!(|data: &[u8]| { - let _share = Share::try_from(data); + const POLY: u16 = 0x11d_u16; + let _share = Share::::try_from(data); }); diff --git a/fuzz/fuzz_targets/generate_shares.rs b/fuzz/fuzz_targets/generate_shares.rs index 98a6f9e..fc8a13b 100644 --- a/fuzz/fuzz_targets/generate_shares.rs +++ b/fuzz/fuzz_targets/generate_shares.rs @@ -12,8 +12,9 @@ struct Parameters { } fuzz_target!(|params: Parameters| { + const POLY: u16 = 0x11d_u16; let sharks = Sharks(params.threshold); - let dealer = sharks.dealer(¶ms.secret); + let dealer = sharks.dealer::(¶ms.secret); - let _shares: Vec = dealer.take(params.n_shares).collect(); + let _shares: Vec> = dealer.take(params.n_shares).collect(); }); diff --git a/fuzz/fuzz_targets/recover.rs b/fuzz/fuzz_targets/recover.rs index 1bdc33f..bed1637 100644 --- a/fuzz/fuzz_targets/recover.rs +++ b/fuzz/fuzz_targets/recover.rs @@ -5,12 +5,12 @@ use arbitrary::Arbitrary; use sharks::{Share, Sharks}; #[derive(Debug, Arbitrary)] -struct Parameters { +struct Parameters { pub threshold: u8, - pub shares: Vec, + pub shares: Vec>, } -fuzz_target!(|params: Parameters| { +fuzz_target!(|params: Parameters<0x11d>| { let sharks = Sharks(params.threshold); let _secret = sharks.recover(¶ms.shares); }); diff --git a/fuzz/fuzz_targets/serialize_share.rs b/fuzz/fuzz_targets/serialize_share.rs index e45fe95..bdca037 100644 --- a/fuzz/fuzz_targets/serialize_share.rs +++ b/fuzz/fuzz_targets/serialize_share.rs @@ -3,6 +3,7 @@ use libfuzzer_sys::fuzz_target; use sharks::Share; -fuzz_target!(|share: Share| { +const POLY: u16 = 0x11d_u16; +fuzz_target!(|share: Share| { let _data: Vec = (&share).into(); }); From 3cc9e51e365c145fb320cecf2aa3b25b5245f60a Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 01:46:45 +0530 Subject: [PATCH 04/17] update readme --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8ebe7b4..078564e 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ [![Codecov](https://codecov.io/gh/multifactor/gf256sss/branch/master/graph/badge.svg)](https://codecov.io/gh/multifactor/gf256sss) [![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/multifactor/gf256sss/blob/master/COPYRIGHT) -Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate +Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate Documentation: -- [API reference (docs.rs)](https://docs.rs/gf256sss) +- [API reference (docs.rs)](https://docs.rs/gf256sss) ## Usage @@ -24,7 +24,7 @@ If your environment doesn't support `std`: ```toml [dependencies] -gf256sss = { version = "0.4", default-features = false } +gf256sss = { version = "0.6", default-features = false } ``` To get started using gf256sss, see the [Rust docs](https://docs.rs/gf256sss) @@ -37,6 +37,12 @@ The API is simple and to the point, with minimal configuration. ### Fast and small The code is as idiomatic and clean as possible, with minimum external dependencies. +### Generic on irreducible polynomial +GF256 field support largely used primitive irreducible polynomials like 0x11B (AES), 0x11D (RS codes), 0x12B (Reed-Solomon codes), and more. + +### Compile time asserts +Any operation on the field with a non-whitelisted polynomial will fail to build due to const assertions done at compile time. + ### Secure by design The implementation forbids the user to choose parameters that would result in an insecure application, like generating more shares than what's allowed by the finite field length. From a5cd26f5f14fa4ee072a028a63dbe62090f329f6 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 01:55:59 +0530 Subject: [PATCH 05/17] clippy fixes --- Cargo.toml | 1 - codecov.yml | 20 -------------------- src/field.rs | 12 ++++++++++-- src/lib.rs | 10 ++++------ src/math.rs | 2 +- 5 files changed, 15 insertions(+), 30 deletions(-) delete mode 100644 codecov.yml diff --git a/Cargo.toml b/Cargo.toml index 67bbb56..4c5abec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ keywords = ["shamir", "secret", "sharing", "share", "crypto"] categories = ["algorithms", "cryptography", "mathematics"] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.64" include = [ "src/**", "README.md", diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 24b3742..0000000 --- a/codecov.yml +++ /dev/null @@ -1,20 +0,0 @@ -codecov: - require_ci_to_pass: yes - -coverage: - precision: 2 - round: down - range: "90...100" - -parsers: - gcov: - branch_detection: - conditional: yes - loop: yes - method: no - macro: no - -comment: - layout: "reach,diff,flags,tree" - behavior: default - require_changes: no diff --git a/src/field.rs b/src/field.rs index 382c3c8..81952a6 100644 --- a/src/field.rs +++ b/src/field.rs @@ -59,6 +59,12 @@ pub struct Tables { pub exp: [u8; 512], } +impl Default for Tables { + fn default() -> Self { + Self::new() + } +} + impl Tables { pub const fn new() -> Self { assert!(is_primitive(POLY), "POLY must be primitive"); @@ -91,12 +97,14 @@ impl GF256 { pub const TABLES: Tables = Tables::new(); pub fn add(self, other: Self) -> Self { - let _ = Self::POLY_CHECK; // const check will be amortized by the compiler + #[allow(path_statements)] + Self::POLY_CHECK; // const check will be amortized by the compiler Self(self.0 ^ other.0) } pub fn sub(self, other: Self) -> Self { - let _ = Self::POLY_CHECK; + #[allow(path_statements)] + Self::POLY_CHECK; Self(self.0 ^ other.0) } diff --git a/src/lib.rs b/src/lib.rs index f1bdf64..9369cd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,13 +242,11 @@ impl Sharks { if keys.is_empty() || (keys.len() < self.0 as usize) { Err("Not enough shares to recover original shares") + } else if self.0 == 1 { + // if threshold is 1, return the shares as is n times + Ok(values.iter().cloned().cycle().take(n).collect()) } else { - if self.0 == 1 { - // if threshold is 1, return the shares as is n times - Ok(values.iter().cloned().cycle().take(n).collect()) - } else { - Ok((1..=n).map(|i| math::reshare(&values, i)).collect()) - } + Ok((1..=n).map(|i| math::reshare(&values, i)).collect()) } } } diff --git a/src/math.rs b/src/math.rs index 1a26ed5..60d2754 100644 --- a/src/math.rs +++ b/src/math.rs @@ -120,7 +120,7 @@ pub fn random_polynomial( pub fn get_evaluator( polys: Vec>>, ) -> impl Iterator> { - (1..=u8::max_value()).map(GF256).map(move |x| Share { + (1..=u8::MAX).map(GF256).map(move |x| Share { x: x.clone(), y: polys .iter() From 2b8484a4685bb0216711dee80b3e17337ac6a747 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 01:56:44 +0530 Subject: [PATCH 06/17] update CI --- .github/workflows/rust.yml | 221 +++++++++++++++++++++++++++---------- 1 file changed, 162 insertions(+), 59 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 59d329b..e16a37b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,103 +1,206 @@ -on: [push, pull_request] - name: Rust +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always jobs: + rust-fmt: + name: rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: rustfmt + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: rust/rustfmt + + - name: Run Rust fmt + run: cargo fmt --all -- --check + + toml-fmt: + name: taplo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install taplo + run: | + curl -L https://github.com/tamasfe/taplo/releases/latest/download/taplo-linux-x86_64.gz -o taplo.gz + gunzip taplo.gz + chmod +x taplo + sudo mv taplo /usr/local/bin/ + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: rust/taplo + + - name: Run TOML fmt + run: taplo fmt --check + check: - name: Check + name: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - uses: actions-rs/cargo@v1 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - command: check + key: rust/check - test-std: - name: Test std + - name: Run cargo check + run: cargo check --workspace + + clippy: + name: clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - uses: actions-rs/cargo@v1 + components: clippy + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - command: test + key: rust/clippy - test-nostd: - name: Test no-std + - name: Build + run: cargo build --workspace + + - name: Clippy + run: cargo clippy --all-targets --all-features -- --deny warnings + + test-std: + name: test-std runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - uses: actions-rs/cargo@v1 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - command: test - args: --no-default-features + key: rust/test + + - name: Run tests + run: cargo test --verbose - format: - name: Format + test-nostd: + name: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - command: fmt - args: --all -- --check + key: rust/test - quality: - name: Quality + - name: Run tests + run: cargo test --verbose --no-default-features + + doc: + name: doc runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - run: rustup component add clippy - - uses: actions-rs/cargo@v1 + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - command: clippy - args: -- -D warnings + key: rust/doc + + - name: Run cargo doc to check for warnings + run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features coverage: - name: Coverage + name: coverage runs-on: ubuntu-latest + continue-on-error: true steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: stable - override: true - - uses: actions-rs/tarpaulin@v0.1 - - uses: codecov/codecov-action@v1.0.5 + components: llvm-tools-preview + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Run cargo-llvm-cov + run: cargo llvm-cov --all-features --workspace --html --output-dir target/coverage + + - name: Upload coverage to Artifacts + uses: actions/upload-artifact@v4 with: - token: ${{secrets.CODECOV_TOKEN}} + name: coverage-report + path: target/coverage - security: - name: Security + udeps: + name: udeps runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions-rs/audit-check@v1 + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 with: - token: ${{ secrets.GITHUB_TOKEN }} + key: rust/udeps + + - name: Install cargo-udeps + uses: taiki-e/install-action@v2 + with: + tool: cargo-udeps + + - name: Run cargo udeps + run: cargo +nightly udeps --all-targets --workspace From eacd99585c9ba39f0edb8152ab55a860f4588e64 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 02:04:27 +0530 Subject: [PATCH 07/17] attribution in readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 078564e..0c659b0 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,13 @@ [![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) [![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) [![Docs](https://docs.rs/gf256sss/badge.svg)](https://docs.rs/gf256sss) -[![Codecov](https://codecov.io/gh/multifactor/gf256sss/branch/master/graph/badge.svg)](https://codecov.io/gh/multifactor/gf256sss) [![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/multifactor/gf256sss/blob/master/COPYRIGHT) Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate +> [!Note] +> This repository is a fork of [c0dearm/sharks](https://github.com/c0dearm/sharks), but will be actively developed and maintained by Multifactor. + Documentation: - [API reference (docs.rs)](https://docs.rs/gf256sss) From 0968afb193d15e4ef8d84b497473eb5aa0f500df Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 13:01:12 +0530 Subject: [PATCH 08/17] fix rand crate dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4c5abec..951309c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ fuzzing = ["std", "arbitrary"] zeroize_memory = ["zeroize"] [dependencies] -rand = { version = "0.9.2", default-features = false, optional = true } +rand = { version = "0.9.2", default-features = false } hashbrown = "0.16" arbitrary = { version = "1.4", features = ["derive"], optional = true } zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } From dd7e464a6915c8f07b88003c22891e18919d3328 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 16:20:17 +0530 Subject: [PATCH 09/17] add primitive element generator in field --- CHANGELOG.md | 5 ++ Cargo.toml | 1 - benches/benchmarks.rs | 13 +-- fuzz/fuzz_targets/generate_shares.rs | 6 +- fuzz/fuzz_targets/recover.rs | 7 +- src/field.rs | 125 ++++++++++++++++++++++++++- src/lib.rs | 67 +++++++------- src/math.rs | 45 +++++++--- src/share.rs | 6 +- 9 files changed, 208 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44bfd4b..3b4ecd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.0] - 2025-10-20 +### Added +- Made GF256 field generic on irreducible polynomials used to construct the field +- Upgraded to latest dependencies + ## [0.5.0] - 2021-03-14 ### Added - Zeroize memory on drop for generated secret shares diff --git a/Cargo.toml b/Cargo.toml index 951309c..23fcd1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ include = [ [badges] maintenance = { status = "actively-developed" } -codecov = { repository = "multifactor/gf256sss" } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index 343270f..259e2ed 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -7,21 +7,22 @@ use sharks::{Share, Sharks}; const POLY: u16 = 0x11d_u16; fn dealer(c: &mut Criterion) { - let sharks = Sharks(255); - let mut dealer = sharks.dealer::(&[1]); + let sharks = Sharks::(255); + let mut dealer = sharks.dealer(&[1]); c.bench_function("obtain_shares_dealer", |b| { - b.iter(|| sharks.dealer::(black_box(&[1]))) + b.iter(|| sharks.dealer(black_box(&[1]))) }); c.bench_function("step_shares_dealer", |b| b.iter(|| dealer.next())); } fn recover(c: &mut Criterion) { - let sharks = Sharks(255); - let shares: Vec> = sharks.dealer::(&[1]).take(255).collect(); + let sharks = Sharks::(255); + let dealer = sharks.dealer(&[1]); + let shares = dealer.take(255).collect::>>(); c.bench_function("recover_secret", |b| { - b.iter(|| sharks.recover(black_box(shares.as_slice()))) + b.iter(|| sharks.recover(black_box(&shares))) }); } diff --git a/fuzz/fuzz_targets/generate_shares.rs b/fuzz/fuzz_targets/generate_shares.rs index fc8a13b..79d300e 100644 --- a/fuzz/fuzz_targets/generate_shares.rs +++ b/fuzz/fuzz_targets/generate_shares.rs @@ -13,8 +13,8 @@ struct Parameters { fuzz_target!(|params: Parameters| { const POLY: u16 = 0x11d_u16; - let sharks = Sharks(params.threshold); - let dealer = sharks.dealer::(¶ms.secret); + let sharks = Sharks::(params.threshold); + let dealer = sharks.dealer(¶ms.secret); - let _shares: Vec> = dealer.take(params.n_shares).collect(); + let _shares = dealer.take(params.n_shares).collect::>>(); }); diff --git a/fuzz/fuzz_targets/recover.rs b/fuzz/fuzz_targets/recover.rs index bed1637..7dfdad9 100644 --- a/fuzz/fuzz_targets/recover.rs +++ b/fuzz/fuzz_targets/recover.rs @@ -4,13 +4,14 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; use sharks::{Share, Sharks}; +const POLY: u16 = 0x11d_u16; #[derive(Debug, Arbitrary)] -struct Parameters { +struct Parameters { pub threshold: u8, pub shares: Vec>, } -fuzz_target!(|params: Parameters<0x11d>| { - let sharks = Sharks(params.threshold); +fuzz_target!(|params: Parameters| { + let sharks = Sharks::(params.threshold); let _secret = sharks.recover(¶ms.shares); }); diff --git a/src/field.rs b/src/field.rs index 81952a6..82fac00 100644 --- a/src/field.rs +++ b/src/field.rs @@ -30,9 +30,24 @@ const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { (result & 0xFF) as u8 } -const PRIMITIVE_POLYS: &[u16] = &[ - 0x11B, 0x11D, 0x12B, 0x12D, 0x14F, 0x15B, 0x15D, 0x163, 0x169, 0x16B, 0x187, 0x18D, 0x19F, - 0x1AF, 0x1C3, 0x1FD, +const fn gf256_pow(base: u8, exponent: u8, poly: u16) -> u8 { + let mut result = 1u8; + let mut base_val = base; + let mut exponent_val = exponent; + + while exponent_val > 0 { + if (exponent_val & 1) == 1 { + result = gf256_mul(result, base_val, poly); + } + base_val = gf256_mul(base_val, base_val, poly); + exponent_val >>= 1; + } + result +} + +pub const PRIMITIVE_POLYS: &[u16] = &[ + 0x11B, 0x11D, 0x12B, 0x12D, 0x14D, 0x15F, 0x163, 0x165, 0x169, 0x171, 0x187, 0x1A9, 0x1C3, + 0x1CF, 0x1E7, 0x1F5, ]; #[inline] @@ -65,13 +80,38 @@ impl Default for Tables { } } +const fn is_primitive_element(x: u8) -> bool { + const FACTORS: [u8; 3] = [3, 5, 17]; + + let mut i = 0; + while i < FACTORS.len() { + if gf256_pow(x, 255 / FACTORS[i], POLY) == 1 { + return false; + } + i += 1; + } + gf256_pow(x, 255, POLY) == 1 +} + +const fn find_generator() -> u8 { + let mut i = 1u16; + while i <= 255 { + if is_primitive_element::(i as u8) { + return i as u8; + } + i += 1; + } + panic!("No primitive element found"); +} + impl Tables { pub const fn new() -> Self { assert!(is_primitive(POLY), "POLY must be primitive"); let mut log = [0u8; 256]; let mut exp = [0u8; 512]; - let gen = 0x02_u8; + + let gen = find_generator::(); let mut i = 0usize; let mut x = 1u8; @@ -358,4 +398,81 @@ mod tests { fn non_primitive_poly(#[case] poly: u16) { assert!(!super::is_primitive(poly)); } + + // Shared test body generic over the primitive polynomial + fn run_ops_all() { + // Addition/Subtraction properties across full byte domain + let mut a_val = 0u16; + while a_val <= 255 { + let a = a_val as u8; + assert_eq!((GF256::<{ POLY }>(a) + GF256::<{ POLY }>(0)).0, a); + assert_eq!((GF256::<{ POLY }>(a) - GF256::<{ POLY }>(0)).0, a); + assert_eq!((GF256::<{ POLY }>(a) - GF256::<{ POLY }>(a)).0, 0); + assert_eq!((GF256::<{ POLY }>(a) + GF256::<{ POLY }>(a)).0, 0); + a_val += 1; + } + + // Sampled values to validate mul/div against reference implementation + let samples: [u8; 16] = [0, 1, 2, 3, 4, 7, 11, 13, 29, 63, 64, 95, 127, 128, 199, 255]; + + for &x in &samples { + for &y in &samples { + // mul equals reference bitwise implementation with POLY + let prod = (GF256::<{ POLY }>(x) * GF256::<{ POLY }>(y)).0; + let ref_prod = super::gf256_mul(x, y, POLY); + assert_eq!(prod, ref_prod); + + // Division inverse property + if y != 0 { + assert_eq!( + ((GF256::<{ POLY }>(x) * GF256::<{ POLY }>(y)) / GF256::<{ POLY }>(y)).0, + x + ); + } + + // Zero rules + assert_eq!((GF256::<{ POLY }>(0) * GF256::<{ POLY }>(y)).0, 0); + assert_eq!((GF256::<{ POLY }>(x) * GF256::<{ POLY }>(0)).0, 0); + + // Self-division for non-zero + if x != 0 { + assert_eq!((GF256::<{ POLY }>(x) / GF256::<{ POLY }>(x)).0, 1); + } else { + assert_eq!((GF256::<{ POLY }>(0) / GF256::<{ POLY }>(1)).0, 0); + } + } + } + } + + // Minimal macro: declare per-test constant and call the shared test body + macro_rules! gen_ops_tests { + ( $( ($poly:expr, $name:ident) ),+ $(,)? ) => { + $( + #[test] + fn $name() { + const POLY: u16 = $poly; + run_ops_all::<{ POLY }>(); + } + )+ + }; + } + + gen_ops_tests!( + (0x11B, ops_poly_11b), + (0x11D, ops_poly_11d), + (0x12B, ops_poly_12b), + (0x12D, ops_poly_12d), + (0x14D, ops_poly_14d), + (0x15F, ops_poly_15f), + (0x163, ops_poly_163), + (0x165, ops_poly_165), + (0x169, ops_poly_169), + (0x171, ops_poly_171), + (0x187, ops_poly_187), + (0x1A9, ops_poly_1a9), + (0x1C3, ops_poly_1c3), + (0x1CF, ops_poly_1cf), + (0x1E7, ops_poly_1e7), + (0x1F5, ops_poly_1f5), + ); } diff --git a/src/lib.rs b/src/lib.rs index 9369cd1..2047693 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,13 +6,13 @@ //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares -//! let sharks = Sharks(10); +//! let sharks = Sharks::(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! # #[cfg(feature = "std")] //! # { //! let dealer = sharks.dealer(&[1, 2, 3, 4]); //! // Get 10 shares -//! let shares: Vec> = dealer.take(10).collect(); +//! let shares = dealer.take(10).collect::>>(); //! // Recover the original secret! //! let secret = sharks.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); @@ -26,12 +26,12 @@ //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares -//! let sharks = Sharks(10); +//! let sharks = Sharks::(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -//! let dealer = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng); +//! let dealer = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng); //! // Get 10 shares -//! let shares: Vec> = dealer.take(10).collect(); +//! let shares = dealer.take(10).collect::>>(); //! // Recover the original secret! //! let secret = sharks.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); @@ -48,6 +48,7 @@ use alloc::vec::Vec; use hashbrown::HashSet; use field::GF256; +pub use field::PRIMITIVE_POLYS; pub use share::Share; #[cfg(feature = "std")] @@ -61,21 +62,21 @@ use rand::{rngs::StdRng, SeedableRng}; /// # use sharks::{ Sharks, Share }; /// # const POLY: u16 = 0x11d_u16; /// // Set a minimum threshold of 10 shares -/// let sharks = Sharks(10); +/// let sharks = Sharks::(10); /// // Obtain an iterator over the shares for secret [1, 2, 3, 4] /// # #[cfg(feature = "std")] /// # { /// let dealer = sharks.dealer(&[1, 2, 3, 4]); /// // Get 10 shares -/// let shares: Vec> = dealer.take(10).collect(); +/// let shares = dealer.take(10).collect::>>(); /// // Recover the original secret! -/// let secret = sharks.recover(shares.as_slice()).unwrap(); +/// let secret = sharks.recover(&shares).unwrap(); /// assert_eq!(secret, vec![1, 2, 3, 4]); /// # } /// ``` -pub struct Sharks(pub u8); +pub struct Sharks(pub u8); -impl Sharks { +impl Sharks { /// This method is useful when `std` is not available. For typical usage /// see the `dealer` method. /// @@ -88,13 +89,13 @@ impl Sharks { /// # use sharks::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks(3); + /// # let sharks = Sharks::(3); /// // Obtain an iterator over the shares for secret [1, 2] /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// let dealer = sharks.dealer_rng::(&[1, 2], &mut rng); + /// let dealer = sharks.dealer_rng::(&[1, 2], &mut rng); /// // Get 3 shares - /// let shares: Vec> = dealer.take(3).collect(); - pub fn dealer_rng( + /// let shares = dealer.take(3).collect::>>(); + pub fn dealer_rng( &self, secret: &[u8], rng: &mut R, @@ -115,13 +116,13 @@ impl Sharks { /// ``` /// # use sharks::{ Sharks, Share }; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks(3); + /// # let sharks = Sharks::(3); /// // Obtain an iterator over the shares for secret [1, 2] - /// let dealer = sharks.dealer::(&[1, 2]); + /// let dealer = sharks.dealer(&[1, 2]); /// // Get 3 shares - /// let shares: Vec> = dealer.take(3).collect(); + /// let shares = dealer.take(3).collect::>>(); #[cfg(feature = "std")] - pub fn dealer(&self, secret: &[u8]) -> impl Iterator> { + pub fn dealer(&self, secret: &[u8]) -> impl Iterator> { let mut rng = StdRng::from_os_rng(); self.dealer_rng(secret, &mut rng) } @@ -135,9 +136,9 @@ impl Sharks { /// # use sharks::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks(3); + /// # let sharks = Sharks::(3); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let mut shares: Vec> = sharks.dealer_rng::(&[1], &mut rng).take(3).collect(); + /// # let mut shares = sharks.dealer_rng::(&[1], &mut rng).take(3).collect::>>(); /// // Recover original secret from shares /// let mut secret = sharks.recover(&shares); /// // Secret correctly recovered @@ -147,7 +148,7 @@ impl Sharks { /// secret = sharks.recover(&shares); /// // Not enough shares to recover secret /// assert!(secret.is_err()); - pub fn recover<'a, T, const POLY: u16>(&self, shares: T) -> Result, &str> + pub fn recover<'a, T>(&self, shares: T) -> Result, &str> where T: IntoIterator>, T::IntoIter: Iterator>, @@ -185,9 +186,9 @@ impl Sharks { /// # use sharks::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks(2); + /// # let sharks = Sharks::(2); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let shares: Vec> = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng).take(3).collect(); + /// # let shares = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng).take(3).collect::>>(); /// // Recover original shares from original shares up to threshold shares /// let recovered_shares = sharks.recover_shares( /// [Some(&shares[0]), None, Some(&shares[2])], @@ -201,11 +202,7 @@ impl Sharks { /// let recovered_shares = sharks.recover_shares([Some(&shares[0]), None, None], 3); /// // Not enough shares to recover shares /// assert!(recovered_shares.is_err()); - pub fn recover_shares<'a, T, const POLY: u16>( - &self, - shares: T, - n: usize, - ) -> Result>, &str> + pub fn recover_shares<'a, T>(&self, shares: T, n: usize) -> Result>, &str> where T: IntoIterator>>, T::IntoIter: Iterator>>, @@ -258,9 +255,9 @@ mod tests { const POLY: u16 = 0x11d_u16; - impl Sharks { + impl Sharks { #[cfg(not(feature = "std"))] - fn make_shares(&self, secret: &[u8]) -> impl Iterator> { + fn make_shares(&self, secret: &[u8]) -> impl Iterator> { use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; let mut rng = ChaCha8Rng::from_seed([0x90; 32]); @@ -268,14 +265,14 @@ mod tests { } #[cfg(feature = "std")] - fn make_shares(&self, secret: &[u8]) -> impl Iterator> { + fn make_shares(&self, secret: &[u8]) -> impl Iterator> { self.dealer(secret) } } #[test] fn test_insufficient_shares_err() { - let sharks = Sharks(255); + let sharks = Sharks::(255); let shares: Vec> = sharks.make_shares(&[1]).take(254).collect(); let secret = sharks.recover(&shares); assert!(secret.is_err()); @@ -283,7 +280,7 @@ mod tests { #[test] fn test_duplicate_shares_err() { - let sharks = Sharks(255); + let sharks = Sharks::(255); let mut shares: Vec> = sharks.make_shares(&[1]).take(255).collect(); shares[1] = Share { x: shares[0].x.clone(), @@ -295,7 +292,7 @@ mod tests { #[test] fn test_integration_works() { - let sharks = Sharks(255); + let sharks = Sharks::(255); let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); let secret = sharks.recover(&shares).unwrap(); assert_eq!(secret, vec![1, 2, 3, 4]); @@ -303,7 +300,7 @@ mod tests { #[test] fn test_reshare_works() { - let sharks = Sharks(3); + let sharks = Sharks::(3); let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(4).collect(); let recovered_shares = sharks diff --git a/src/math.rs b/src/math.rs index 60d2754..ef9fb57 100644 --- a/src/math.rs +++ b/src/math.rs @@ -64,6 +64,7 @@ pub fn interpolate_polynomial( result } +/// Resharing a share at a given index. pub fn reshare(shares: &[Share], index: usize) -> Share { // assert that atleast 2 shares exist assert!( @@ -134,18 +135,21 @@ pub fn get_evaluator( #[cfg(test)] mod tests { - use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; + use super::{get_evaluator, interpolate, random_polynomial, reshare, Share, GF256}; use alloc::{vec, vec::Vec}; use rand_chacha::rand_core::SeedableRng; + use rstest::rstest; const POLY: u16 = 0x11d_u16; - #[test] - fn random_polynomial_works() { - let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - let poly = random_polynomial::<_, POLY>(GF256(1), 3, &mut rng); - assert_eq!(poly.len(), 3); - assert_eq!(poly[2], GF256(1)); + #[rstest] + #[case([0x90; 32], 3)] + #[case([0x10; 32], 8)] + #[case([0x20; 32], 16)] + fn random_polynomial_works(#[case] seed: [u8; 32], #[case] k: usize) { + let mut rng = rand_chacha::ChaCha8Rng::from_seed(seed); + let poly = random_polynomial::<_, POLY>(GF256(1), k as u8, &mut rng); + assert_eq!(poly.len(), k); } #[test] @@ -158,13 +162,30 @@ mod tests { ); } - #[test] - fn interpolate_works() { - let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - let poly = random_polynomial(GF256(185), 10, &mut rng); + #[rstest] + #[case([0x90; 32], 10)] + #[case([0x10; 32], 8)] + #[case([0x20; 32], 16)] + fn interpolate_works(#[case] seed: [u8; 32], #[case] k: usize) { + let mut rng = rand_chacha::ChaCha8Rng::from_seed(seed); + let poly = random_polynomial(GF256(185), k as u8, &mut rng); let iter = get_evaluator(vec![poly]); - let shares: Vec> = iter.take(10).collect(); + let shares: Vec> = iter.take(k).collect(); let root = interpolate(&shares); assert_eq!(root, vec![185]); } + + #[rstest] + #[case([0x90; 32], 10, 2)] + #[case([0x90; 32], 10, 5)] + #[case([0x10; 32], 8, 7)] + #[case([0x10; 32], 8, 8)] + fn reshare_works(#[case] seed: [u8; 32], #[case] k: usize, #[case] index: usize) { + let mut rng = rand_chacha::ChaCha8Rng::from_seed(seed); + let poly = random_polynomial(GF256(185), k as u8, &mut rng); + let iter = get_evaluator(vec![poly]); + let shares: Vec> = iter.take(k).collect(); + let share = reshare(&shares, index); + assert_eq!(share.y, shares[index - 1].y); + } } diff --git a/src/share.rs b/src/share.rs index 3721d6a..4f77d5a 100644 --- a/src/share.rs +++ b/src/share.rs @@ -20,9 +20,9 @@ use zeroize::Zeroize; /// /// // Transmit the share bytes to a printer /// # const POLY: u16 = 0x11d_u16; -/// let sharks = Sharks(3); +/// let sharks = Sharks::(3); /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -/// let dealer = sharks.dealer_rng::(&[1, 2, 3], &mut rng); +/// let dealer = sharks.dealer_rng::(&[1, 2, 3], &mut rng); /// /// // Get 5 shares and print paper keys /// for s in dealer.take(5) { @@ -72,8 +72,8 @@ mod tests { use super::{Share, GF256}; use alloc::{vec, vec::Vec}; use core::convert::TryFrom; - const POLY: u16 = 0x11d_u16; + #[test] fn vec_from_share_works() { let share = Share:: { From 13482121f08962536608ea9619a4c50ac1958f3b Mon Sep 17 00:00:00 2001 From: lonerapier Date: Fri, 17 Oct 2025 16:56:05 +0530 Subject: [PATCH 10/17] rename repo --- Cargo.toml | 2 +- README.md | 2 +- benches/benchmarks.rs | 3 +-- fuzz/Cargo.toml | 4 ++-- fuzz/fuzz_targets/deserialize_share.rs | 2 +- fuzz/fuzz_targets/generate_shares.rs | 2 +- fuzz/fuzz_targets/recover.rs | 2 +- fuzz/fuzz_targets/serialize_share.rs | 2 +- src/lib.rs | 14 +++++++------- src/share.rs | 2 +- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23fcd1a..4f1ead8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sharks" +name = "gf256sss" version = "0.6.0" authors = [ "Aitor Ruano ", diff --git a/README.md b/README.md index 0c659b0..679826a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sharks +# GF256SSS [![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) [![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index 259e2ed..e2f568d 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,8 +1,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use std::convert::TryFrom; use std::hint::black_box; -use sharks::{Share, Sharks}; +use gf256sss::{Share, Sharks}; const POLY: u16 = 0x11d_u16; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f9bf171..ba76324 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "sharks-fuzz" +name = "gf256sss-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false @@ -13,7 +13,7 @@ cargo-fuzz = true libfuzzer-sys = "0.4" arbitrary = { version = "1.4", features = ["derive"] } -[dependencies.sharks] +[dependencies.gf256sss] path = ".." features = ["fuzzing"] diff --git a/fuzz/fuzz_targets/deserialize_share.rs b/fuzz/fuzz_targets/deserialize_share.rs index 95abb1d..eb33656 100644 --- a/fuzz/fuzz_targets/deserialize_share.rs +++ b/fuzz/fuzz_targets/deserialize_share.rs @@ -1,7 +1,7 @@ #![no_main] use core::convert::TryFrom; +use gf256sss::Share; use libfuzzer_sys::fuzz_target; -use sharks::Share; fuzz_target!(|data: &[u8]| { const POLY: u16 = 0x11d_u16; diff --git a/fuzz/fuzz_targets/generate_shares.rs b/fuzz/fuzz_targets/generate_shares.rs index 79d300e..484445a 100644 --- a/fuzz/fuzz_targets/generate_shares.rs +++ b/fuzz/fuzz_targets/generate_shares.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use sharks::{Share, Sharks}; +use gf256sss::{Share, Sharks}; #[derive(Debug, Arbitrary)] struct Parameters { diff --git a/fuzz/fuzz_targets/recover.rs b/fuzz/fuzz_targets/recover.rs index 7dfdad9..4e67c44 100644 --- a/fuzz/fuzz_targets/recover.rs +++ b/fuzz/fuzz_targets/recover.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use sharks::{Share, Sharks}; +use gf256sss::{Share, Sharks}; const POLY: u16 = 0x11d_u16; #[derive(Debug, Arbitrary)] diff --git a/fuzz/fuzz_targets/serialize_share.rs b/fuzz/fuzz_targets/serialize_share.rs index bdca037..a97b635 100644 --- a/fuzz/fuzz_targets/serialize_share.rs +++ b/fuzz/fuzz_targets/serialize_share.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use sharks::Share; +use gf256sss::Share; const POLY: u16 = 0x11d_u16; fuzz_target!(|share: Share| { diff --git a/src/lib.rs b/src/lib.rs index 2047693..4217279 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! Usage example (std): //! ``` -//! use sharks::{ Sharks, Share }; +//! use gf256sss::{ Sharks, Share }; //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares @@ -21,7 +21,7 @@ //! //! Usage example (no std): //! ``` -//! use sharks::{ Sharks, Share }; +//! use gf256sss::{ Sharks, Share }; //! use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; //! //! # const POLY: u16 = 0x11d_u16; @@ -59,7 +59,7 @@ use rand::{rngs::StdRng, SeedableRng}; /// /// Usage example: /// ``` -/// # use sharks::{ Sharks, Share }; +/// # use gf256sss::{ Sharks, Share }; /// # const POLY: u16 = 0x11d_u16; /// // Set a minimum threshold of 10 shares /// let sharks = Sharks::(10); @@ -86,7 +86,7 @@ impl Sharks { /// /// Example: /// ``` - /// # use sharks::{ Sharks, Share }; + /// # use gf256sss::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks::(3); @@ -114,7 +114,7 @@ impl Sharks { /// /// Example: /// ``` - /// # use sharks::{ Sharks, Share }; + /// # use gf256sss::{ Sharks, Share }; /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks::(3); /// // Obtain an iterator over the shares for secret [1, 2] @@ -133,7 +133,7 @@ impl Sharks { /// /// Example: /// ``` - /// # use sharks::{ Sharks, Share }; + /// # use gf256sss::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks::(3); @@ -183,7 +183,7 @@ impl Sharks { /// /// Example: /// ``` - /// # use sharks::{ Sharks, Share }; + /// # use gf256sss::{ Sharks, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sharks = Sharks::(2); diff --git a/src/share.rs b/src/share.rs index 4f77d5a..1772fab 100644 --- a/src/share.rs +++ b/src/share.rs @@ -12,7 +12,7 @@ use zeroize::Zeroize; /// /// Usage example: /// ``` -/// use sharks::{Sharks, Share}; +/// use gf256sss::{Sharks, Share}; /// use core::convert::TryFrom; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # fn send_to_printer(_: Vec) {} From e6fc2223bf27222620a87c4ab2b74a8bdbbfa6d9 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Tue, 21 Oct 2025 11:45:32 +0530 Subject: [PATCH 11/17] remove changelog and add release workflow --- .github/workflows/release.yml | 49 ++++++++++++++++++++++ .github/workflows/rust.yml | 22 +++++----- CHANGELOG.md | 76 ----------------------------------- 3 files changed, 60 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/release.yml delete mode 100644 CHANGELOG.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a79af18 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release + +permissions: + pull-requests: write + contents: write + +on: + push: + branches: + - main + +jobs: + release: + name: release + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + + - name: Install cargo-semver-checks + uses: taiki-e/install-action@cargo-binstall + with: + tool: cargo-semver-checks + + - name: Check semver + run: cargo semver-checks check-release + continue-on-error: true + + - name: Update Cargo.lock + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "chore: update Cargo.lock" + file_pattern: "Cargo.lock" + + - name: Run release-plz + uses: MarcoIeni/release-plz-action@v0.5.41 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e16a37b..98fdf54 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,12 +17,12 @@ jobs: name: rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master with: - toolchain: nightly + toolchain: stable components: rustfmt - name: Rust Cache @@ -37,7 +37,7 @@ jobs: name: taplo runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install taplo run: | @@ -58,7 +58,7 @@ jobs: name: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -77,7 +77,7 @@ jobs: name: clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -100,7 +100,7 @@ jobs: name: test-std runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -119,7 +119,7 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -138,7 +138,7 @@ jobs: name: doc runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -158,7 +158,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master @@ -185,12 +185,12 @@ jobs: name: udeps runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@master with: - toolchain: nightly + toolchain: stable - name: Rust Cache uses: Swatinem/rust-cache@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 3b4ecd1..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,76 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.6.0] - 2025-10-20 -### Added -- Made GF256 field generic on irreducible polynomials used to construct the field -- Upgraded to latest dependencies - -## [0.5.0] - 2021-03-14 -### Added -- Zeroize memory on drop for generated secret shares - -## [0.4.3] - 2021-02-04 -### Changed -- Upgraded project dependencies - -## [0.4.2] - 2020-08-03 -### Fixed -- Small fix in docs - -## [0.4.1] - 2020-04-23 -### Added -- Fuzz tests - -### Fixed -- Unexpected panic when trying to recover secret from different length shares -- Unexpected panic when trying to convert less than 2 bytes to `Share` - -## [0.4.0] - 2020-04-02 -### Added -- It is now possible to compile without `std` with `--no-default-features` - -## [0.3.3] - 2020-03-23 -### Changed -- Fix codecov badge - -## [0.3.2] - 2020-03-09 -### Changed -- Share structs now derives the `Clone` trait - -## [0.3.1] - 2020-01-23 -### Changed -- Sharks recover method now accepts any iterable collection - -## [0.3.0] - 2020-01-22 -### Added -- Share struct which allows to convert from/to byte vectors - -### Changed -- Methods use the new Share struct, instead of (GF245, Vec) tuples - -## [0.2.0] - 2020-01-21 -### Added -- Computations performed over GF256 (much faster) -- Secret can now be arbitrarily long - -### Changed -- Some method names and docs -- Maximum number of shares enforced by Rust static types instead of conditional branching - -### Removed -- Modular arithmetic around Mersenne primes - -## [0.1.1] - 2020-01-13 -### Fixed -- Typo in cargo description - -### Removed -- Maintenance badges in cargo file - -## [0.1.0] - 2020-01-13 -### Added -- Initial version From d739e443d1245f9d24a9f64d9bf102736a20f7aa Mon Sep 17 00:00:00 2001 From: lonerapier Date: Tue, 21 Oct 2025 11:50:26 +0530 Subject: [PATCH 12/17] add acknowledgment --- COPYRIGHT | 12 ------------ Cargo.toml | 13 +++---------- README.md | 13 ++++++++----- fuzz/Cargo.toml | 2 +- 4 files changed, 12 insertions(+), 28 deletions(-) delete mode 100644 COPYRIGHT diff --git a/COPYRIGHT b/COPYRIGHT deleted file mode 100644 index 38c3e0d..0000000 --- a/COPYRIGHT +++ /dev/null @@ -1,12 +0,0 @@ -Copyrights in the Sharks project are retained by their contributors. No -copyright assignment is required to contribute to the Sharks project. - -For full authorship information, see the version control history. - -Except as otherwise noted (below and/or in individual files), Sharks is -licensed under the Apache License, Version 2.0 or - or the MIT license - or , at your option. - -The Sharks project includes code from the Rust project -published under these same licenses. diff --git a/Cargo.toml b/Cargo.toml index 4f1ead8..47a13e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "gf256sss" -version = "0.6.0" +version = "0.1.0" authors = [ - "Aitor Ruano ", "Multifactor, Inc. ", + "Sambhav Dusad ", ] description = "Fast, small and secure Shamir's Secret Sharing library crate" homepage = "https://github.com/multifactor/gf256sss" @@ -14,14 +14,7 @@ keywords = ["shamir", "secret", "sharing", "share", "crypto"] categories = ["algorithms", "cryptography", "mathematics"] license = "MIT OR Apache-2.0" edition = "2021" -include = [ - "src/**", - "README.md", - "CHANGELOG.md", - "COPYRIGHT", - "LICENSE-APACHE", - "LICENSE-MIT", -] +include = ["src/**", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] [badges] maintenance = { status = "actively-developed" } diff --git a/README.md b/README.md index 679826a..cc9bd24 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) [![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) [![Docs](https://docs.rs/gf256sss/badge.svg)](https://docs.rs/gf256sss) -[![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/multifactor/gf256sss/blob/master/COPYRIGHT) +[![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](LICENSE-MIT) Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate @@ -19,14 +19,14 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -gf256sss = "0.4" +gf256sss = "0.1" ``` If your environment doesn't support `std`: ```toml [dependencies] -gf256sss = { version = "0.6", default-features = false } +gf256sss = { version = "0.1", default-features = false } ``` To get started using gf256sss, see the [Rust docs](https://docs.rs/gf256sss) @@ -81,5 +81,8 @@ Many thanks! gf256sss is distributed under the terms of both the MIT license and the Apache License (Version 2.0). -See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and -[COPYRIGHT](COPYRIGHT) for details. +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. + +# Acknowledgments + +This project is derived from the excellent work in the original [sharks](https://github.com/c0dearm/sharks) repository by Aitor Ruano (`c0dearm`). We appreciate Aitor's foundational contributions, on which this crate is based. diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index ba76324..a6a15ed 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -4,7 +4,7 @@ name = "gf256sss-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false -edition = "2021" +edition = "2024" [package.metadata] cargo-fuzz = true From 5db05d835e09f43278b0758e3487b5f465833d52 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Tue, 21 Oct 2025 11:51:30 +0530 Subject: [PATCH 13/17] rename Sharks to SecretSharing --- Cargo.toml | 4 +- README.md | 10 ++- benches/benchmarks.rs | 14 ++--- fuzz/fuzz_targets/generate_shares.rs | 6 +- fuzz/fuzz_targets/recover.rs | 6 +- src/field.rs | 6 +- src/lib.rs | 94 ++++++++++++++-------------- src/share.rs | 8 +-- 8 files changed, 76 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 47a13e2..4fc27ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,9 @@ zeroize_memory = ["zeroize"] [dependencies] rand = { version = "0.9.2", default-features = false } -hashbrown = "0.16" +hashbrown = { version = "0.16", default-features = false, features = [ + "default-hasher", +] } arbitrary = { version = "1.4", features = ["derive"], optional = true } zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } diff --git a/README.md b/README.md index cc9bd24..278a46e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) [![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) [![Docs](https://docs.rs/gf256sss/badge.svg)](https://docs.rs/gf256sss) -[![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](LICENSE-MIT) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT) Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate @@ -62,11 +62,9 @@ You can run them with `cargo test` and `cargo bench`. ### Benchmark results [min mean max] -| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes | -| ---------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | -| Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz | [1.4321 us 1.4339 us 1.4357 us] | [1.3385 ns 1.3456 ns 1.3552 ns] | [228.77 us 232.17 us 236.23 us] | [24.688 ns 25.083 ns 25.551 ns] | [22.832 ns 22.910 ns 22.995 ns] | -| Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz | [1.3439 us 1.3499 us 1.3562 us] | [1.5416 ns 1.5446 ns 1.5481 ns] | [197.46 us 198.37 us 199.22 us] | [20.455 ns 20.486 ns 20.518 ns] | [18.726 ns 18.850 ns 18.993 ns] | -| Apple M1 ARM (Macbook Air) | [3.3367 us 3.3629 us 3.4058 us] | [741.75 ps 742.65 ps 743.52 ps] | [210.14 us 210.23 us 210.34 us] | [27.567 ns 27.602 ns 27.650 ns] | [26.716 ns 26.735 ns 26.755 ns] | +| CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes | +| ------------ | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | +| Apple M1 Pro | [2.6976 µs 2.7007 µs 2.7039 µs] | [938.79 ps 939.83 ps 941.04 ps] | [190.00 µs 190.46 µs 191.06 µs] | [31.176 ns 31.311 ns 31.529 ns] | [23.196 ns 23.211 ns 23.230 ns] | # Contributing diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index e2f568d..c658782 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,27 +1,27 @@ use criterion::{criterion_group, criterion_main, Criterion}; use std::hint::black_box; -use gf256sss::{Share, Sharks}; +use gf256sss::{SecretSharing, Share}; const POLY: u16 = 0x11d_u16; fn dealer(c: &mut Criterion) { - let sharks = Sharks::(255); - let mut dealer = sharks.dealer(&[1]); + let sss = SecretSharing::(255); + let mut dealer = sss.dealer(&[1]); c.bench_function("obtain_shares_dealer", |b| { - b.iter(|| sharks.dealer(black_box(&[1]))) + b.iter(|| sss.dealer(black_box(&[1]))) }); c.bench_function("step_shares_dealer", |b| b.iter(|| dealer.next())); } fn recover(c: &mut Criterion) { - let sharks = Sharks::(255); - let dealer = sharks.dealer(&[1]); + let sss = SecretSharing::(255); + let dealer = sss.dealer(&[1]); let shares = dealer.take(255).collect::>>(); c.bench_function("recover_secret", |b| { - b.iter(|| sharks.recover(black_box(&shares))) + b.iter(|| sss.recover(black_box(&shares))) }); } diff --git a/fuzz/fuzz_targets/generate_shares.rs b/fuzz/fuzz_targets/generate_shares.rs index 484445a..30bdeff 100644 --- a/fuzz/fuzz_targets/generate_shares.rs +++ b/fuzz/fuzz_targets/generate_shares.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use gf256sss::{Share, Sharks}; +use gf256sss::{SecretSharing, Share}; #[derive(Debug, Arbitrary)] struct Parameters { @@ -13,8 +13,8 @@ struct Parameters { fuzz_target!(|params: Parameters| { const POLY: u16 = 0x11d_u16; - let sharks = Sharks::(params.threshold); - let dealer = sharks.dealer(¶ms.secret); + let sss = SecretSharing::(params.threshold); + let dealer = sss.dealer(¶ms.secret); let _shares = dealer.take(params.n_shares).collect::>>(); }); diff --git a/fuzz/fuzz_targets/recover.rs b/fuzz/fuzz_targets/recover.rs index 4e67c44..69e0037 100644 --- a/fuzz/fuzz_targets/recover.rs +++ b/fuzz/fuzz_targets/recover.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use gf256sss::{Share, Sharks}; +use gf256sss::{SecretSharing, Share}; const POLY: u16 = 0x11d_u16; #[derive(Debug, Arbitrary)] @@ -12,6 +12,6 @@ struct Parameters { } fuzz_target!(|params: Parameters| { - let sharks = Sharks::(params.threshold); - let _secret = sharks.recover(¶ms.shares); + let sss = SecretSharing::(params.threshold); + let _secret = sss.recover(¶ms.shares); }); diff --git a/src/field.rs b/src/field.rs index 82fac00..9d92af4 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,5 +1,5 @@ // Basic operations overrided for the Galois Field 256 (2**8) -// Uses pre-calculated tables for 0x11d primitive polynomial (x**8 + x**4 + x**3 + x**2 + 1) +// Implements the operations over general irreducible polynomials. use core::iter::{Product, Sum}; use core::ops::{Add, Div, Mul, Sub}; @@ -10,6 +10,7 @@ use arbitrary::Arbitrary; #[cfg(feature = "zeroize_memory")] use zeroize::Zeroize; +#[inline] const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { let mut result = 0u16; let mut a_val = a as u16; @@ -30,6 +31,7 @@ const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { (result & 0xFF) as u8 } +#[inline] const fn gf256_pow(base: u8, exponent: u8, poly: u16) -> u8 { let mut result = 1u8; let mut base_val = base; @@ -80,6 +82,7 @@ impl Default for Tables { } } +#[inline] const fn is_primitive_element(x: u8) -> bool { const FACTORS: [u8; 3] = [3, 5, 17]; @@ -93,6 +96,7 @@ const fn is_primitive_element(x: u8) -> bool { gf256_pow(x, 255, POLY) == 1 } +#[inline] const fn find_generator() -> u8 { let mut i = 1u16; while i <= 255 { diff --git a/src/lib.rs b/src/lib.rs index 4217279..a5e2ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,38 +2,38 @@ //! //! Usage example (std): //! ``` -//! use gf256sss::{ Sharks, Share }; +//! use gf256sss::{ SecretSharing, Share }; //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares -//! let sharks = Sharks::(10); +//! let sss = SecretSharing::(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! # #[cfg(feature = "std")] //! # { -//! let dealer = sharks.dealer(&[1, 2, 3, 4]); +//! let dealer = sss.dealer(&[1, 2, 3, 4]); //! // Get 10 shares //! let shares = dealer.take(10).collect::>>(); //! // Recover the original secret! -//! let secret = sharks.recover(shares.as_slice()).unwrap(); +//! let secret = sss.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); //! # } //! ``` //! //! Usage example (no std): //! ``` -//! use gf256sss::{ Sharks, Share }; +//! use gf256sss::{ SecretSharing, Share }; //! use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares -//! let sharks = Sharks::(10); +//! let sss = SecretSharing::(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -//! let dealer = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng); +//! let dealer = sss.dealer_rng::(&[1, 2, 3, 4], &mut rng); //! // Get 10 shares //! let shares = dealer.take(10).collect::>>(); //! // Recover the original secret! -//! let secret = sharks.recover(shares.as_slice()).unwrap(); +//! let secret = sss.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); //! ``` #![cfg_attr(not(feature = "std"), no_std)] @@ -59,24 +59,24 @@ use rand::{rngs::StdRng, SeedableRng}; /// /// Usage example: /// ``` -/// # use gf256sss::{ Sharks, Share }; +/// # use gf256sss::{ SecretSharing, Share }; /// # const POLY: u16 = 0x11d_u16; /// // Set a minimum threshold of 10 shares -/// let sharks = Sharks::(10); +/// let sss = SecretSharing::(10); /// // Obtain an iterator over the shares for secret [1, 2, 3, 4] /// # #[cfg(feature = "std")] /// # { -/// let dealer = sharks.dealer(&[1, 2, 3, 4]); +/// let dealer = sss.dealer(&[1, 2, 3, 4]); /// // Get 10 shares /// let shares = dealer.take(10).collect::>>(); /// // Recover the original secret! -/// let secret = sharks.recover(&shares).unwrap(); +/// let secret = sss.recover(&shares).unwrap(); /// assert_eq!(secret, vec![1, 2, 3, 4]); /// # } /// ``` -pub struct Sharks(pub u8); +pub struct SecretSharing(pub u8); -impl Sharks { +impl SecretSharing { /// This method is useful when `std` is not available. For typical usage /// see the `dealer` method. /// @@ -86,13 +86,13 @@ impl Sharks { /// /// Example: /// ``` - /// # use gf256sss::{ Sharks, Share }; + /// # use gf256sss::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks::(3); + /// # let sss = SecretSharing::(3); /// // Obtain an iterator over the shares for secret [1, 2] /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// let dealer = sharks.dealer_rng::(&[1, 2], &mut rng); + /// let dealer = sss.dealer_rng::(&[1, 2], &mut rng); /// // Get 3 shares /// let shares = dealer.take(3).collect::>>(); pub fn dealer_rng( @@ -114,11 +114,11 @@ impl Sharks { /// /// Example: /// ``` - /// # use gf256sss::{ Sharks, Share }; + /// # use gf256sss::{ SecretSharing, Share }; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks::(3); + /// # let sss = SecretSharing::(3); /// // Obtain an iterator over the shares for secret [1, 2] - /// let dealer = sharks.dealer(&[1, 2]); + /// let dealer = sss.dealer(&[1, 2]); /// // Get 3 shares /// let shares = dealer.take(3).collect::>>(); #[cfg(feature = "std")] @@ -133,19 +133,19 @@ impl Sharks { /// /// Example: /// ``` - /// # use gf256sss::{ Sharks, Share }; + /// # use gf256sss::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks::(3); + /// # let sss = SecretSharing::(3); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let mut shares = sharks.dealer_rng::(&[1], &mut rng).take(3).collect::>>(); + /// # let mut shares = sss.dealer_rng::(&[1], &mut rng).take(3).collect::>>(); /// // Recover original secret from shares - /// let mut secret = sharks.recover(&shares); + /// let mut secret = sss.recover(&shares); /// // Secret correctly recovered /// assert!(secret.is_ok()); /// // Remove shares for demonstration purposes /// shares.clear(); - /// secret = sharks.recover(&shares); + /// secret = sss.recover(&shares); /// // Not enough shares to recover secret /// assert!(secret.is_err()); pub fn recover<'a, T>(&self, shares: T) -> Result, &str> @@ -183,14 +183,14 @@ impl Sharks { /// /// Example: /// ``` - /// # use gf256sss::{ Sharks, Share }; + /// # use gf256sss::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; - /// # let sharks = Sharks::(2); + /// # let sss = SecretSharing::(2); /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); - /// # let shares = sharks.dealer_rng::(&[1, 2, 3, 4], &mut rng).take(3).collect::>>(); + /// # let shares = sss.dealer_rng::(&[1, 2, 3, 4], &mut rng).take(3).collect::>>(); /// // Recover original shares from original shares up to threshold shares - /// let recovered_shares = sharks.recover_shares( + /// let recovered_shares = sss.recover_shares( /// [Some(&shares[0]), None, Some(&shares[2])], /// 3, /// ); @@ -199,7 +199,7 @@ impl Sharks { /// let recovered_shares = recovered_shares.unwrap(); /// assert_eq!(recovered_shares.len(), 3); /// // Remove shares for demonstration purposes - /// let recovered_shares = sharks.recover_shares([Some(&shares[0]), None, None], 3); + /// let recovered_shares = sss.recover_shares([Some(&shares[0]), None, None], 3); /// // Not enough shares to recover shares /// assert!(recovered_shares.is_err()); pub fn recover_shares<'a, T>(&self, shares: T, n: usize) -> Result>, &str> @@ -250,12 +250,12 @@ impl Sharks { #[cfg(test)] mod tests { - use super::{Share, Sharks}; + use super::{SecretSharing, Share}; use alloc::{vec, vec::Vec}; const POLY: u16 = 0x11d_u16; - impl Sharks { + impl SecretSharing { #[cfg(not(feature = "std"))] fn make_shares(&self, secret: &[u8]) -> impl Iterator> { use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; @@ -272,38 +272,38 @@ mod tests { #[test] fn test_insufficient_shares_err() { - let sharks = Sharks::(255); - let shares: Vec> = sharks.make_shares(&[1]).take(254).collect(); - let secret = sharks.recover(&shares); + let sss = SecretSharing::(255); + let shares: Vec> = sss.make_shares(&[1]).take(254).collect(); + let secret = sss.recover(&shares); assert!(secret.is_err()); } #[test] fn test_duplicate_shares_err() { - let sharks = Sharks::(255); - let mut shares: Vec> = sharks.make_shares(&[1]).take(255).collect(); + let sss = SecretSharing::(255); + let mut shares: Vec> = sss.make_shares(&[1]).take(255).collect(); shares[1] = Share { x: shares[0].x.clone(), y: shares[0].y.clone(), }; - let secret = sharks.recover(&shares); + let secret = sss.recover(&shares); assert!(secret.is_err()); } #[test] fn test_integration_works() { - let sharks = Sharks::(255); - let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); - let secret = sharks.recover(&shares).unwrap(); + let sss = SecretSharing::(255); + let shares: Vec> = sss.make_shares(&[1, 2, 3, 4]).take(255).collect(); + let secret = sss.recover(&shares).unwrap(); assert_eq!(secret, vec![1, 2, 3, 4]); } #[test] fn test_reshare_works() { - let sharks = Sharks::(3); - let shares: Vec> = sharks.make_shares(&[1, 2, 3, 4]).take(4).collect(); + let sss = SecretSharing::(3); + let shares: Vec> = sss.make_shares(&[1, 2, 3, 4]).take(4).collect(); - let recovered_shares = sharks + let recovered_shares = sss .recover_shares( [Some(&shares[0]), None, Some(&shares[2]), Some(&shares[3])], 4, @@ -316,7 +316,7 @@ mod tests { assert_eq!(recovered_share.y, share.y); } - let recovered_shares = sharks + let recovered_shares = sss .recover_shares( [None, Some(&shares[1]), Some(&shares[2]), Some(&shares[3])], 4, @@ -329,7 +329,7 @@ mod tests { assert_eq!(recovered_share.y, share.y); } - let recovered_shares = sharks + let recovered_shares = sss .recover_shares( [Some(&shares[0]), Some(&shares[1]), Some(&shares[2]), None], 4, @@ -343,7 +343,7 @@ mod tests { } let recovered_shares = - sharks.recover_shares([Some(&shares[0]), None, None, Some(&shares[3])], 4); + sss.recover_shares([Some(&shares[0]), None, None, Some(&shares[3])], 4); assert!(recovered_shares.is_err()); } } diff --git a/src/share.rs b/src/share.rs index 1772fab..aa863f4 100644 --- a/src/share.rs +++ b/src/share.rs @@ -12,7 +12,7 @@ use zeroize::Zeroize; /// /// Usage example: /// ``` -/// use gf256sss::{Sharks, Share}; +/// use gf256sss::{SecretSharing, Share}; /// use core::convert::TryFrom; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # fn send_to_printer(_: Vec) {} @@ -20,9 +20,9 @@ use zeroize::Zeroize; /// /// // Transmit the share bytes to a printer /// # const POLY: u16 = 0x11d_u16; -/// let sharks = Sharks::(3); +/// let sss = SecretSharing::(3); /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); -/// let dealer = sharks.dealer_rng::(&[1, 2, 3], &mut rng); +/// let dealer = sss.dealer_rng::(&[1, 2, 3], &mut rng); /// /// // Get 5 shares and print paper keys /// for s in dealer.take(5) { @@ -32,7 +32,7 @@ use zeroize::Zeroize; /// // Get share bytes from an external source and recover secret /// let shares_bytes: Vec> = ask_shares(); /// let shares: Vec> = shares_bytes.iter().map(|s| Share::::try_from(s.as_slice()).unwrap()).collect(); -/// let secret = sharks.recover(&shares).unwrap(); +/// let secret = sss.recover(&shares).unwrap(); #[derive(Clone)] #[cfg_attr(feature = "fuzzing", derive(Arbitrary, Debug))] #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] From 751f1c871e00331a610adca2ffbd26c8df36ba65 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Tue, 21 Oct 2025 19:08:11 +0530 Subject: [PATCH 14/17] revert rng updates --- Cargo.toml | 6 +++--- src/lib.rs | 5 +---- src/math.rs | 5 +++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4fc27ed..efef431 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,12 @@ maintenance = { status = "actively-developed" } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["std", "zeroize_memory"] -std = ["rand/std", "rand/std_rng", "rand/os_rng"] +std = ["rand/std", "rand/std_rng"] fuzzing = ["std", "arbitrary"] zeroize_memory = ["zeroize"] [dependencies] -rand = { version = "0.9.2", default-features = false } +rand = { version = "0.8.5", default-features = false } hashbrown = { version = "0.16", default-features = false, features = [ "default-hasher", ] } @@ -36,7 +36,7 @@ zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } [dev-dependencies] criterion = "0.7" -rand_chacha = "0.9" +rand_chacha = "0.3" rstest = "0.26.1" [[bench]] diff --git a/src/lib.rs b/src/lib.rs index a5e2ccd..0eb6b06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,9 +51,6 @@ use field::GF256; pub use field::PRIMITIVE_POLYS; pub use share::Share; -#[cfg(feature = "std")] -use rand::{rngs::StdRng, SeedableRng}; - /// Tuple struct which implements methods to generate shares and recover secrets over a 256 bits Galois Field. /// Its only parameter is the minimum shares threshold. /// @@ -123,7 +120,7 @@ impl SecretSharing { /// let shares = dealer.take(3).collect::>>(); #[cfg(feature = "std")] pub fn dealer(&self, secret: &[u8]) -> impl Iterator> { - let mut rng = StdRng::from_os_rng(); + let mut rng = rand::thread_rng(); self.dealer_rng(secret, &mut rng) } diff --git a/src/math.rs b/src/math.rs index ef9fb57..41264e2 100644 --- a/src/math.rs +++ b/src/math.rs @@ -2,7 +2,8 @@ use alloc::vec::Vec; -use rand::distr::{Distribution, Uniform}; +use rand::distributions::Distribution; +use rand::distributions::Uniform; use super::field::GF256; use super::share::Share; @@ -104,7 +105,7 @@ pub fn random_polynomial( ) -> Vec> { let k = k as usize; let mut poly = Vec::with_capacity(k); - let between = Uniform::new_inclusive(1, 255).unwrap(); + let between = Uniform::new_inclusive(1, 255); for _ in 1..k { poly.push(GF256(between.sample(rng))); From e3dc133de952375ef4385e488d16fc8bbb870314 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Sun, 26 Oct 2025 11:15:37 +0530 Subject: [PATCH 15/17] rename crate --- README.md | 20 ++++++++++---------- benches/benchmarks.rs | 2 +- fuzz/Cargo.toml | 4 ++-- fuzz/fuzz_targets/deserialize_share.rs | 2 +- fuzz/fuzz_targets/generate_shares.rs | 2 +- fuzz/fuzz_targets/recover.rs | 2 +- fuzz/fuzz_targets/serialize_share.rs | 2 +- src/lib.rs | 14 +++++++------- src/share.rs | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 278a46e..d1f83f3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# GF256SSS +# SSSKit -[![Rust](https://github.com/multifactor/gf256sss/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/gf256sss/actions) -[![Crates](https://img.shields.io/crates/v/gf256sss.svg)](https://crates.io/crates/gf256sss) -[![Docs](https://docs.rs/gf256sss/badge.svg)](https://docs.rs/gf256sss) +[![Rust](https://github.com/multifactor/ssskit/workflows/Rust/badge.svg?branch=master)](https://github.com/multifactor/ssskit/actions) +[![Crates](https://img.shields.io/crates/v/ssskit.svg)](https://crates.io/crates/ssskit) +[![Docs](https://docs.rs/ssskit/badge.svg)](https://docs.rs/ssskit) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT) Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate @@ -11,7 +11,7 @@ Fast, small, generic and secure [Shamir's Secret Sharing](https://en.wikipedia.o > This repository is a fork of [c0dearm/sharks](https://github.com/c0dearm/sharks), but will be actively developed and maintained by Multifactor. Documentation: -- [API reference (docs.rs)](https://docs.rs/gf256sss) +- [API reference (docs.rs)](https://docs.rs/ssskit) ## Usage @@ -19,17 +19,17 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -gf256sss = "0.1" +ssskit = "0.1" ``` If your environment doesn't support `std`: ```toml [dependencies] -gf256sss = { version = "0.1", default-features = false } +ssskit = { version = "0.1", default-features = false } ``` -To get started using gf256sss, see the [Rust docs](https://docs.rs/gf256sss) +To get started using ssskit, see the [Rust docs](https://docs.rs/ssskit) ## Features @@ -68,7 +68,7 @@ You can run them with `cargo test` and `cargo bench`. # Contributing -If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/multifactor/gf256sss/issues/new). +If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/multifactor/ssskit/issues/new). To introduce your changes into the codebase, submit a Pull Request. @@ -76,7 +76,7 @@ Many thanks! # License -gf256sss is distributed under the terms of both the MIT license and the +ssskit is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs index c658782..48aeaf8 100644 --- a/benches/benchmarks.rs +++ b/benches/benchmarks.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use std::hint::black_box; -use gf256sss::{SecretSharing, Share}; +use ssskit::{SecretSharing, Share}; const POLY: u16 = 0x11d_u16; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index a6a15ed..f669217 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "gf256sss-fuzz" +name = "ssskit-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false @@ -13,7 +13,7 @@ cargo-fuzz = true libfuzzer-sys = "0.4" arbitrary = { version = "1.4", features = ["derive"] } -[dependencies.gf256sss] +[dependencies.ssskit] path = ".." features = ["fuzzing"] diff --git a/fuzz/fuzz_targets/deserialize_share.rs b/fuzz/fuzz_targets/deserialize_share.rs index eb33656..78be895 100644 --- a/fuzz/fuzz_targets/deserialize_share.rs +++ b/fuzz/fuzz_targets/deserialize_share.rs @@ -1,7 +1,7 @@ #![no_main] use core::convert::TryFrom; -use gf256sss::Share; use libfuzzer_sys::fuzz_target; +use ssskit::Share; fuzz_target!(|data: &[u8]| { const POLY: u16 = 0x11d_u16; diff --git a/fuzz/fuzz_targets/generate_shares.rs b/fuzz/fuzz_targets/generate_shares.rs index 30bdeff..f2c063f 100644 --- a/fuzz/fuzz_targets/generate_shares.rs +++ b/fuzz/fuzz_targets/generate_shares.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use gf256sss::{SecretSharing, Share}; +use ssskit::{SecretSharing, Share}; #[derive(Debug, Arbitrary)] struct Parameters { diff --git a/fuzz/fuzz_targets/recover.rs b/fuzz/fuzz_targets/recover.rs index 69e0037..7d27cf5 100644 --- a/fuzz/fuzz_targets/recover.rs +++ b/fuzz/fuzz_targets/recover.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use arbitrary::Arbitrary; -use gf256sss::{SecretSharing, Share}; +use ssskit::{SecretSharing, Share}; const POLY: u16 = 0x11d_u16; #[derive(Debug, Arbitrary)] diff --git a/fuzz/fuzz_targets/serialize_share.rs b/fuzz/fuzz_targets/serialize_share.rs index a97b635..c859486 100644 --- a/fuzz/fuzz_targets/serialize_share.rs +++ b/fuzz/fuzz_targets/serialize_share.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use gf256sss::Share; +use ssskit::Share; const POLY: u16 = 0x11d_u16; fuzz_target!(|share: Share| { diff --git a/src/lib.rs b/src/lib.rs index 0eb6b06..448a204 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! Usage example (std): //! ``` -//! use gf256sss::{ SecretSharing, Share }; +//! use ssskit::{ SecretSharing, Share }; //! //! # const POLY: u16 = 0x11d_u16; //! // Set a minimum threshold of 10 shares @@ -21,7 +21,7 @@ //! //! Usage example (no std): //! ``` -//! use gf256sss::{ SecretSharing, Share }; +//! use ssskit::{ SecretSharing, Share }; //! use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; //! //! # const POLY: u16 = 0x11d_u16; @@ -56,7 +56,7 @@ pub use share::Share; /// /// Usage example: /// ``` -/// # use gf256sss::{ SecretSharing, Share }; +/// # use ssskit::{ SecretSharing, Share }; /// # const POLY: u16 = 0x11d_u16; /// // Set a minimum threshold of 10 shares /// let sss = SecretSharing::(10); @@ -83,7 +83,7 @@ impl SecretSharing { /// /// Example: /// ``` - /// # use gf256sss::{ SecretSharing, Share }; + /// # use ssskit::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sss = SecretSharing::(3); @@ -111,7 +111,7 @@ impl SecretSharing { /// /// Example: /// ``` - /// # use gf256sss::{ SecretSharing, Share }; + /// # use ssskit::{ SecretSharing, Share }; /// # const POLY: u16 = 0x11d_u16; /// # let sss = SecretSharing::(3); /// // Obtain an iterator over the shares for secret [1, 2] @@ -130,7 +130,7 @@ impl SecretSharing { /// /// Example: /// ``` - /// # use gf256sss::{ SecretSharing, Share }; + /// # use ssskit::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sss = SecretSharing::(3); @@ -180,7 +180,7 @@ impl SecretSharing { /// /// Example: /// ``` - /// # use gf256sss::{ SecretSharing, Share }; + /// # use ssskit::{ SecretSharing, Share }; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # const POLY: u16 = 0x11d_u16; /// # let sss = SecretSharing::(2); diff --git a/src/share.rs b/src/share.rs index aa863f4..6563c9b 100644 --- a/src/share.rs +++ b/src/share.rs @@ -12,7 +12,7 @@ use zeroize::Zeroize; /// /// Usage example: /// ``` -/// use gf256sss::{SecretSharing, Share}; +/// use ssskit::{SecretSharing, Share}; /// use core::convert::TryFrom; /// # use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; /// # fn send_to_printer(_: Vec) {} From 0971d4d773af8b21aac459125483b01c24573fa5 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Sun, 26 Oct 2025 11:15:42 +0530 Subject: [PATCH 16/17] add profiles --- Cargo.toml | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efef431..b27cd6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "gf256sss" +name = "ssskit" version = "0.1.0" authors = [ "Multifactor, Inc. ", "Sambhav Dusad ", ] description = "Fast, small and secure Shamir's Secret Sharing library crate" -homepage = "https://github.com/multifactor/gf256sss" -repository = "https://github.com/multifactor/gf256sss" -documentation = "https://docs.rs/gf256sss" +homepage = "https://github.com/multifactor/ssskit" +repository = "https://github.com/multifactor/ssskit" +documentation = "https://docs.rs/ssskit" readme = "README.md" keywords = ["shamir", "secret", "sharing", "share", "crypto"] categories = ["algorithms", "cryptography", "mathematics"] @@ -31,12 +31,17 @@ rand = { version = "0.8.5", default-features = false } hashbrown = { version = "0.16", default-features = false, features = [ "default-hasher", ] } -arbitrary = { version = "1.4", features = ["derive"], optional = true } -zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } +arbitrary = { version = "1.4.2", default-features = false, features = [ + "derive", +], optional = true } +zeroize = { version = "1.8.2", default-features = false, features = [ + "alloc", + "zeroize_derive", +], optional = true } [dev-dependencies] -criterion = "0.7" -rand_chacha = "0.3" +criterion = "0.7.0" +rand_chacha = "0.3.1" rstest = "0.26.1" [[bench]] @@ -47,3 +52,15 @@ harness = false features = ["std"] all-features = false rustdoc-args = ["--cfg", "docsrs"] + +[profile.release] +opt-level = 3 +codegen-units = 1 +incremental = false +debug = 1 +strip = true + +[profile.bench] +inherits = "release" +debug = 1 +strip = false From 4ef4ec8bcb0ff6a361246b9eff2eb35d2a6ae788 Mon Sep 17 00:00:00 2001 From: lonerapier Date: Sun, 26 Oct 2025 11:53:40 +0530 Subject: [PATCH 17/17] add docs --- src/field.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 18 +++++++++++++++--- src/math.rs | 4 ++-- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/field.rs b/src/field.rs index 9d92af4..67189f9 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,5 +1,5 @@ -// Basic operations overrided for the Galois Field 256 (2**8) -// Implements the operations over general irreducible polynomials. +//! Basic operations overrided for the Galois Field 256 (2**8) +//! Implements the operations over general irreducible polynomials. use core::iter::{Product, Sum}; use core::ops::{Add, Div, Mul, Sub}; @@ -11,6 +11,10 @@ use arbitrary::Arbitrary; use zeroize::Zeroize; #[inline] +// GF(2^8) multiplication via Russian peasant method with polynomial reduction. +// `poly` encodes the irreducible degree-8 polynomial (e.g., 0x11D for x^8 + x^4 + x^3 + x + 1). +// When the x^8 term would appear (carry), we reduce by XOR-ing with `poly` after the left shift. +// Returns the canonical byte representative in GF(256). const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { let mut result = 0u16; let mut a_val = a as u16; @@ -32,6 +36,8 @@ const fn gf256_mul(a: u8, b: u8, poly: u16) -> u8 { } #[inline] +// Constant-time-ish binary exponentiation in GF(2^8). +// Used by generator checks and table construction. const fn gf256_pow(base: u8, exponent: u8, poly: u16) -> u8 { let mut result = 1u8; let mut base_val = base; @@ -47,12 +53,19 @@ const fn gf256_pow(base: u8, exponent: u8, poly: u16) -> u8 { result } +/// Known primitive degree-8 polynomials over GF(2). +/// Any `POLY` must be one of these to give a field with multiplicative group of order 255. +/// +/// References: +/// - [Primitive elements and irreducible polynomials of GF(256)](https://codyplanteen.com/assets/rs/gf256_prim.pdf) pub const PRIMITIVE_POLYS: &[u16] = &[ - 0x11B, 0x11D, 0x12B, 0x12D, 0x14D, 0x15F, 0x163, 0x165, 0x169, 0x171, 0x187, 0x1A9, 0x1C3, - 0x1CF, 0x1E7, 0x1F5, + 0x11B, 0x11D, 0x12B, 0x12D, 0x139, 0x13F, 0x14D, 0x15F, 0x163, 0x165, 0x169, 0x171, 0x177, + 0x17B, 0x187, 0x18B, 0x18D, 0x19F, 0x1A3, 0x1A9, 0x1B1, 0x1BD, 0x1C3, 0x1CF, 0x1D7, 0x1DD, + 0x1E7, 0x1F3, 0x1F5, 0x1F9, ]; #[inline] +/// Simple membership check for compile time primitive polynomial check. const fn is_primitive(poly: u16) -> bool { let mut i = 0; @@ -65,12 +78,16 @@ const fn is_primitive(poly: u16) -> bool { false } +/// Field element type parametrized by the irreducible polynomial at the type level. +/// Different `POLY` values produce distinct, non-interoperable types. #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "fuzzing", derive(Arbitrary))] #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] #[cfg_attr(feature = "zeroize_memory", zeroize(drop))] pub struct GF256(pub u8); +/// Precomputed tables for fast log/exp arithmetic. +/// Note: `exp` is duplicated to length 512 so additions/subtractions of logs can index without explicit mod 255. pub struct Tables { pub log: [u8; 256], pub exp: [u8; 512], @@ -83,6 +100,7 @@ impl Default for Tables { } #[inline] +/// Checks if `x` has multiplicative order 255 by testing x^(255/p) != 1 for all prime factors p of 255 (=3,5,17). const fn is_primitive_element(x: u8) -> bool { const FACTORS: [u8; 3] = [3, 5, 17]; @@ -97,6 +115,7 @@ const fn is_primitive_element(x: u8) -> bool { } #[inline] +/// Linear search for a primitive element (generator) of GF(256) under `POLY`. const fn find_generator() -> u8 { let mut i = 1u16; while i <= 255 { @@ -109,6 +128,7 @@ const fn find_generator() -> u8 { } impl Tables { + /// Builds log/exp tables at compile time; panics at compile time if `POLY` is not primitive. pub const fn new() -> Self { assert!(is_primitive(POLY), "POLY must be primitive"); @@ -127,6 +147,7 @@ impl Tables { } let mut j = 255usize; + // Duplicate exp table to avoid modulus: exp[i + 255] == exp[i]. while j < 512 { exp[j] = exp[j - 255]; j += 1; @@ -137,7 +158,9 @@ impl Tables { } impl GF256 { + /// Compile-time assertion tying this type to a primitive polynomial. const POLY_CHECK: () = assert!(is_primitive(POLY), "POLY must be primitive"); + /// Precompute tables once per concrete `POLY` type. pub const TABLES: Tables = Tables::new(); pub fn add(self, other: Self) -> Self { @@ -153,23 +176,28 @@ impl GF256 { } pub fn mul(self, other: Self) -> Self { + // Map to log space; zeros are handled explicitly to avoid using log(0). let log_x = Self::TABLES.log[self.0 as usize] as usize; let log_y = Self::TABLES.log[other.0 as usize] as usize; if self.0 == 0 || other.0 == 0 { Self(0) } else { + // Addition in log space corresponds to multiplication in the field. Self(Self::TABLES.exp[log_x + log_y]) } } pub fn div(self, other: Self) -> Self { + // Map to log space; requires non-zero divisor. let log_x = Self::TABLES.log[self.0 as usize] as usize; let log_y = Self::TABLES.log[other.0 as usize] as usize; if self.0 == 0 { Self(0) } else { + // Subtraction in log space corresponds to division; +255 implements wrap-around. + // Precondition: `other` must be non-zero; dividing by zero is undefined for this API. Self(Self::TABLES.exp[log_x + 255 - log_y]) } } @@ -466,17 +494,31 @@ mod tests { (0x11D, ops_poly_11d), (0x12B, ops_poly_12b), (0x12D, ops_poly_12d), + (0x139, ops_poly_139), + (0x13F, ops_poly_13f), (0x14D, ops_poly_14d), (0x15F, ops_poly_15f), (0x163, ops_poly_163), (0x165, ops_poly_165), (0x169, ops_poly_169), (0x171, ops_poly_171), + (0x177, ops_poly_177), + (0x17B, ops_poly_17b), (0x187, ops_poly_187), + (0x18B, ops_poly_18b), + (0x18D, ops_poly_18d), + (0x19F, ops_poly_19f), + (0x1A3, ops_poly_1a3), (0x1A9, ops_poly_1a9), + (0x1B1, ops_poly_1b1), + (0x1BD, ops_poly_1bd), (0x1C3, ops_poly_1c3), (0x1CF, ops_poly_1cf), + (0x1D7, ops_poly_1d7), + (0x1DD, ops_poly_1dd), (0x1E7, ops_poly_1e7), + (0x1F3, ops_poly_1f3), (0x1F5, ops_poly_1f5), + (0x1F9, ops_poly_1f9), ); } diff --git a/src/lib.rs b/src/lib.rs index 448a204..2b853b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,13 @@ //! Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate //! -//! Usage example (std): +//! # Usage +//! ## (std) +//! //! ``` //! use ssskit::{ SecretSharing, Share }; //! //! # const POLY: u16 = 0x11d_u16; -//! // Set a minimum threshold of 10 shares +//! // Set a minimum threshold of 10 shares for an irreducible polynomial POLY //! let sss = SecretSharing::(10); //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] //! # #[cfg(feature = "std")] @@ -19,7 +21,8 @@ //! # } //! ``` //! -//! Usage example (no std): +//! ## (no std) +//! //! ``` //! use ssskit::{ SecretSharing, Share }; //! use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; @@ -36,6 +39,15 @@ //! let secret = sss.recover(shares.as_slice()).unwrap(); //! assert_eq!(secret, vec![1, 2, 3, 4]); //! ``` +//! +//! # Irreducible Polynomials +//! +//! This crate supports all 30 degree-8 irreducible polynomials over GF(2). +//! See the exported list [`PRIMITIVE_POLYS`] (defined in `field.rs`). +//! +//! Commonly used polynomials: +//! - 0x11B — used in AES (Rijndael) +//! - 0x11D — commonly used in Reed–Solomon (e.g., QR codes) #![cfg_attr(not(feature = "std"), no_std)] mod field; diff --git a/src/math.rs b/src/math.rs index 41264e2..43193a6 100644 --- a/src/math.rs +++ b/src/math.rs @@ -96,8 +96,8 @@ pub fn reshare(shares: &[Share], index: usize) -> Share

( s: GF256, k: u8,