├── fuzz ├── .gitignore ├── fuzz_targets │ ├── serialize_share.rs │ ├── deserialize_share.rs │ ├── recover.rs │ └── generate_shares.rs └── Cargo.toml ├── .gitignore ├── codecov.yml ├── COPYRIGHT ├── LICENSE-MIT ├── Cargo.toml ├── benches └── benchmarks.rs ├── CHANGELOG.md ├── .github └── workflows │ └── rust.yml ├── src ├── share.rs ├── math.rs ├── lib.rs └── field.rs ├── README.md └── LICENSE-APACHE /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | .vscode 6 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/serialize_share.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | use sharks::Share; 5 | 6 | fuzz_target!(|share: Share| { 7 | let _data: Vec = (&share).into(); 8 | }); 9 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/deserialize_share.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use core::convert::TryFrom; 3 | use libfuzzer_sys::fuzz_target; 4 | use sharks::Share; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let _share = Share::try_from(data); 8 | }); 9 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "90...100" 8 | 9 | parsers: 10 | gcov: 11 | branch_detection: 12 | conditional: yes 13 | loop: yes 14 | method: no 15 | macro: no 16 | 17 | comment: 18 | layout: "reach,diff,flags,tree" 19 | behavior: default 20 | require_changes: no 21 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/recover.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | use arbitrary::Arbitrary; 5 | use sharks::{Share, Sharks}; 6 | 7 | #[derive(Debug, Arbitrary)] 8 | struct Parameters { 9 | pub threshold: u8, 10 | pub shares: Vec, 11 | } 12 | 13 | fuzz_target!(|params: Parameters| { 14 | let sharks = Sharks(params.threshold); 15 | let _secret = sharks.recover(¶ms.shares); 16 | }); 17 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/generate_shares.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | use arbitrary::Arbitrary; 5 | use sharks::{Share, Sharks}; 6 | 7 | #[derive(Debug, Arbitrary)] 8 | struct Parameters { 9 | pub threshold: u8, 10 | pub secret: Vec, 11 | pub n_shares: usize, 12 | } 13 | 14 | fuzz_target!(|params: Parameters| { 15 | let sharks = Sharks(params.threshold); 16 | let dealer = sharks.dealer(¶ms.secret); 17 | 18 | let _shares: Vec = dealer.take(params.n_shares).collect(); 19 | }); 20 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the Sharks project are retained by their contributors. No 2 | copyright assignment is required to contribute to the Sharks project. 3 | 4 | For full authorship information, see the version control history. 5 | 6 | Except as otherwise noted (below and/or in individual files), Sharks is 7 | licensed under the Apache License, Version 2.0 or 8 | or the MIT license 9 | or , at your option. 10 | 11 | The Sharks project includes code from the Rust project 12 | published under these same licenses. 13 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "sharks-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.3" 14 | arbitrary = { version = "0.4.2", features = ["derive"] } 15 | 16 | [dependencies.sharks] 17 | path = ".." 18 | features = ["fuzzing"] 19 | 20 | # Prevent this from interfering with workspaces 21 | [workspace] 22 | members = ["."] 23 | 24 | [[bin]] 25 | name = "deserialize_share" 26 | path = "fuzz_targets/deserialize_share.rs" 27 | 28 | [[bin]] 29 | name = "serialize_share" 30 | path = "fuzz_targets/serialize_share.rs" 31 | 32 | [[bin]] 33 | name = "generate_shares" 34 | path = "fuzz_targets/generate_shares.rs" 35 | 36 | [[bin]] 37 | name = "recover" 38 | path = "fuzz_targets/recover.rs" 39 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aitor Ruano Miralles 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sharks" 3 | version = "0.5.0" 4 | authors = ["Aitor Ruano "] 5 | description = "Fast, small and secure Shamir's Secret Sharing library crate" 6 | homepage = "https://github.com/c0dearm/sharks" 7 | repository = "https://github.com/c0dearm/sharks" 8 | readme = "README.md" 9 | keywords = ["shamir", "secret", "sharing", "share", "crypto"] 10 | categories = ["algorithms", "cryptography", "mathematics"] 11 | license = "MIT/Apache-2.0" 12 | edition = "2018" 13 | 14 | [badges] 15 | maintenance = { status = "actively-developed" } 16 | codecov = { repository = "c0dearm/sharks" } 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | [features] 20 | default = ["std", "zeroize_memory"] 21 | std = ["rand/std", "rand/std_rng"] 22 | fuzzing = ["std", "arbitrary"] 23 | zeroize_memory = ["zeroize"] 24 | 25 | [dependencies] 26 | rand = { version = "0.8", default-features = false } 27 | hashbrown = "0.9" 28 | arbitrary = { version = "0.4.7", features = ["derive"], optional = true } 29 | zeroize = { version = "1.2.0", features = ["zeroize_derive"], optional = true } 30 | 31 | [dev-dependencies] 32 | criterion = "0.3" 33 | rand_chacha = "0.3" 34 | 35 | [[bench]] 36 | name = "benchmarks" 37 | harness = false 38 | -------------------------------------------------------------------------------- /benches/benchmarks.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use std::convert::TryFrom; 3 | 4 | use sharks::{Share, Sharks}; 5 | 6 | fn dealer(c: &mut Criterion) { 7 | let sharks = Sharks(255); 8 | let mut dealer = sharks.dealer(&[1]); 9 | 10 | c.bench_function("obtain_shares_dealer", |b| { 11 | b.iter(|| sharks.dealer(black_box(&[1]))) 12 | }); 13 | c.bench_function("step_shares_dealer", |b| b.iter(|| dealer.next())); 14 | } 15 | 16 | fn recover(c: &mut Criterion) { 17 | let sharks = Sharks(255); 18 | let shares: Vec = sharks.dealer(&[1]).take(255).collect(); 19 | 20 | c.bench_function("recover_secret", |b| { 21 | b.iter(|| sharks.recover(black_box(shares.as_slice()))) 22 | }); 23 | } 24 | 25 | fn share(c: &mut Criterion) { 26 | let bytes_vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 27 | let bytes = bytes_vec.as_slice(); 28 | let share = Share::try_from(bytes).unwrap(); 29 | 30 | c.bench_function("share_from_bytes", |b| { 31 | b.iter(|| Share::try_from(black_box(bytes))) 32 | }); 33 | 34 | c.bench_function("share_to_bytes", |b| { 35 | b.iter(|| Vec::from(black_box(&share))) 36 | }); 37 | } 38 | 39 | criterion_group!(benches, dealer, recover, share); 40 | criterion_main!(benches); 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.5.0] - 2021-03-14 8 | ### Added 9 | - Zeroize memory on drop for generated secret shares 10 | 11 | ## [0.4.3] - 2021-02-04 12 | ### Changed 13 | - Upgraded project dependencies 14 | 15 | ## [0.4.2] - 2020-08-03 16 | ### Fixed 17 | - Small fix in docs 18 | 19 | ## [0.4.1] - 2020-04-23 20 | ### Added 21 | - Fuzz tests 22 | 23 | ### Fixed 24 | - Unexpected panic when trying to recover secret from different length shares 25 | - Unexpected panic when trying to convert less than 2 bytes to `Share` 26 | 27 | ## [0.4.0] - 2020-04-02 28 | ### Added 29 | - It is now possible to compile without `std` with `--no-default-features` 30 | 31 | ## [0.3.3] - 2020-03-23 32 | ### Changed 33 | - Fix codecov badge 34 | 35 | ## [0.3.2] - 2020-03-09 36 | ### Changed 37 | - Share structs now derives the `Clone` trait 38 | 39 | ## [0.3.1] - 2020-01-23 40 | ### Changed 41 | - Sharks recover method now accepts any iterable collection 42 | 43 | ## [0.3.0] - 2020-01-22 44 | ### Added 45 | - Share struct which allows to convert from/to byte vectors 46 | 47 | ### Changed 48 | - Methods use the new Share struct, instead of (GF245, Vec) tuples 49 | 50 | ## [0.2.0] - 2020-01-21 51 | ### Added 52 | - Computations performed over GF256 (much faster) 53 | - Secret can now be arbitrarily long 54 | 55 | ### Changed 56 | - Some method names and docs 57 | - Maximum number of shares enforced by Rust static types instead of conditional branching 58 | 59 | ### Removed 60 | - Modular arithmetic around Mersenne primes 61 | 62 | ## [0.1.1] - 2020-01-13 63 | ### Fixed 64 | - Typo in cargo description 65 | 66 | ### Removed 67 | - Maintenance badges in cargo file 68 | 69 | ## [0.1.0] - 2020-01-13 70 | ### Added 71 | - Initial version 72 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Rust 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | 20 | test-std: 21 | name: Test std 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | - uses: actions-rs/cargo@v1 31 | with: 32 | command: test 33 | 34 | test-nostd: 35 | name: Test no-std 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | profile: minimal 42 | toolchain: stable 43 | override: true 44 | - uses: actions-rs/cargo@v1 45 | with: 46 | command: test 47 | args: --no-default-features 48 | 49 | format: 50 | name: Format 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions-rs/toolchain@v1 55 | with: 56 | profile: minimal 57 | toolchain: stable 58 | override: true 59 | - run: rustup component add rustfmt 60 | - uses: actions-rs/cargo@v1 61 | with: 62 | command: fmt 63 | args: --all -- --check 64 | 65 | quality: 66 | name: Quality 67 | runs-on: ubuntu-latest 68 | steps: 69 | - uses: actions/checkout@v2 70 | - uses: actions-rs/toolchain@v1 71 | with: 72 | profile: minimal 73 | toolchain: stable 74 | override: true 75 | - run: rustup component add clippy 76 | - uses: actions-rs/cargo@v1 77 | with: 78 | command: clippy 79 | args: -- -D warnings 80 | 81 | coverage: 82 | name: Coverage 83 | runs-on: ubuntu-latest 84 | steps: 85 | - uses: actions/checkout@v2 86 | - uses: actions-rs/toolchain@v1 87 | with: 88 | profile: minimal 89 | toolchain: stable 90 | override: true 91 | - uses: actions-rs/tarpaulin@v0.1 92 | - uses: codecov/codecov-action@v1.0.5 93 | with: 94 | token: ${{secrets.CODECOV_TOKEN}} 95 | 96 | security: 97 | name: Security 98 | runs-on: ubuntu-latest 99 | steps: 100 | - uses: actions/checkout@v1 101 | - uses: actions-rs/audit-check@v1 102 | with: 103 | token: ${{ secrets.GITHUB_TOKEN }} 104 | -------------------------------------------------------------------------------- /src/share.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use super::field::GF256; 4 | 5 | #[cfg(feature = "fuzzing")] 6 | use arbitrary::Arbitrary; 7 | 8 | #[cfg(feature = "zeroize_memory")] 9 | use zeroize::Zeroize; 10 | 11 | /// A share used to reconstruct the secret. Can be serialized to and from a byte array. 12 | /// 13 | /// Usage example: 14 | /// ``` 15 | /// use sharks::{Sharks, Share}; 16 | /// use core::convert::TryFrom; 17 | /// # use rand_chacha::rand_core::SeedableRng; 18 | /// # fn send_to_printer(_: Vec) {} 19 | /// # fn ask_shares() -> Vec> {vec![vec![1, 2], vec![2, 3], vec![3, 4]]} 20 | /// 21 | /// // Transmit the share bytes to a printer 22 | /// let sharks = Sharks(3); 23 | /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 24 | /// let dealer = sharks.dealer_rng(&[1, 2, 3], &mut rng); 25 | /// 26 | /// // Get 5 shares and print paper keys 27 | /// for s in dealer.take(5) { 28 | /// send_to_printer(Vec::from(&s)); 29 | /// }; 30 | /// 31 | /// // Get share bytes from an external source and recover secret 32 | /// let shares_bytes: Vec> = ask_shares(); 33 | /// let shares: Vec = shares_bytes.iter().map(|s| Share::try_from(s.as_slice()).unwrap()).collect(); 34 | /// let secret = sharks.recover(&shares).unwrap(); 35 | #[derive(Clone)] 36 | #[cfg_attr(feature = "fuzzing", derive(Arbitrary, Debug))] 37 | #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] 38 | #[cfg_attr(feature = "zeroize_memory", zeroize(drop))] 39 | pub struct Share { 40 | pub x: GF256, 41 | pub y: Vec, 42 | } 43 | 44 | /// Obtains a byte vector from a `Share` instance 45 | impl From<&Share> for Vec { 46 | fn from(s: &Share) -> Vec { 47 | let mut bytes = Vec::with_capacity(s.y.len() + 1); 48 | bytes.push(s.x.0); 49 | bytes.extend(s.y.iter().map(|p| p.0)); 50 | bytes 51 | } 52 | } 53 | 54 | /// Obtains a `Share` instance from a byte slice 55 | impl core::convert::TryFrom<&[u8]> for Share { 56 | type Error = &'static str; 57 | 58 | fn try_from(s: &[u8]) -> Result { 59 | if s.len() < 2 { 60 | Err("A Share must be at least 2 bytes long") 61 | } else { 62 | let x = GF256(s[0]); 63 | let y = s[1..].iter().map(|p| GF256(*p)).collect(); 64 | Ok(Share { x, y }) 65 | } 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use super::{Share, GF256}; 72 | use alloc::{vec, vec::Vec}; 73 | use core::convert::TryFrom; 74 | 75 | #[test] 76 | fn vec_from_share_works() { 77 | let share = Share { 78 | x: GF256(1), 79 | y: vec![GF256(2), GF256(3)], 80 | }; 81 | let bytes = Vec::from(&share); 82 | assert_eq!(bytes, vec![1, 2, 3]); 83 | } 84 | 85 | #[test] 86 | fn share_from_u8_slice_works() { 87 | let bytes = [1, 2, 3]; 88 | let share = Share::try_from(&bytes[..]).unwrap(); 89 | assert_eq!(share.x, GF256(1)); 90 | assert_eq!(share.y, vec![GF256(2), GF256(3)]); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/math.rs: -------------------------------------------------------------------------------- 1 | // A module which contains necessary algorithms to compute Shamir's shares and recover secrets 2 | 3 | use alloc::vec::Vec; 4 | 5 | use rand::distributions::{Distribution, Uniform}; 6 | 7 | use super::field::GF256; 8 | use super::share::Share; 9 | 10 | // Finds the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). 11 | // The expected `shares` argument format is the same as the output by the `get_evaluator´ function. 12 | // Where each (key, value) pair corresponds to one share, where the key is the `x` and the value is a vector of `y`, 13 | // where each element corresponds to one of the secret's byte chunks. 14 | pub fn interpolate(shares: &[Share]) -> Vec { 15 | (0..shares[0].y.len()) 16 | .map(|s| { 17 | shares 18 | .iter() 19 | .map(|s_i| { 20 | shares 21 | .iter() 22 | .filter(|s_j| s_j.x != s_i.x) 23 | .map(|s_j| s_j.x.clone() / (s_j.x.clone() - s_i.x.clone())) 24 | .product::() 25 | * s_i.y[s].clone() 26 | }) 27 | .sum::() 28 | .0 29 | }) 30 | .collect() 31 | } 32 | 33 | // Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[1, 255]`. 34 | // Coefficient degrees go from higher to lower in the returned vector order. 35 | pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec { 36 | let k = k as usize; 37 | let mut poly = Vec::with_capacity(k); 38 | let between = Uniform::new_inclusive(1, 255); 39 | 40 | for _ in 1..k { 41 | poly.push(GF256(between.sample(rng))); 42 | } 43 | poly.push(s); 44 | 45 | poly 46 | } 47 | 48 | // Returns an iterator over the points of the `polys` polynomials passed as argument. 49 | // Each item of the iterator is a tuple `(x, [f_1(x), f_2(x)..])` where eaxh `f_i` is the result for the ith polynomial. 50 | // Each polynomial corresponds to one byte chunk of the original secret. 51 | // The iterator will start at `x = 1` and end at `x = 255`. 52 | pub fn get_evaluator(polys: Vec>) -> impl Iterator { 53 | (1..=u8::max_value()).map(GF256).map(move |x| Share { 54 | x: x.clone(), 55 | y: polys 56 | .iter() 57 | .map(|p| { 58 | p.iter() 59 | .fold(GF256(0), |acc, c| acc * x.clone() + c.clone()) 60 | }) 61 | .collect(), 62 | }) 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; 68 | use alloc::{vec, vec::Vec}; 69 | use rand_chacha::rand_core::SeedableRng; 70 | 71 | #[test] 72 | fn random_polynomial_works() { 73 | let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 74 | let poly = random_polynomial(GF256(1), 3, &mut rng); 75 | assert_eq!(poly.len(), 3); 76 | assert_eq!(poly[2], GF256(1)); 77 | } 78 | 79 | #[test] 80 | fn evaluator_works() { 81 | let iter = get_evaluator(vec![vec![GF256(3), GF256(2), GF256(5)]]); 82 | let values: Vec<_> = iter.take(2).map(|s| (s.x.clone(), s.y.clone())).collect(); 83 | assert_eq!( 84 | values, 85 | vec![(GF256(1), vec![GF256(4)]), (GF256(2), vec![GF256(13)])] 86 | ); 87 | } 88 | 89 | #[test] 90 | fn interpolate_works() { 91 | let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 92 | let poly = random_polynomial(GF256(185), 10, &mut rng); 93 | let iter = get_evaluator(vec![poly]); 94 | let shares: Vec = iter.take(10).collect(); 95 | let root = interpolate(&shares); 96 | assert_eq!(root, vec![185]); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sharks 2 | 3 | [![Rust](https://github.com/c0dearm/sharks/workflows/Rust/badge.svg?branch=master)](https://github.com/c0dearm/sharks/actions) 4 | [![Crates](https://img.shields.io/crates/v/sharks.svg)](https://crates.io/crates/sharks) 5 | [![Docs](https://docs.rs/sharks/badge.svg)](https://docs.rs/sharks) 6 | [![Codecov](https://codecov.io/gh/c0dearm/sharks/branch/master/graph/badge.svg)](https://codecov.io/gh/c0dearm/sharks) 7 | [![License](https://camo.githubusercontent.com/47069b7e06b64b608c692a8a7f40bc6915cf629c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865322e302532464d49542d626c75652e737667)](https://github.com/c0dearm/sharks/blob/master/COPYRIGHT) 8 | 9 | Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate 10 | 11 | Documentation: 12 | - [API reference (docs.rs)](https://docs.rs/sharks) 13 | 14 | ## Usage 15 | 16 | Add this to your `Cargo.toml`: 17 | 18 | ```toml 19 | [dependencies] 20 | sharks = "0.4" 21 | ``` 22 | 23 | If your environment doesn't support `std`: 24 | 25 | ```toml 26 | [dependencies] 27 | sharks = { version = "0.4", default-features = false } 28 | ``` 29 | 30 | To get started using Sharks, see the [Rust docs](https://docs.rs/sharks) 31 | 32 | ## Features 33 | 34 | ### Developer friendly 35 | The API is simple and to the point, with minimal configuration. 36 | 37 | ### Fast and small 38 | The code is as idiomatic and clean as possible, with minimum external dependencies. 39 | 40 | ### Secure by design 41 | The implementation forbids the user to choose parameters that would result in an insecure application, 42 | like generating more shares than what's allowed by the finite field length. 43 | 44 | ## Limitations 45 | 46 | Because the Galois finite field it uses is [GF256](https://en.wikipedia.org/wiki/Finite_field#GF(p2)_for_an_odd_prime_p), 47 | only up to 255 shares can be generated for a given secret. A larger number would be insecure as shares would start duplicating. 48 | Nevertheless, the secret can be arbitrarily long as computations are performed on single byte chunks. 49 | 50 | ## Testing 51 | 52 | This crate contains both unit and benchmark tests (as well as the examples included in the docs). 53 | You can run them with `cargo test` and `cargo bench`. 54 | 55 | ### Benchmark results [min mean max] 56 | 57 | | CPU | obtain_shares_dealer | step_shares_dealer | recover_secret | share_from_bytes | share_to_bytes | 58 | | ----------------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | 59 | | 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] | 60 | | 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] | 61 | | 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] | 62 | 63 | # Contributing 64 | 65 | If you find a vulnerability, bug or would like a new feature, [open a new issue](https://github.com/c0dearm/sharks/issues/new). 66 | 67 | To introduce your changes into the codebase, submit a Pull Request. 68 | 69 | Many thanks! 70 | 71 | # License 72 | 73 | Sharks is distributed under the terms of both the MIT license and the 74 | Apache License (Version 2.0). 75 | 76 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and 77 | [COPYRIGHT](COPYRIGHT) for details. 78 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate 2 | //! 3 | //! Usage example (std): 4 | //! ``` 5 | //! use sharks::{ Sharks, Share }; 6 | //! 7 | //! // Set a minimum threshold of 10 shares 8 | //! let sharks = Sharks(10); 9 | //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] 10 | //! # #[cfg(feature = "std")] 11 | //! # { 12 | //! let dealer = sharks.dealer(&[1, 2, 3, 4]); 13 | //! // Get 10 shares 14 | //! let shares: Vec = dealer.take(10).collect(); 15 | //! // Recover the original secret! 16 | //! let secret = sharks.recover(shares.as_slice()).unwrap(); 17 | //! assert_eq!(secret, vec![1, 2, 3, 4]); 18 | //! # } 19 | //! ``` 20 | //! 21 | //! Usage example (no std): 22 | //! ``` 23 | //! use sharks::{ Sharks, Share }; 24 | //! use rand_chacha::rand_core::SeedableRng; 25 | //! 26 | //! // Set a minimum threshold of 10 shares 27 | //! let sharks = Sharks(10); 28 | //! // Obtain an iterator over the shares for secret [1, 2, 3, 4] 29 | //! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 30 | //! let dealer = sharks.dealer_rng(&[1, 2, 3, 4], &mut rng); 31 | //! // Get 10 shares 32 | //! let shares: Vec = dealer.take(10).collect(); 33 | //! // Recover the original secret! 34 | //! let secret = sharks.recover(shares.as_slice()).unwrap(); 35 | //! assert_eq!(secret, vec![1, 2, 3, 4]); 36 | //! ``` 37 | #![cfg_attr(not(feature = "std"), no_std)] 38 | 39 | mod field; 40 | mod math; 41 | mod share; 42 | 43 | extern crate alloc; 44 | 45 | use alloc::vec::Vec; 46 | use hashbrown::HashSet; 47 | 48 | use field::GF256; 49 | pub use share::Share; 50 | 51 | /// Tuple struct which implements methods to generate shares and recover secrets over a 256 bits Galois Field. 52 | /// Its only parameter is the minimum shares threshold. 53 | /// 54 | /// Usage example: 55 | /// ``` 56 | /// # use sharks::{ Sharks, Share }; 57 | /// // Set a minimum threshold of 10 shares 58 | /// let sharks = Sharks(10); 59 | /// // Obtain an iterator over the shares for secret [1, 2, 3, 4] 60 | /// # #[cfg(feature = "std")] 61 | /// # { 62 | /// let dealer = sharks.dealer(&[1, 2, 3, 4]); 63 | /// // Get 10 shares 64 | /// let shares: Vec = dealer.take(10).collect(); 65 | /// // Recover the original secret! 66 | /// let secret = sharks.recover(shares.as_slice()).unwrap(); 67 | /// assert_eq!(secret, vec![1, 2, 3, 4]); 68 | /// # } 69 | /// ``` 70 | pub struct Sharks(pub u8); 71 | 72 | impl Sharks { 73 | /// This method is useful when `std` is not available. For typical usage 74 | /// see the `dealer` method. 75 | /// 76 | /// Given a `secret` byte slice, returns an `Iterator` along new shares. 77 | /// The maximum number of shares that can be generated is 256. 78 | /// A random number generator has to be provided. 79 | /// 80 | /// Example: 81 | /// ``` 82 | /// # use sharks::{ Sharks, Share }; 83 | /// # use rand_chacha::rand_core::SeedableRng; 84 | /// # let sharks = Sharks(3); 85 | /// // Obtain an iterator over the shares for secret [1, 2] 86 | /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 87 | /// let dealer = sharks.dealer_rng(&[1, 2], &mut rng); 88 | /// // Get 3 shares 89 | /// let shares: Vec = dealer.take(3).collect(); 90 | pub fn dealer_rng( 91 | &self, 92 | secret: &[u8], 93 | rng: &mut R, 94 | ) -> impl Iterator { 95 | let mut polys = Vec::with_capacity(secret.len()); 96 | 97 | for chunk in secret { 98 | polys.push(math::random_polynomial(GF256(*chunk), self.0, rng)) 99 | } 100 | 101 | math::get_evaluator(polys) 102 | } 103 | 104 | /// Given a `secret` byte slice, returns an `Iterator` along new shares. 105 | /// The maximum number of shares that can be generated is 256. 106 | /// 107 | /// Example: 108 | /// ``` 109 | /// # use sharks::{ Sharks, Share }; 110 | /// # let sharks = Sharks(3); 111 | /// // Obtain an iterator over the shares for secret [1, 2] 112 | /// let dealer = sharks.dealer(&[1, 2]); 113 | /// // Get 3 shares 114 | /// let shares: Vec = dealer.take(3).collect(); 115 | #[cfg(feature = "std")] 116 | pub fn dealer(&self, secret: &[u8]) -> impl Iterator { 117 | let mut rng = rand::thread_rng(); 118 | self.dealer_rng(secret, &mut rng) 119 | } 120 | 121 | /// Given an iterable collection of shares, recovers the original secret. 122 | /// If the number of distinct shares is less than the minimum threshold an `Err` is returned, 123 | /// otherwise an `Ok` containing the secret. 124 | /// 125 | /// Example: 126 | /// ``` 127 | /// # use sharks::{ Sharks, Share }; 128 | /// # use rand_chacha::rand_core::SeedableRng; 129 | /// # let sharks = Sharks(3); 130 | /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); 131 | /// # let mut shares: Vec = sharks.dealer_rng(&[1], &mut rng).take(3).collect(); 132 | /// // Recover original secret from shares 133 | /// let mut secret = sharks.recover(&shares); 134 | /// // Secret correctly recovered 135 | /// assert!(secret.is_ok()); 136 | /// // Remove shares for demonstration purposes 137 | /// shares.clear(); 138 | /// secret = sharks.recover(&shares); 139 | /// // Not enough shares to recover secret 140 | /// assert!(secret.is_err()); 141 | pub fn recover<'a, T>(&self, shares: T) -> Result, &str> 142 | where 143 | T: IntoIterator, 144 | T::IntoIter: Iterator, 145 | { 146 | let mut share_length: Option = None; 147 | let mut keys: HashSet = HashSet::new(); 148 | let mut values: Vec = Vec::new(); 149 | 150 | for share in shares.into_iter() { 151 | if share_length.is_none() { 152 | share_length = Some(share.y.len()); 153 | } 154 | 155 | if Some(share.y.len()) != share_length { 156 | return Err("All shares must have the same length"); 157 | } else { 158 | keys.insert(share.x.0); 159 | values.push(share.clone()); 160 | } 161 | } 162 | 163 | if keys.is_empty() || (keys.len() < self.0 as usize) { 164 | Err("Not enough shares to recover original secret") 165 | } else { 166 | Ok(math::interpolate(values.as_slice())) 167 | } 168 | } 169 | } 170 | 171 | #[cfg(test)] 172 | mod tests { 173 | use super::{Share, Sharks}; 174 | use alloc::{vec, vec::Vec}; 175 | 176 | impl Sharks { 177 | #[cfg(not(feature = "std"))] 178 | fn make_shares(&self, secret: &[u8]) -> impl Iterator { 179 | use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; 180 | 181 | let mut rng = ChaCha8Rng::from_seed([0x90; 32]); 182 | self.dealer_rng(secret, &mut rng) 183 | } 184 | 185 | #[cfg(feature = "std")] 186 | fn make_shares(&self, secret: &[u8]) -> impl Iterator { 187 | self.dealer(secret) 188 | } 189 | } 190 | 191 | #[test] 192 | fn test_insufficient_shares_err() { 193 | let sharks = Sharks(255); 194 | let shares: Vec = sharks.make_shares(&[1]).take(254).collect(); 195 | let secret = sharks.recover(&shares); 196 | assert!(secret.is_err()); 197 | } 198 | 199 | #[test] 200 | fn test_duplicate_shares_err() { 201 | let sharks = Sharks(255); 202 | let mut shares: Vec = sharks.make_shares(&[1]).take(255).collect(); 203 | shares[1] = Share { 204 | x: shares[0].x.clone(), 205 | y: shares[0].y.clone(), 206 | }; 207 | let secret = sharks.recover(&shares); 208 | assert!(secret.is_err()); 209 | } 210 | 211 | #[test] 212 | fn test_integration_works() { 213 | let sharks = Sharks(255); 214 | let shares: Vec = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect(); 215 | let secret = sharks.recover(&shares).unwrap(); 216 | assert_eq!(secret, vec![1, 2, 3, 4]); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Aitor Ruano Miralles 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/field.rs: -------------------------------------------------------------------------------- 1 | // Basic operations overrided for the Galois Field 256 (2**8) 2 | // Uses pre-calculated tables for 0x11d primitive polynomial (x**8 + x**4 + x**3 + x**2 + 1) 3 | 4 | use core::iter::{Product, Sum}; 5 | use core::ops::{Add, Div, Mul, Sub}; 6 | 7 | #[cfg(feature = "fuzzing")] 8 | use arbitrary::Arbitrary; 9 | 10 | #[cfg(feature = "zeroize_memory")] 11 | use zeroize::Zeroize; 12 | 13 | const LOG_TABLE: [u8; 256] = [ 14 | 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 15 | 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, 16 | 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 17 | 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 18 | 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 19 | 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 20 | 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 21 | 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 22 | 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 23 | 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 24 | 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 25 | 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 26 | 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, 27 | 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 28 | 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 29 | 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf, 30 | ]; 31 | 32 | const EXP_TABLE: [u8; 512] = [ 33 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 34 | 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 35 | 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 36 | 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 37 | 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 38 | 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 39 | 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 40 | 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 41 | 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 42 | 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 43 | 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 44 | 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 45 | 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 46 | 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 47 | 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 48 | 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, 49 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 50 | 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x9d, 51 | 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 52 | 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 53 | 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 54 | 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 55 | 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 56 | 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 57 | 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 58 | 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 59 | 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 60 | 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 61 | 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 62 | 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12, 63 | 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 0x2c, 64 | 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, 0x02, 65 | ]; 66 | 67 | #[derive(Debug, PartialEq, Clone)] 68 | #[cfg_attr(feature = "fuzzing", derive(Arbitrary))] 69 | #[cfg_attr(feature = "zeroize_memory", derive(Zeroize))] 70 | #[cfg_attr(feature = "zeroize_memory", zeroize(drop))] 71 | pub struct GF256(pub u8); 72 | 73 | #[allow(clippy::suspicious_arithmetic_impl)] 74 | impl Add for GF256 { 75 | type Output = GF256; 76 | 77 | fn add(self, other: Self) -> Self::Output { 78 | Self(self.0 ^ other.0) 79 | } 80 | } 81 | 82 | #[allow(clippy::suspicious_arithmetic_impl)] 83 | impl Sub for GF256 { 84 | type Output = Self; 85 | 86 | fn sub(self, other: Self) -> Self::Output { 87 | Self(self.0 ^ other.0) 88 | } 89 | } 90 | 91 | #[allow(clippy::suspicious_arithmetic_impl)] 92 | impl Mul for GF256 { 93 | type Output = Self; 94 | 95 | fn mul(self, other: Self) -> Self::Output { 96 | let log_x = LOG_TABLE[self.0 as usize] as usize; 97 | let log_y = LOG_TABLE[other.0 as usize] as usize; 98 | 99 | if self.0 == 0 || other.0 == 0 { 100 | Self(0) 101 | } else { 102 | Self(EXP_TABLE[log_x + log_y]) 103 | } 104 | } 105 | } 106 | 107 | #[allow(clippy::suspicious_arithmetic_impl)] 108 | impl Div for GF256 { 109 | type Output = Self; 110 | 111 | fn div(self, other: Self) -> Self::Output { 112 | let log_x = LOG_TABLE[self.0 as usize] as usize; 113 | let log_y = LOG_TABLE[other.0 as usize] as usize; 114 | 115 | if self.0 == 0 { 116 | Self(0) 117 | } else { 118 | Self(EXP_TABLE[log_x + 255 - log_y]) 119 | } 120 | } 121 | } 122 | 123 | impl Sum for GF256 { 124 | fn sum>(iter: I) -> Self { 125 | iter.fold(Self(0), |acc, x| acc + x) 126 | } 127 | } 128 | 129 | impl Product for GF256 { 130 | fn product>(iter: I) -> Self { 131 | iter.fold(Self(1), |acc, x| acc * x) 132 | } 133 | } 134 | 135 | #[cfg(test)] 136 | mod tests { 137 | use super::{EXP_TABLE, GF256, LOG_TABLE}; 138 | use alloc::vec; 139 | 140 | #[test] 141 | fn add_works() { 142 | let answers: [u8; 256] = [ 143 | 1, 2, 5, 17, 18, 18, 90, 70, 30, 229, 71, 6, 214, 239, 212, 109, 72, 252, 205, 84, 128, 144 | 248, 5, 72, 147, 194, 111, 244, 208, 56, 44, 177, 152, 173, 43, 179, 196, 110, 155, 20, 145 | 95, 71, 59, 173, 30, 211, 29, 102, 91, 57, 199, 119, 126, 15, 169, 25, 148, 32, 96, 146 | 170, 244, 139, 172, 7, 89, 1, 234, 160, 255, 242, 110, 65, 135, 82, 172, 188, 14, 173, 147 | 90, 120, 203, 55, 71, 117, 228, 64, 106, 194, 15, 51, 204, 255, 216, 142, 55, 162, 199, 148 | 237, 245, 37, 210, 106, 58, 230, 102, 32, 28, 60, 42, 56, 221, 243, 75, 65, 165, 227, 149 | 242, 248, 190, 184, 117, 162, 9, 105, 228, 192, 193, 155, 130, 103, 238, 171, 52, 237, 150 | 185, 164, 40, 212, 255, 175, 181, 208, 212, 76, 75, 232, 3, 94, 116, 28, 225, 214, 88, 151 | 214, 171, 171, 199, 245, 62, 93, 209, 238, 110, 56, 83, 45, 240, 179, 108, 98, 64, 1, 152 | 167, 10, 79, 158, 17, 141, 120, 224, 130, 27, 63, 90, 17, 11, 87, 143, 226, 58, 239, 153 | 227, 157, 52, 113, 188, 127, 246, 163, 120, 216, 47, 57, 12, 162, 171, 60, 80, 61, 3, 154 | 98, 224, 80, 111, 172, 69, 56, 251, 173, 231, 23, 137, 180, 83, 217, 125, 23, 32, 161, 155 | 211, 84, 164, 252, 6, 237, 0, 177, 254, 39, 193, 99, 246, 101, 148, 28, 14, 98, 107, 156 | 111, 224, 152, 50, 5, 23, 214, 174, 157 | ]; 158 | 159 | for (i, a) in answers.iter().enumerate() { 160 | assert_eq!((GF256(LOG_TABLE[i]) + GF256(EXP_TABLE[i])).0, *a); 161 | } 162 | } 163 | 164 | #[test] 165 | fn sub_works() { 166 | add_works(); 167 | } 168 | 169 | #[test] 170 | fn mul_works() { 171 | let answers: [u8; 256] = [ 172 | 0, 0, 4, 200, 32, 14, 206, 179, 39, 134, 169, 160, 32, 59, 184, 50, 45, 121, 69, 43, 173 | 102, 43, 139, 169, 18, 94, 107, 84, 18, 157, 159, 51, 211, 1, 52, 13, 51, 128, 31, 219, 174 | 240, 230, 212, 219, 197, 19, 11, 135, 93, 163, 237, 53, 91, 177, 135, 124, 240, 224, 6, 175 | 158, 167, 155, 155, 38, 223, 144, 70, 54, 50, 45, 134, 170, 126, 223, 103, 207, 253, 176 | 176, 75, 98, 137, 87, 59, 50, 208, 116, 29, 200, 128, 82, 13, 138, 107, 53, 42, 34, 177 | 123, 203, 65, 174, 111, 101, 19, 78, 165, 62, 115, 108, 175, 139, 126, 107, 55, 196, 178 | 30, 209, 126, 8, 15, 211, 57, 191, 37, 254, 24, 136, 30, 111, 188, 30, 209, 208, 49, 179 | 132, 181, 22, 207, 241, 28, 2, 97, 58, 244, 179, 190, 120, 249, 174, 99, 6, 215, 232, 180 | 173, 1, 20, 216, 224, 191, 247, 78, 223, 101, 153, 1, 182, 203, 213, 75, 132, 98, 53, 181 | 204, 13, 177, 22, 88, 218, 21, 32, 68, 247, 153, 11, 190, 47, 128, 214, 33, 110, 194, 182 | 102, 77, 5, 178, 74, 65, 134, 62, 91, 190, 133, 15, 134, 94, 37, 247, 205, 51, 224, 183 | 152, 15, 13, 13, 233, 189, 206, 100, 131, 222, 5, 70, 182, 231, 176, 167, 150, 156, 184 | 249, 29, 189, 96, 149, 239, 162, 43, 239, 89, 8, 9, 57, 118, 227, 168, 243, 164, 188, 185 | 125, 8, 8, 240, 36, 45, 21, 20, 44, 175, 186 | ]; 187 | 188 | for (i, a) in answers.iter().enumerate() { 189 | assert_eq!((GF256(LOG_TABLE[i]) * GF256(EXP_TABLE[i])).0, *a); 190 | } 191 | } 192 | 193 | #[test] 194 | fn div_works() { 195 | let answers: [u8; 256] = [ 196 | 0, 0, 71, 174, 173, 87, 134, 213, 152, 231, 124, 39, 203, 113, 13, 198, 88, 171, 55, 197 | 150, 177, 227, 25, 225, 227, 180, 157, 225, 252, 122, 88, 161, 45, 87, 148, 78, 40, 198 | 165, 74, 134, 142, 120, 121, 163, 156, 75, 154, 241, 239, 27, 152, 130, 125, 235, 230, 199 | 32, 138, 225, 145, 90, 214, 226, 182, 168, 155, 175, 179, 124, 105, 169, 249, 58, 201, 200 | 14, 155, 217, 196, 254, 201, 143, 229, 12, 178, 24, 100, 226, 163, 234, 177, 36, 75, 201 | 106, 114, 208, 162, 63, 235, 181, 108, 131, 248, 51, 190, 187, 235, 115, 112, 37, 79, 202 | 90, 112, 237, 195, 121, 136, 110, 174, 143, 113, 134, 229, 255, 35, 175, 156, 208, 240, 203 | 222, 94, 202, 228, 34, 123, 23, 48, 18, 122, 114, 75, 243, 212, 139, 56, 132, 157, 119, 204 | 219, 170, 236, 11, 51, 86, 224, 221, 142, 200, 154, 136, 179, 72, 3, 32, 142, 149, 180, 205 | 209, 253, 17, 210, 134, 162, 106, 38, 108, 154, 154, 74, 181, 115, 142, 204, 195, 23, 206 | 162, 178, 41, 9, 90, 190, 14, 2, 45, 227, 253, 115, 93, 155, 244, 83, 219, 11, 196, 207 | 167, 241, 33, 60, 103, 69, 181, 189, 145, 130, 174, 137, 65, 65, 45, 153, 79, 236, 199, 208 | 209, 41, 10, 205, 44, 182, 38, 222, 209, 253, 247, 64, 71, 32, 1, 27, 53, 4, 110, 170, 209 | 221, 215, 4, 179, 163, 64, 90, 152, 163, 235, 6, 41, 93, 176, 175, 210 | ]; 211 | 212 | for (i, a) in answers.iter().enumerate() { 213 | assert_eq!((GF256(LOG_TABLE[i]) / GF256(EXP_TABLE[i])).0, *a); 214 | } 215 | } 216 | 217 | #[test] 218 | fn sum_works() { 219 | let values = vec![GF256(0x53), GF256(0xCA), GF256(0)]; 220 | assert_eq!(values.into_iter().sum::().0, 0x99); 221 | } 222 | 223 | #[test] 224 | fn product_works() { 225 | let values = vec![GF256(1), GF256(1), GF256(4)]; 226 | assert_eq!(values.into_iter().product::().0, 4); 227 | } 228 | } 229 | --------------------------------------------------------------------------------