├── .gitmodules ├── .gitignore ├── .github └── workflows │ └── rust.yml ├── Cargo.toml ├── LICENSE ├── benches └── latencies.rs ├── README.md └── src └── lib.rs /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # Files used by vs-code 14 | .vscode/ 15 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | categories = ["cryptography"] 3 | description = "Shamir secret sharing library for Rust" 4 | documentation = "https://docs.rs/shamirsecretsharing" 5 | license = "MIT" 6 | name = "shamirsecretsharing" 7 | repository = "https://github.com/dsprenkels/sss-rs" 8 | version = "0.1.7" 9 | 10 | [badges] 11 | maintenance = {status = "passively-maintained"} 12 | travis-ci = {repository = "dsprenkels/sss-rs", branch = "master"} 13 | 14 | [features] 15 | # DEPRECATED: There is a version of libsodium linked in. This feature flag does not do 16 | # anything at the moment, and will be removed in version 0.2. 17 | have_libsodium = [] 18 | 19 | [dependencies] 20 | rand = "0.8.5" 21 | crypto_secretbox = { version = "0.1", default-features = false, features = ["salsa20", "alloc"] } 22 | 23 | [dev-dependencies] 24 | chacha20-poly1305-aead = "0.1" 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Amber Sprenkels 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /benches/latencies.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate shamirsecretsharing; 4 | extern crate test; 5 | 6 | use test::Bencher; 7 | 8 | use shamirsecretsharing as sss; 9 | 10 | #[bench] 11 | fn create_shares_54(b: &mut Bencher) { 12 | let data = vec![42; sss::DATA_SIZE]; 13 | let count = 5; 14 | let threshold = 4; 15 | b.iter(|| { 16 | sss::create_shares(&data, count, threshold).unwrap(); 17 | }); 18 | } 19 | 20 | #[bench] 21 | fn combine_shares_4(b: &mut Bencher) { 22 | let data = vec![42; sss::DATA_SIZE]; 23 | let threshold = 4; 24 | let shares = sss::create_shares(&data, threshold, threshold).unwrap(); 25 | b.iter(|| { 26 | sss::combine_shares(&shares).unwrap(); 27 | }); 28 | } 29 | #[bench] 30 | fn create_keyshares_54(b: &mut Bencher) { 31 | let key = vec![42; sss::hazmat::KEY_SIZE]; 32 | let count = 5; 33 | let threshold = 4; 34 | b.iter(|| { 35 | sss::hazmat::create_keyshares(&key, count, threshold).unwrap(); 36 | }); 37 | } 38 | 39 | #[bench] 40 | fn combine_keyshares_4(b: &mut Bencher) { 41 | let key = vec![42; sss::hazmat::KEY_SIZE]; 42 | let threshold = 4; 43 | let shares = sss::hazmat::create_keyshares(&key, threshold, threshold).unwrap(); 44 | b.iter(|| { 45 | sss::hazmat::combine_keyshares(&shares).unwrap(); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shamir secret sharing in Rust 2 | 3 | [![Build Status](https://travis-ci.org/dsprenkels/sss-rs.svg?branch=master)](https://travis-ci.org/dsprenkels/sss-rs) 4 | [![Coverage Status](https://coveralls.io/repos/github/dsprenkels/sss-rs/badge.svg?branch=master)](https://coveralls.io/github/dsprenkels/sss-rs?branch=master) 5 | [![Docs](https://docs.rs/shamirsecretsharing/badge.svg)](https://docs.rs/shamirsecretsharing) 6 | 7 | `sss-rs` contains Rust bindings for my [Shamir secret sharing library][sss]. 8 | This library allows users to split secret data into a number of different 9 | shares. With the possession of some or all of these shares, the original secret 10 | can be restored. ([Looking for the command line interface?][cli]) 11 | 12 | An example use case is a beer brewery which has a vault which contains their 13 | precious super secret recipe. The 5 board members of this brewery do not trust 14 | all the others well enough that they won't secretly break into the vault and 15 | sell the recipe to a competitor. So they split the code into 5 shares, and 16 | allow 4 shares to restore the original code. Now they are sure that the 17 | majority of the staff will know when the vault is opened, but they can still 18 | open the vault when one of the staff members is abroad or sick at home. 19 | 20 | ## Installation 21 | 22 | ```toml 23 | [dependencies] 24 | shamirsecretsharing = "0.1" 25 | ``` 26 | 27 | ## Usage 28 | 29 | Secrets are always supplied as `&[u8]` slices with a length of 64 items. Shares 30 | are generated from a piece of secret data using the `create_shares` function and 31 | shares can be afterwards be combined using `combine_shares`. 32 | 33 | Shares are always 113 bytes long. Both `create_shares` and `combine_shares` 34 | return a `Result<_, SSSError>` type. Errors will _only_ happen when invalid 35 | parameters are supplied. When given valid parameters, these function will always 36 | return `Ok(_)`. In the case of invalid parameters the error will be able to tell 37 | you what went wrong. 38 | 39 | ```rust 40 | use shamirsecretsharing::*; 41 | 42 | // Create a some shares over the secret data `[42, 42, 42, ...]` 43 | let data = vec![42; DATA_SIZE]; 44 | let count = 5; 45 | let treshold = 4; 46 | let mut shares = create_shares(&data, count, treshold).unwrap(); 47 | 48 | // Lose a share (for demonstrational purposes) 49 | shares.remove(3); 50 | 51 | // We still have 4 shares, so we should still be able to restore the secret 52 | let restored = combine_shares(&shares).unwrap(); 53 | assert_eq!(restored, Some(data)); 54 | 55 | // If we lose another share the secret is lost 56 | shares.remove(0); 57 | let restored2 = combine_shares(&shares).unwrap(); 58 | assert_eq!(restored2, None); 59 | ``` 60 | 61 | ## Changelog 62 | 63 | ### Version 0.1.1 64 | 65 | - Remove an unintended side channel which allows a participating attacker with 66 | access to a accurate timing channel to iteratively guess shares during the 67 | execution of `combine_shares`. 68 | 69 | ### Version 0.1.5 70 | 71 | - This library used to link to my `sss` library that was written in C. From 72 | this version, the complete library is written only in Rust. 73 | - The `have_libsodium` feature flag is deprecated. 74 | - The minimum required rustc version is now 1.44.0. 75 | 76 | ### Version 0.1.7 77 | 78 | - Use `crypto-secretbox` crate instead of `xsalsa20poly1305` 79 | ([#12](https://github.com/dsprenkels/sss-rs/pull/12)). 80 | 81 | ## Questions 82 | 83 | Feel free to [open an issue](https://github.com/dsprenkels/sss-rs/issues/new) 84 | or send me an email on my Github associated e-mail address. 85 | 86 | 87 | [sss]: https://github.com/dsprenkels/sss 88 | [cli]: https://github.com/dsprenkels/sss-cli 89 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This crate provides bindings to my [Shamir secret sharing library][sss]. 3 | 4 | The main functions to use are [`create_shares`] and [`combine_shares`]. 5 | 6 | *The [`hazmat`] module is for experts.* The functions in the `hazmat` module miss some security 7 | guarantees, so do not use them unless you really know what you are doing. 8 | 9 | Encapsulated in the `SSSResult`, [`combine_shares`] will return an `Option<_>` which will be 10 | `Some(data)` if the data could be restored. If the data could not be restored, [`combine_shares`] 11 | will return `Ok(None)`. This means that could mean either of: 12 | 13 | 1. More shares were needed to reach the treshold. 14 | 2. Shares of different sets (corresponding to different secrets) were supplied or some of the 15 | shares were tampered with. 16 | 17 | [`hazmat`]: hazmat/index.html 18 | [`create_shares`]: fn.create_shares.html 19 | [`combine_shares`]: fn.combine_shares.html 20 | 21 | # Example 22 | 23 | ```rust 24 | use shamirsecretsharing::*; 25 | 26 | // Create a some shares over the secret data `[42, 42, 42, ...]` 27 | let data = vec![42; DATA_SIZE]; 28 | let count = 5; 29 | let treshold = 4; 30 | let mut shares = create_shares(&data, count, treshold).unwrap(); 31 | 32 | // Lose a share (for demonstrational purposes) 33 | shares.remove(3); 34 | 35 | // We still have 4 shares, so we should still be able to restore the secret 36 | let restored = combine_shares(&shares).unwrap(); 37 | assert_eq!(restored, Some(data)); 38 | 39 | // If we lose another share the secret is lost 40 | shares.remove(0); 41 | let restored2 = combine_shares(&shares).unwrap(); 42 | assert_eq!(restored2, None); 43 | ``` 44 | 45 | This library supports can generate sets with at most `count` and a `treshold` shares. 46 | 47 | [sss]: https://github.com/dsprenkels/sss 48 | */ 49 | 50 | #![warn(missing_docs)] 51 | 52 | extern crate rand; 53 | extern crate crypto_secretbox; 54 | use hazmat::{KEYSHARE_SIZE, KEY_SIZE}; 55 | use std::error; 56 | use std::fmt; 57 | use crypto_secretbox::{ 58 | aead::{Aead, KeyInit}, 59 | XSalsa20Poly1305, 60 | }; 61 | 62 | /// Custom error types for errors originating from this crate 63 | #[derive(Debug, PartialEq, Eq)] 64 | pub enum SSSError { 65 | /// The `n` parameter was invalid 66 | InvalidN(u8), 67 | /// The `k` parameter was invalid 68 | InvalidK(u8), 69 | /// There was a (key)share that had an invalid length 70 | BadShareLen((usize, usize)), 71 | /// The input supplied to a function had an incorrect length 72 | BadInputLen(usize), 73 | } 74 | 75 | /// The size of the input data to `create_shares` 76 | pub const DATA_SIZE: usize = 64; 77 | /// Regular share size from shares produced by `create_shares` 78 | pub const SHARE_SIZE: usize = 113; 79 | 80 | impl fmt::Display for SSSError { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | use SSSError::*; 83 | match *self { 84 | // Both underlying errors already impl `Display`, so we defer to 85 | // their implementations. 86 | InvalidN(n) => write!(f, "Error: invalid share count ({})", n), 87 | InvalidK(k) => write!(f, "Error: invalid treshold ({})", k), 88 | BadShareLen((i, x)) => write!(f, "Error: share {} has bad length ({})", i, x), 89 | BadInputLen(x) => write!(f, "Error: bad input length ({})", x), 90 | } 91 | } 92 | } 93 | 94 | impl error::Error for SSSError { 95 | fn description(&self) -> &str { 96 | use SSSError::*; 97 | match *self { 98 | InvalidN(_) => "invalid n", 99 | InvalidK(_) => "invalid k", 100 | BadShareLen(_) => "bad share length", 101 | BadInputLen(_) => "bad input length", 102 | } 103 | } 104 | } 105 | 106 | type SSSResult = Result; 107 | 108 | /// Check the parameters `n` and `k` and return `Ok(())` if they were valid 109 | fn check_nk(n: u8, k: u8) -> SSSResult<()> { 110 | if n < 1 { 111 | return Err(SSSError::InvalidN(n)); 112 | } 113 | if k < 1 || k > n { 114 | return Err(SSSError::InvalidK(k)); 115 | } 116 | Ok(()) 117 | } 118 | 119 | /// Check `data` and return `Ok(())` if its length is correct for being shared with 120 | /// `create_shares` 121 | fn check_data_len(data: &[u8]) -> SSSResult<()> { 122 | if data.len() != DATA_SIZE { 123 | Err(SSSError::BadInputLen(data.len())) 124 | } else { 125 | Ok(()) 126 | } 127 | } 128 | 129 | /** 130 | Create a set of shares 131 | 132 | - `data` must be a `&[u8]` slice of length `DATA_SIZE` (64) 133 | - `n` is the number of shares that is to be generated 134 | - `k` is the treshold value of how many shares are needed to restore the secret 135 | 136 | The value that is returned is a newly allocated vector of vectors. Each of these vectors will 137 | contain `SHARE_SIZE` `u8` items. 138 | 139 | # Example 140 | ``` 141 | use shamirsecretsharing::*; 142 | 143 | // Create a some shares over the secret data `[42, 42, 42, ...]` 144 | let data = vec![42; DATA_SIZE]; 145 | let count = 5; 146 | let treshold = 4; 147 | let shares = create_shares(&data, count, treshold); 148 | match shares { 149 | Ok(shares) => println!("Created some shares: {:?}", shares), 150 | Err(err) => panic!("Oops! Something went wrong: {}", err), 151 | } 152 | ``` 153 | */ 154 | pub fn create_shares(data: &[u8], n: u8, k: u8) -> SSSResult>> { 155 | check_nk(n, k)?; 156 | check_data_len(data)?; 157 | 158 | let key = rand::random::<[u8; KEY_SIZE]>(); 159 | let mut shares = hazmat::create_keyshares(&key, n, k)?; 160 | let cipher = XSalsa20Poly1305::new(&key.into()); 161 | let ciphertext = cipher 162 | .encrypt(&[0; XSalsa20Poly1305::NONCE_SIZE].into(), data) 163 | .expect("xsalsa20poly1305 encryption error"); 164 | for share in shares.iter_mut() { 165 | share.extend_from_slice(&ciphertext); 166 | } 167 | Ok(shares) 168 | } 169 | 170 | /** 171 | Combine a set of shares and return the original secret 172 | 173 | `shares` must be a slice of share vectors. 174 | 175 | The return type will be a `Result` which will only be `Err(err)` of the input shares were 176 | malformed. When the input shares are of the correct length, this function will always return 177 | `Ok(_)`. 178 | 179 | Attempts at restoring a secret may fail. Then `combine_shares` will return `Ok(None)`. This only 180 | cases in which this can happen are: 181 | 182 | 1. More shares were needed to reach the treshold. 183 | 2. Shares of different sets (corresponding to different secrets) were supplied or some of the 184 | shares were tampered with. 185 | 186 | If the shares were correct---and a secret could be restored---this function will return 187 | `Ok(Some(data))`, with `data` being a vector of `u8`s. This `data` will be the same length as When 188 | it was shared, namely `DATA_SIZE` (64) bytes. 189 | 190 | # Example 191 | 192 | ```rust 193 | use shamirsecretsharing::*; 194 | 195 | # let mut shares = create_shares(&vec![42; DATA_SIZE], 3, 3).unwrap(); 196 | // When `shares` contains a set of valid shares 197 | let restored = combine_shares(&shares).unwrap(); 198 | let data = restored.expect("`shares` did not contain a valid set of shares"); 199 | println!("Restored some data: {:?}", data); 200 | 201 | # // Remove a share s.t. the treshold is not reached 202 | # shares.pop(); 203 | // When `shares` contains an invalid set of shares 204 | let restored = combine_shares(&shares).unwrap(); 205 | assert_eq!(restored, None); 206 | ``` 207 | */ 208 | pub fn combine_shares(shares: &[Vec]) -> SSSResult>> { 209 | for (i, share) in shares.iter().enumerate() { 210 | if share.len() != SHARE_SIZE { 211 | return Err(SSSError::BadShareLen((i, share.len()))); 212 | } 213 | } 214 | 215 | let mut keyshares = Vec::with_capacity(shares.len()); 216 | for share in shares.iter() { 217 | keyshares.push(share[..KEYSHARE_SIZE].to_owned()); 218 | } 219 | let key_vec = hazmat::combine_keyshares(&keyshares)?; 220 | let mut key = [0; KEY_SIZE]; 221 | key.copy_from_slice(&key_vec); 222 | let cipher = XSalsa20Poly1305::new(&key.into()); 223 | for share in shares.iter() { 224 | let ciphertext = &share[KEYSHARE_SIZE..]; 225 | let nonce = [0; XSalsa20Poly1305::NONCE_SIZE]; 226 | if let Ok(plaintext) = cipher.decrypt(&nonce.into(), ciphertext) { 227 | return Ok(Some(plaintext)); 228 | } 229 | } 230 | Ok(None) 231 | } 232 | 233 | pub mod hazmat { 234 | /*! 235 | Hazardous materials (key-sharing) 236 | 237 | This is the `hazmat` module. This stands for **hazardous materials**. This module is only to 238 | be used by experts, because it does not have all the straightforward guarantees that the 239 | normal API has. E.g. where the [normal API](../index.html) prevents tampering with the shares, 240 | this API does not do any integrity checks, etc. Only use this module when you are really sure 241 | that Shamir secret sharing is secure in your use case! _If you are not sure about this, you 242 | are probably lost ([go back](../index.html))._ 243 | 244 | Example stuff that you will need to guarantee when using this API (not exhaustive): 245 | 246 | - All shared keys are uniformly random. 247 | - Keys produced by [`combine_keyshares`] are kept secret even if they did not manage to restore 248 | a secret. 249 | - _You_ will check the integrity of the restored secrets (or integrity is not a requirement). 250 | 251 | When your security model actually allows you to use the `hazmat` module, it can be a very 252 | powerful tool. In the normal API, the library wraps the secret data for the user in an AEAD 253 | `crypto_secretbox`. This guarantees the security items above. The `hazmat` module exposes the 254 | low level *key-sharing* API which allows you to bypass the AEAD wrapper leaving you with shares 255 | that are a lot shorter (useful for sharing bitcoin secret keys). You can also implement you own 256 | AEAD wrapper so that you can secret-share arbitrary long streams of data. 257 | 258 | ## Sharing data of arbitrary length 259 | 260 | [`create_shares`](../fn.create_shares.html) only shares buffers of exactly 64 bytes, which is 261 | of course quite limiting. However when using the keysharing module you can use an AEAD wrapper 262 | and share buffers of arbitrary length. I think an example is in place: 263 | 264 | ``` 265 | extern crate chacha20_poly1305_aead; 266 | extern crate rand; 267 | extern crate shamirsecretsharing; 268 | 269 | use chacha20_poly1305_aead::{encrypt, decrypt}; 270 | use shamirsecretsharing::hazmat::{create_keyshares, combine_keyshares}; 271 | 272 | /// Stores an encrypted message with a message authentication tag 273 | struct CryptoSecretbox { 274 | ciphertext: Vec, 275 | tag: Vec, 276 | } 277 | 278 | /// AEAD encrypt the message with `key` 279 | fn aead_wrap(key: &[u8], text: &[u8]) -> CryptoSecretbox { 280 | let nonce = vec![0; 12]; 281 | let mut ciphertext = Vec::with_capacity(text.len()); 282 | let tag = encrypt(&key, &nonce, &[], text, &mut ciphertext).unwrap().to_vec(); 283 | CryptoSecretbox { ciphertext: ciphertext, tag: tag } 284 | } 285 | 286 | /// AEAD decrypt the message with `key` 287 | fn aead_unwrap(key: &[u8], boxed: CryptoSecretbox) -> Vec { 288 | let CryptoSecretbox { ciphertext: ciphertext, tag: tag } = boxed; 289 | let nonce = vec![0; 12]; 290 | let mut text = Vec::with_capacity(ciphertext.len()); 291 | decrypt(&key, &nonce, &[], &ciphertext, &tag, &mut text).unwrap(); 292 | text 293 | } 294 | 295 | fn main() { 296 | let text = b"Snape kills Dumbledore!"; // Secret message 297 | let (boxed, keyshares) = { 298 | // Generate an ephemeral key 299 | let ref key = rand::random::<[u8; 32]>(); 300 | 301 | // Encrypt the text using the key 302 | let boxed = aead_wrap(key, text); 303 | 304 | // Share the key using `create_keyshares` 305 | let keyshares = create_keyshares(key, 2, 2).unwrap(); 306 | 307 | (boxed, keyshares) 308 | }; 309 | 310 | let restored = { 311 | // Recover the key using `combine_keyshares` 312 | let key = combine_keyshares(&keyshares).unwrap(); 313 | 314 | // Decrypt the secret message using the restored key 315 | aead_unwrap(&key, boxed) 316 | }; 317 | 318 | assert_eq!(restored, text); 319 | } 320 | ``` 321 | 322 | ## Sharing differently sized keys 323 | 324 | A keyshare is a string of 33 bytes. The first byte denotes the `x` coordinate in the Shamir 325 | secret sharing scheme. This `x`-coordinate can be viewed as the share "tag". The other 32 326 | bytes hold the actual data. Each byte of a keyshare corresponds to the same byte in the secret 327 | key. They are independent from one another. 328 | This makes it possible to share keys that are not necesarrily 32 bytes long, by truncating the 329 | shares. For example: 330 | 331 | ``` 332 | use shamirsecretsharing::hazmat::*; 333 | 334 | fn pad(vec: &mut Vec, desired_len: usize) { 335 | while vec.len() < desired_len { 336 | vec.push(Default::default()); 337 | } 338 | } 339 | 340 | let short_key = [42; 16]; // `key` holds a 128 bit key (16 bytes) 341 | let mut key = [0; KEY_SIZE]; 342 | &mut key[..16].copy_from_slice(&short_key); 343 | 344 | // Split the key into keyshares 345 | let mut keyshares = create_keyshares(&key, 3, 3).unwrap(); 346 | 347 | // The keyshares are 33 bytes long, only store the first 17 bytes (1 + 16 for x and y's) 348 | for mut keyshare in &mut keyshares { 349 | keyshare.truncate(17); // Truncate the last keyshare bytes 350 | pad(&mut keyshare, 33); // and put zeros in place 351 | } 352 | 353 | // Restore the key 354 | let restored = combine_keyshares(&keyshares).unwrap(); 355 | assert_eq!(restored, key); 356 | ``` 357 | The same trick is possible with keys that are longer than 32 bytes, to secret-share long keys 358 | in a streaming manner. But remember that the key must be uniformly random if you do not trust 359 | *all* the shareholders (which you probably don't otherwise you would not be using this crate). 360 | (In other words: Do not use this to share RSA keys, use an AEAD wrapper instead!) 361 | 362 | You might guess that this approach kills performance by a factor of 2, but this is not really 363 | true. Like a block cipher `sss` library performs all cryptographic computations in parrallel 364 | with block sizes of 32 bytes. Below 32 bytes we will still have to compute one block, so we 365 | cannot gain an additional speedup by secret-sharing less than 32 bytes of key material. 366 | 367 | I agree that with all this truncating and padding the code looks a bit messy, but I do not 368 | consider these kinds of tricks really considered mainstream anyway. 369 | 370 | [`create_keyshares`]: fn.create_keyshares.html 371 | [`combine_keyshares`]: fn.combine_keyshares.html 372 | */ 373 | 374 | use super::*; 375 | 376 | /// The size of the input data to `create_keyshares` 377 | pub const KEY_SIZE: usize = 32; 378 | 379 | /// Keyshare size from shares produced by `create_keyshares` 380 | pub const KEYSHARE_SIZE: usize = 33; 381 | 382 | /// Check `key` and return `Ok(())` if its length is correct for being shared with 383 | /// `create_keyshares` 384 | fn check_key_len(key: &[u8]) -> SSSResult<()> { 385 | if key.len() != KEY_SIZE { 386 | Err(SSSError::BadInputLen(key.len())) 387 | } else { 388 | Ok(()) 389 | } 390 | } 391 | 392 | /** 393 | Create a set of key shares 394 | 395 | - `key` must be a `&[u8]` slice of length `DATA_SIZE` (32) 396 | - `n` is the number of shares that is to be generated 397 | - `k` is the treshold value of how many shares are needed to restore the secret 398 | 399 | The value that is returned is a newly allocated vector of vectors. Each of these vectors will 400 | contain `KEYSHARE_SIZE` `u8` items. 401 | 402 | # Example 403 | ``` 404 | use shamirsecretsharing::hazmat::*; 405 | 406 | # let key = [42; KEY_SIZE]; 407 | // With a `key` vector containing a uniform key 408 | 409 | // Create a some key shares of the secret key 410 | let count = 5; 411 | let treshold = 4; 412 | let keyshares = create_keyshares(&key, count, treshold); 413 | match keyshares { 414 | Ok(keyshares) => println!("Created some keyshares: {:?}", keyshares), 415 | Err(err) => panic!("Oops! Something went wrong: {}", err), 416 | } 417 | ``` 418 | */ 419 | pub fn create_keyshares(key: &[u8], n: u8, k: u8) -> SSSResult>> { 420 | check_nk(n, k)?; 421 | check_key_len(key)?; 422 | 423 | let mut key_arr = [0; KEY_SIZE]; 424 | key_arr.copy_from_slice(key); 425 | 426 | // Restore the keyshares into one buffer 427 | let mut keyshares = Vec::with_capacity(n.into()); 428 | 429 | // Put the secret in the bottom part of the polynomial 430 | let poly0 = gf256::bitslice(&key_arr); 431 | 432 | // Randomly generate the other terms in the polynomial 433 | let mut poly = vec![gf256::Poly::default(); (k - 1).into()]; 434 | for coeff in poly.iter_mut() { 435 | *coeff = rand::random(); 436 | } 437 | 438 | for share_idx in 0..n { 439 | // x value is in 1..n 440 | let unbitsliced_x = share_idx + 1; 441 | let x = gf256::splat(unbitsliced_x); 442 | 443 | // Calculate y 444 | let mut y = poly0; 445 | let mut xpow = gf256::splat(1); 446 | for coeff_idx in 0..(k - 1).into() { 447 | xpow = gf256::mul(&xpow, &x); 448 | let tmp = gf256::mul(&xpow, &poly[coeff_idx]); 449 | y = gf256::add(&y, &tmp); 450 | } 451 | let y_unbitsliced = gf256::unbitslice(&y); 452 | let mut keyshare = vec![0; KEYSHARE_SIZE]; 453 | keyshare[0] = unbitsliced_x; 454 | keyshare[1..].copy_from_slice(&y_unbitsliced); 455 | keyshares.push(keyshare); 456 | } 457 | Ok(keyshares) 458 | } 459 | 460 | /** 461 | Combine a set of key shares and return the original key 462 | 463 | `keyshares` must be a slice of keyshare vectors. 464 | 465 | The return type will be a `Result` which will only be `Err(err)` of the input key shares were 466 | malformed. When the input key shares are of the correct length, this function will always 467 | return `Ok(_)`. 468 | 469 | Restoring the secret will fail in the same cases as with `combine_shares`: 470 | 471 | 1. More shares were needed to reach the treshold. 472 | 2. Shares of different sets (corresponding to different keys) were supplied or some of the 473 | keyshares were tampered with. 474 | 475 | Opposed to `combine_shares`, this function will always return a restored key buffer. This 476 | restored key MAY be correct. The function just performs the cryptographic calculation, but 477 | does not know if restoration succeeded. However, **treat all output from this function as 478 | secret**. Even if combining the key shares failed, the returned buffer can tell an attacker 479 | information of the shares that were used to make it. The best way to secure this is by using 480 | a cryptographic integrity check to secure the integrity of the key. 481 | 482 | # Example 483 | 484 | ```rust 485 | use shamirsecretsharing::hazmat::*; 486 | 487 | # let mut key = [42; KEY_SIZE]; 488 | # let mut keyshares = create_keyshares(&key, 3, 3).unwrap(); 489 | // When `keyshares` contains a set of valid shares for `key` 490 | let restored = combine_keyshares(&keyshares).unwrap(); 491 | assert_eq!(restored, key); 492 | 493 | # // Remove a key share s.t. the treshold is not reached 494 | # keyshares.pop(); 495 | // When `keyshares` contains an invalid set of key shares 496 | let restored = combine_keyshares(&keyshares).unwrap(); 497 | assert_ne!(restored, key); 498 | ``` 499 | */ 500 | pub fn combine_keyshares(keyshares: &[Vec]) -> SSSResult> { 501 | for (i, keyshare) in keyshares.iter().enumerate() { 502 | if keyshare.len() != KEYSHARE_SIZE { 503 | return Err(SSSError::BadShareLen((i, keyshare.len()))); 504 | } 505 | } 506 | 507 | // Collect the x and y values. 508 | let k = keyshares.len(); 509 | let mut xs = Vec::with_capacity(k); 510 | let mut ys = Vec::with_capacity(k); 511 | for keyshare in keyshares.iter() { 512 | xs.push(gf256::splat(keyshare[0])); 513 | let mut y_arr = [0; 32]; 514 | y_arr.copy_from_slice(&keyshare[1..]); 515 | ys.push(gf256::bitslice(&y_arr)); 516 | } 517 | 518 | let mut secret = gf256::Poly::default(); 519 | for (idx1, (x1, y)) in Iterator::zip(xs.iter(), ys.iter()).enumerate() { 520 | let mut num = gf256::splat(1); 521 | let mut denom = gf256::splat(1); 522 | for (idx2, x2) in xs.iter().enumerate() { 523 | if idx1 == idx2 { 524 | continue; 525 | } 526 | num = gf256::mul(&num, x2); 527 | let tmp = gf256::add(x1, x2); 528 | denom = gf256::mul(&denom, &tmp); 529 | } 530 | let denom_inv = gf256::inv(denom); // Inverted denominator 531 | let basis = gf256::mul(&num, &denom_inv); // Basis polynomial 532 | let scaled_coeff = gf256::mul(&basis, y); 533 | secret = gf256::add(&secret, &scaled_coeff); 534 | } 535 | let key = gf256::unbitslice(&secret); 536 | Ok(key.into()) 537 | } 538 | 539 | #[cfg(test)] 540 | mod tests { 541 | use super::*; 542 | const KEY: [u8; KEY_SIZE] = [42; KEY_SIZE]; 543 | 544 | #[test] 545 | fn test_create_keyshares_ok() { 546 | let keyshares = create_keyshares(&KEY, 5, 4).unwrap(); 547 | assert_eq!(keyshares.len(), 5); 548 | for keyshare in keyshares { 549 | assert_eq!(keyshare.len(), KEYSHARE_SIZE); 550 | } 551 | } 552 | 553 | #[test] 554 | fn test_create_keyshares_err() { 555 | assert_eq!(create_keyshares(&KEY, 0, 0), Err(SSSError::InvalidN(0))); 556 | assert_eq!(create_keyshares(&KEY, 5, 0), Err(SSSError::InvalidK(0))); 557 | assert_eq!(create_keyshares(&KEY, 5, 6), Err(SSSError::InvalidK(6))); 558 | assert_eq!(create_keyshares(&[], 5, 3), Err(SSSError::BadInputLen(0))); 559 | } 560 | 561 | #[test] 562 | fn test_combine_keyshares_ok() { 563 | let mut keyshares = create_keyshares(&KEY, 5, 4).unwrap(); 564 | assert_eq!(combine_keyshares(&keyshares).unwrap(), KEY); 565 | keyshares.pop(); 566 | assert_eq!(combine_keyshares(&keyshares).unwrap(), KEY); 567 | keyshares.pop(); 568 | assert_ne!(combine_keyshares(&keyshares).unwrap(), KEY); 569 | keyshares.pop(); 570 | assert_ne!(combine_keyshares(&keyshares).unwrap(), KEY); 571 | keyshares.pop(); 572 | assert_ne!(combine_keyshares(&keyshares).unwrap(), KEY); 573 | keyshares.pop(); 574 | assert_ne!(combine_keyshares(&keyshares).unwrap(), KEY); 575 | } 576 | 577 | #[test] 578 | fn test_combine_keyshares_err() { 579 | let keyshares = vec![vec![]]; 580 | assert_eq!( 581 | combine_keyshares(&keyshares), 582 | Err(SSSError::BadShareLen((0, 0))) 583 | ); 584 | } 585 | } 586 | } 587 | 588 | mod gf256 { 589 | pub type Poly = [u32; 8]; 590 | 591 | #[must_use] 592 | pub fn bitslice(x: &[u8; 32]) -> Poly { 593 | let mut r = [0u32; 8]; 594 | for (arr_idx, cur) in x.iter().enumerate() { 595 | for bit_idx in 0..8 { 596 | r[bit_idx] |= ((*cur as u32 >> bit_idx) & 1) << arr_idx; 597 | } 598 | } 599 | r 600 | } 601 | 602 | #[must_use] 603 | pub fn unbitslice(x: &Poly) -> [u8; 32] { 604 | let mut r = [0; 32]; 605 | for bit_idx in 0..8 { 606 | let cur = x[bit_idx] as u32; 607 | for (arr_idx, b) in r.iter_mut().enumerate() { 608 | *b |= (((cur >> arr_idx) & 1) as u8) << bit_idx; 609 | } 610 | } 611 | r 612 | } 613 | 614 | #[must_use] 615 | pub fn splat(x: u8) -> Poly { 616 | let mut r = Poly::default(); 617 | for (idx, cur) in r.iter_mut().enumerate() { 618 | let bit = u32::from(x) >> idx & 0x1; 619 | let (expand, _) = 0_i32.overflowing_sub(bit as i32); 620 | *cur = expand as u32; 621 | } 622 | r 623 | } 624 | 625 | /// Add (XOR) `r` with `x` and store the result in `r`. 626 | #[must_use] 627 | pub fn add(x1: &Poly, x2: &Poly) -> Poly { 628 | let mut r = *x1; 629 | let iter = Iterator::zip(r.iter_mut(), x2.iter()); 630 | for (acc, rhs) in iter { 631 | *acc ^= *rhs; 632 | } 633 | r 634 | } 635 | 636 | /// Safely multiply two bitsliced polynomials in GF(2^8) reduced by 637 | /// x^8 + x^4 + x^3 + x + 1. If you need to square a polynomial 638 | /// use `gf256::square` instead. 639 | #[must_use] 640 | pub fn mul(a: &Poly, b: &Poly) -> Poly { 641 | // This function implements Russian Peasant multiplication on two 642 | // bitsliced polynomials. 643 | // 644 | // I personally think that these kinds of long lists of operations 645 | // are often a bit ugly. A double for loop would be nicer and would 646 | // take up a lot less lines of code. 647 | // However, some compilers seem to fail in optimizing these kinds of 648 | // loops. So we will just have to do this by hand. 649 | // 650 | let mut a = *a; 651 | let mut r = [0; 8]; 652 | 653 | r[0] = a[0] & b[0]; // add (assignment, because r is 0) 654 | r[1] = a[1] & b[0]; 655 | r[2] = a[2] & b[0]; 656 | r[3] = a[3] & b[0]; 657 | r[4] = a[4] & b[0]; 658 | r[5] = a[5] & b[0]; 659 | r[6] = a[6] & b[0]; 660 | r[7] = a[7] & b[0]; 661 | a[0] ^= a[7]; // reduce 662 | a[2] ^= a[7]; 663 | a[3] ^= a[7]; 664 | 665 | r[0] ^= a[7] & b[1]; // add 666 | r[1] ^= a[0] & b[1]; 667 | r[2] ^= a[1] & b[1]; 668 | r[3] ^= a[2] & b[1]; 669 | r[4] ^= a[3] & b[1]; 670 | r[5] ^= a[4] & b[1]; 671 | r[6] ^= a[5] & b[1]; 672 | r[7] ^= a[6] & b[1]; 673 | a[7] ^= a[6]; // reduce 674 | a[1] ^= a[6]; 675 | a[2] ^= a[6]; 676 | 677 | r[0] ^= a[6] & b[2]; // add 678 | r[1] ^= a[7] & b[2]; 679 | r[2] ^= a[0] & b[2]; 680 | r[3] ^= a[1] & b[2]; 681 | r[4] ^= a[2] & b[2]; 682 | r[5] ^= a[3] & b[2]; 683 | r[6] ^= a[4] & b[2]; 684 | r[7] ^= a[5] & b[2]; 685 | a[6] ^= a[5]; // reduce 686 | a[0] ^= a[5]; 687 | a[1] ^= a[5]; 688 | 689 | r[0] ^= a[5] & b[3]; // add 690 | r[1] ^= a[6] & b[3]; 691 | r[2] ^= a[7] & b[3]; 692 | r[3] ^= a[0] & b[3]; 693 | r[4] ^= a[1] & b[3]; 694 | r[5] ^= a[2] & b[3]; 695 | r[6] ^= a[3] & b[3]; 696 | r[7] ^= a[4] & b[3]; 697 | a[5] ^= a[4]; // reduce 698 | a[7] ^= a[4]; 699 | a[0] ^= a[4]; 700 | 701 | r[0] ^= a[4] & b[4]; // add 702 | r[1] ^= a[5] & b[4]; 703 | r[2] ^= a[6] & b[4]; 704 | r[3] ^= a[7] & b[4]; 705 | r[4] ^= a[0] & b[4]; 706 | r[5] ^= a[1] & b[4]; 707 | r[6] ^= a[2] & b[4]; 708 | r[7] ^= a[3] & b[4]; 709 | a[4] ^= a[3]; // reduce 710 | a[6] ^= a[3]; 711 | a[7] ^= a[3]; 712 | 713 | r[0] ^= a[3] & b[5]; // add 714 | r[1] ^= a[4] & b[5]; 715 | r[2] ^= a[5] & b[5]; 716 | r[3] ^= a[6] & b[5]; 717 | r[4] ^= a[7] & b[5]; 718 | r[5] ^= a[0] & b[5]; 719 | r[6] ^= a[1] & b[5]; 720 | r[7] ^= a[2] & b[5]; 721 | a[3] ^= a[2]; // reduce 722 | a[5] ^= a[2]; 723 | a[6] ^= a[2]; 724 | 725 | r[0] ^= a[2] & b[6]; // add 726 | r[1] ^= a[3] & b[6]; 727 | r[2] ^= a[4] & b[6]; 728 | r[3] ^= a[5] & b[6]; 729 | r[4] ^= a[6] & b[6]; 730 | r[5] ^= a[7] & b[6]; 731 | r[6] ^= a[0] & b[6]; 732 | r[7] ^= a[1] & b[6]; 733 | a[2] ^= a[1]; // reduce 734 | a[4] ^= a[1]; 735 | a[5] ^= a[1]; 736 | 737 | r[0] ^= a[1] & b[7]; // add 738 | r[1] ^= a[2] & b[7]; 739 | r[2] ^= a[3] & b[7]; 740 | r[3] ^= a[4] & b[7]; 741 | r[4] ^= a[5] & b[7]; 742 | r[5] ^= a[6] & b[7]; 743 | r[6] ^= a[7] & b[7]; 744 | r[7] ^= a[0] & b[7]; 745 | 746 | r 747 | } 748 | 749 | /// Square `x` in GF(2^8) and write the result to `r`. 750 | #[must_use] 751 | pub fn square(x: &Poly) -> Poly { 752 | let mut r = [0; 8]; 753 | let r14; 754 | let r12; 755 | let mut r10; 756 | let mut r8; 757 | 758 | // Use the Freshman's Dream rule to square the polynomial. 759 | r14 = x[7]; 760 | r12 = x[6]; 761 | r10 = x[5]; 762 | r8 = x[4]; 763 | r[6] = x[3]; 764 | r[4] = x[2]; 765 | r[2] = x[1]; 766 | r[0] = x[0]; 767 | 768 | // Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 769 | r[7] = r14; // r[7] was 0 770 | r[6] ^= r14; 771 | r10 ^= r14; 772 | // Skip, because r13 is always 0 773 | r[4] ^= r12; 774 | r[5] = r12; // r[5] was 0 775 | r[7] ^= r12; 776 | r8 ^= r12; 777 | // Skip, because r11 is always 0 778 | r[2] ^= r10; 779 | r[3] = r10; // r[3] was 0 780 | r[5] ^= r10; 781 | r[6] ^= r10; 782 | r[1] = r14; // r[1] was 0 783 | r[2] ^= r14; // Substitute r9 by r14 because they will always be equa 784 | r[4] ^= r14; 785 | r[5] ^= r14; 786 | r[0] ^= r8; 787 | r[1] ^= r8; 788 | r[3] ^= r8; 789 | r[4] ^= r8; 790 | 791 | r 792 | } 793 | 794 | /// Invert `x` in GF(2^8) and write the result to `r` 795 | #[must_use] 796 | pub fn inv(x: Poly) -> Poly { 797 | let v1 = square(&x); // v1 = x^2 798 | let v2 = square(&v1); // v2 = x^4 799 | let v3 = square(&v2); // v3 = x^8 800 | let v4 = mul(&v3, &x); // v4 = x^9 801 | let v5 = square(&v3); // v5 = x^16 802 | let v6 = mul(&v5, &v4); // v6 = x^25 803 | let v7 = square(&v6); // v7 = x^50 804 | let v8 = square(&v7); // v8 = x^100 805 | let v9 = square(&v8); // v9 = x^200 806 | let v10 = mul(&v7, &v9); // v10 = x^250 807 | let v11 = mul(&v10, &v2); // v11 = x^254 808 | v11 809 | } 810 | } 811 | 812 | #[cfg(test)] 813 | mod tests { 814 | use super::*; 815 | use std::error::Error; 816 | const DATA: &[u8] = &[42; DATA_SIZE]; 817 | const VECTOR: &[&[u8]] = &[ 818 | &[ 819 | 1, 232, 113, 234, 13, 203, 140, 217, 18, 158, 76, 141, 48, 245, 6, 209, 255, 148, 102, 820 | 194, 8, 101, 30, 227, 230, 213, 22, 38, 240, 201, 240, 169, 64, 146, 238, 237, 69, 206, 821 | 113, 248, 187, 201, 125, 81, 116, 21, 167, 187, 38, 139, 105, 191, 100, 82, 57, 10, 822 | 198, 201, 137, 121, 51, 216, 207, 187, 158, 214, 11, 215, 97, 54, 45, 11, 76, 169, 199, 823 | 251, 104, 70, 4, 184, 58, 101, 176, 165, 63, 143, 137, 42, 148, 135, 57, 11, 31, 127, 824 | 51, 210, 117, 153, 54, 28, 47, 114, 86, 217, 172, 162, 142, 143, 230, 53, 81, 65, 49, 825 | ], 826 | &[ 827 | 2, 128, 84, 229, 185, 55, 144, 120, 251, 175, 136, 141, 35, 45, 221, 101, 117, 212, 97, 828 | 1, 216, 229, 97, 117, 247, 247, 202, 37, 31, 37, 112, 32, 111, 146, 238, 237, 69, 206, 829 | 113, 248, 187, 201, 125, 81, 116, 21, 167, 187, 38, 139, 105, 191, 100, 82, 57, 10, 830 | 198, 201, 137, 121, 51, 216, 207, 187, 158, 214, 11, 215, 97, 54, 45, 11, 76, 169, 199, 831 | 251, 104, 70, 4, 184, 58, 101, 176, 165, 63, 143, 137, 42, 148, 135, 57, 11, 31, 127, 832 | 51, 210, 117, 153, 54, 28, 47, 114, 86, 217, 172, 162, 142, 143, 230, 53, 81, 65, 49, 833 | ], 834 | &[ 835 | 3, 56, 109, 129, 47, 61, 65, 161, 27, 158, 170, 215, 19, 111, 203, 214, 208, 21, 211, 836 | 198, 76, 4, 163, 27, 207, 26, 192, 60, 213, 152, 143, 189, 110, 146, 238, 237, 69, 206, 837 | 113, 248, 187, 201, 125, 81, 116, 21, 167, 187, 38, 139, 105, 191, 100, 82, 57, 10, 838 | 198, 201, 137, 121, 51, 216, 207, 187, 158, 214, 11, 215, 97, 54, 45, 11, 76, 169, 199, 839 | 251, 104, 70, 4, 184, 58, 101, 176, 165, 63, 143, 137, 42, 148, 135, 57, 11, 31, 127, 840 | 51, 210, 117, 153, 54, 28, 47, 114, 86, 217, 172, 162, 142, 143, 230, 53, 81, 65, 49, 841 | ], 842 | &[ 843 | 4, 61, 7, 149, 120, 117, 162, 145, 96, 66, 212, 9, 12, 180, 236, 238, 230, 239, 207, 844 | 126, 155, 155, 200, 154, 83, 69, 115, 216, 234, 182, 219, 111, 236, 146, 238, 237, 69, 845 | 206, 113, 248, 187, 201, 125, 81, 116, 21, 167, 187, 38, 139, 105, 191, 100, 82, 57, 846 | 10, 198, 201, 137, 121, 51, 216, 207, 187, 158, 214, 11, 215, 97, 54, 45, 11, 76, 169, 847 | 199, 251, 104, 70, 4, 184, 58, 101, 176, 165, 63, 143, 137, 42, 148, 135, 57, 11, 31, 848 | 127, 51, 210, 117, 153, 54, 28, 47, 114, 86, 217, 172, 162, 142, 143, 230, 53, 81, 65, 849 | 49, 850 | ], 851 | &[ 852 | 5, 112, 243, 234, 40, 239, 5, 238, 230, 156, 158, 96, 139, 181, 55, 177, 17, 17, 151, 853 | 235, 238, 42, 157, 232, 75, 97, 238, 137, 115, 43, 201, 119, 248, 146, 238, 237, 69, 854 | 206, 113, 248, 187, 201, 125, 81, 116, 21, 167, 187, 38, 139, 105, 191, 100, 82, 57, 855 | 10, 198, 201, 137, 121, 51, 216, 207, 187, 158, 214, 11, 215, 97, 54, 45, 11, 76, 169, 856 | 199, 251, 104, 70, 4, 184, 58, 101, 176, 165, 63, 143, 137, 42, 148, 135, 57, 11, 31, 857 | 127, 51, 210, 117, 153, 54, 28, 47, 114, 86, 217, 172, 162, 142, 143, 230, 53, 81, 65, 858 | 49, 859 | ], 860 | ]; 861 | 862 | #[test] 863 | fn test_create_shares_ok() { 864 | let shares = create_shares(DATA, 5, 4).unwrap(); 865 | assert_eq!(shares.len(), 5); 866 | for share in shares { 867 | assert_eq!(share.len(), SHARE_SIZE); 868 | } 869 | } 870 | 871 | #[test] 872 | fn test_create_shares_err() { 873 | assert_eq!(create_shares(DATA, 0, 0), Err(SSSError::InvalidN(0))); 874 | assert_eq!(create_shares(DATA, 5, 0), Err(SSSError::InvalidK(0))); 875 | assert_eq!(create_shares(DATA, 5, 6), Err(SSSError::InvalidK(6))); 876 | assert_eq!(create_shares(&[], 5, 3), Err(SSSError::BadInputLen(0))); 877 | } 878 | 879 | #[test] 880 | fn test_combine_shares_ok() { 881 | let mut shares = create_shares(DATA, 5, 4).unwrap(); 882 | assert_eq!(combine_shares(&shares).unwrap().unwrap(), DATA); 883 | shares.pop(); 884 | assert_eq!(combine_shares(&shares).unwrap().unwrap(), DATA); 885 | shares.pop(); 886 | assert_eq!(combine_shares(&shares).unwrap(), None); 887 | shares.pop(); 888 | assert_eq!(combine_shares(&shares).unwrap(), None); 889 | shares.pop(); 890 | assert_eq!(combine_shares(&shares).unwrap(), None); 891 | shares.pop(); 892 | assert_eq!(combine_shares(&shares).unwrap(), None); 893 | } 894 | 895 | #[test] 896 | fn test_combine_shares_err() { 897 | let shares = vec![vec![]]; 898 | assert_eq!(combine_shares(&shares), Err(SSSError::BadShareLen((0, 0)))); 899 | } 900 | 901 | #[test] 902 | fn test_sss_error_display() { 903 | assert_eq!( 904 | format!("{}", SSSError::InvalidN(5)), 905 | "Error: invalid share count (5)" 906 | ); 907 | assert_eq!( 908 | format!("{}", SSSError::InvalidK(3)), 909 | "Error: invalid treshold (3)" 910 | ); 911 | assert_eq!( 912 | format!("{}", SSSError::BadShareLen((1, 2))), 913 | "Error: share 1 has bad length (2)" 914 | ); 915 | assert_eq!( 916 | format!("{}", SSSError::BadInputLen(0)), 917 | "Error: bad input length (0)" 918 | ); 919 | } 920 | 921 | #[test] 922 | #[allow(deprecated)] 923 | fn test_sss_error_description() { 924 | assert_eq!(SSSError::InvalidN(5).description(), "invalid n"); 925 | assert_eq!(SSSError::InvalidK(3).description(), "invalid k"); 926 | assert_eq!( 927 | SSSError::BadShareLen((0, 0)).description(), 928 | "bad share length" 929 | ); 930 | assert_eq!(SSSError::BadInputLen(0).description(), "bad input length"); 931 | } 932 | 933 | #[test] 934 | fn test_splat() { 935 | let expected = [!0, 0, 0, 0, 0, 0, 0, 0]; 936 | let actual = gf256::splat(1); 937 | assert_eq!(actual, expected); 938 | } 939 | 940 | #[test] 941 | fn test_vector() { 942 | let mut shares = Vec::with_capacity(VECTOR.len()); 943 | for share in VECTOR.iter() { 944 | let share: &[u8] = share; 945 | shares.push(share.to_vec()); 946 | } 947 | let data = combine_shares(&shares); 948 | assert_eq!(data, Ok(Some(DATA.to_vec()))); 949 | } 950 | } 951 | --------------------------------------------------------------------------------