feat: add secretshare
This commit is contained in:
8
__crypto/secretshare/CHANGELOG.md
Normal file
8
__crypto/secretshare/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2015-02-03:
|
||||||
|
|
||||||
|
* I changed the CRC-24 checksum computation to include the coding parameter
|
||||||
|
K and the share number N so that these numbers are also protected.
|
||||||
|
If you have older shares generated with a previous version, you can still
|
||||||
|
decode the secret by simply removing the checksum part of the shares.
|
||||||
|
* The README now includes more information about the inner workings of
|
||||||
|
secretshare and also a note on "perfect secrecy".
|
||||||
121
__crypto/secretshare/Cargo.lock
generated
Normal file
121
__crypto/secretshare/Cargo.lock
generated
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc24"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.147"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand 0.4.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-serialize"
|
||||||
|
version = "0.3.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secretshare"
|
||||||
|
version = "0.1.6"
|
||||||
|
dependencies = [
|
||||||
|
"crc24",
|
||||||
|
"getopts",
|
||||||
|
"rand 0.3.23",
|
||||||
|
"rustc-serialize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
13
__crypto/secretshare/Cargo.toml
Normal file
13
__crypto/secretshare/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "secretshare"
|
||||||
|
version = "0.1.6"
|
||||||
|
authors = ["Sebastian Gesemann <s.gesemann@gmail.com>"]
|
||||||
|
description = "This is an implementation of Shamir's secret sharing scheme."
|
||||||
|
license = "GPLv3"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
getopts = "0.2"
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
crc24 = "0.1"
|
||||||
|
rand = "0.3"
|
||||||
114
__crypto/secretshare/README.md
Normal file
114
__crypto/secretshare/README.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
> From: https://github.com/sellibitze/secretshare
|
||||||
|
|
||||||
|
# secretshare
|
||||||
|
|
||||||
|
This program is an implementation of
|
||||||
|
[Shamir's secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing).
|
||||||
|
A secret can be split into N shares in a way so that
|
||||||
|
a selectable number of shares K (with K ≤ N) is required
|
||||||
|
to reconstruct the secret again.
|
||||||
|
|
||||||
|
**Warning**: I don't yet recommend the serious use of this tool. The
|
||||||
|
encoding of the shares might change in a newer version in which case
|
||||||
|
you would have trouble decoding secrets that have been shared using
|
||||||
|
an older version of the program. For now, this is experimental.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
Passing a secret to secretshare for encoding:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo My secret | ./secretshare -e2,5
|
||||||
|
2-1-1YAYwmOHqZ69jA-v+mz
|
||||||
|
2-2-YJZQDGm22Y77Gw-IhSh
|
||||||
|
2-3-+G9ovW9SAnUynQ-Elwi
|
||||||
|
2-4-F7rAjX3UOa53KA-b2vm
|
||||||
|
2-5-j0P4PHsw4lW+rg-XyNl
|
||||||
|
```
|
||||||
|
|
||||||
|
The parameters following the `-e` option tell `secretshare` to create 5 shares of which 2 will be necessary for decoding.
|
||||||
|
|
||||||
|
Decoding a subset of shares (one share per line) can be done like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo -e "2-2-YJZQDGm22Y77Gw-IhSh \n 2-4-F7rAjX3UOa53KA-b2vm" | ./secretshare -d
|
||||||
|
My secret
|
||||||
|
```
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
This project is Cargo-enabled. So, you should be able to build it with
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
once you have made sure that `rustc` (the compiler) and `cargo`
|
||||||
|
(the build and dependency management tool) are installed.
|
||||||
|
Visit the [Rust homepage](http://www.rust-lang.org/) if you are
|
||||||
|
don't know where to get these tools.
|
||||||
|
|
||||||
|
# I/O
|
||||||
|
|
||||||
|
The secret data does not have to be text. `secretshare` treats it as
|
||||||
|
binary data. But, of course, you can feed it text as well. In the above
|
||||||
|
example the echo command terminated the string with a line feed which
|
||||||
|
is actually part of the secret and output as well after decoding.
|
||||||
|
Note that, while `secretshare` supports secrets of up to 64 KiB
|
||||||
|
it makes little sense to use such large secrets directly. In situations
|
||||||
|
where you want to share larger secrets, you would usually pick a random
|
||||||
|
password for encryption and use that password as secret for `secretshare`.
|
||||||
|
|
||||||
|
The generated shares are lines of ASCII text.
|
||||||
|
|
||||||
|
# Structure of the shares
|
||||||
|
|
||||||
|
```
|
||||||
|
2-1-LiTyeXwEP71IUA-Qj6n
|
||||||
|
^ ^ ^^^^^^^^^^^^^^ ^^^^
|
||||||
|
K N D C
|
||||||
|
```
|
||||||
|
|
||||||
|
A share is built out of three or four parts separated with a minus: K-N-D-C.
|
||||||
|
The last part is optional. K is one of the encoding parameters that tell you
|
||||||
|
how many distinct
|
||||||
|
shares of a specific secret are necessary to be able to recover the
|
||||||
|
secret. The number N identifies the share (ranging from 1 to the number
|
||||||
|
of shares that have been created). The D part is a Base64 encoding of
|
||||||
|
a specific share's raw data. The optional part C is a Base64 encoding
|
||||||
|
of a CRC-24 checksum of the concatenation of K and N as bytes followed
|
||||||
|
by the share's raw data (before Base64 encoding). The same checksum
|
||||||
|
algorithm is used in the OpenPGP format for “ASCII amoring”.
|
||||||
|
|
||||||
|
# A word on the secrecy
|
||||||
|
|
||||||
|
Shamir's secret sharing is known to have the perfect secrecy property.
|
||||||
|
In the context of (K,N)-threshold schemes this means that if you have
|
||||||
|
less than K shares available, you have absolutely no information about
|
||||||
|
what the secret is except for its length. The checksums that are included
|
||||||
|
in the shares
|
||||||
|
also don't reveal anything about the secret.
|
||||||
|
They are just a simple integrity protection of the shares themselves.
|
||||||
|
In other words, given a share without checksum, we can derive a share
|
||||||
|
with a checksum. This obviously does not add any new information.
|
||||||
|
|
||||||
|
# Galois field
|
||||||
|
|
||||||
|
Shamir's secret sharing algorithm requires the use of polynomials over
|
||||||
|
a finite field. One easy way of constructing a finite field is to pick
|
||||||
|
a prime number p, use the integers 0, 1, 2, ..., p-1 as field elements
|
||||||
|
and simply use modular arithmetic (mod p) for the field operations.
|
||||||
|
|
||||||
|
So, you *could* pick a prime like 257 to apply Shamir's algorithm
|
||||||
|
byte-wise. The downside of this is that the shares would consist of
|
||||||
|
sequences of values each between 0 and 256 *inclusive*. So, you would
|
||||||
|
need more than 8 bits to encode each of them.
|
||||||
|
|
||||||
|
But there is another way. We are not restricted to so-called
|
||||||
|
prime fields. There are also non-prime fields where the number of
|
||||||
|
elements is a *power* of a prime, for example 2^8=256. It's just
|
||||||
|
a bit harder to explain how they are constructed. The finite
|
||||||
|
field I used is the same as the one you can find in the RAID 6
|
||||||
|
implementation of the Linux kernel or the Anubis block cipher:
|
||||||
|
Gf(2^8) reduction polynomial is x^8 + x^4 + x^3 + x^2 + 1 or
|
||||||
|
alternatively 11D in hex.
|
||||||
143
__crypto/secretshare/src/gf256.rs
Normal file
143
__crypto/secretshare/src/gf256.rs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
//! This module provides the Gf256 type which is used to represent
|
||||||
|
//! elements of a finite field wich 256 elements.
|
||||||
|
|
||||||
|
use std::num::Wrapping;
|
||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
const POLY: u8 = 0x1D; // represents x^8 + x^4 + x^3 + x^2 + 1
|
||||||
|
|
||||||
|
/// replicates the least significant bit to every other bit
|
||||||
|
#[inline]
|
||||||
|
fn mask(bit: u8) -> u8 {
|
||||||
|
(Wrapping(0u8) - Wrapping(bit & 1)).0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// multiplies a polynomial with x and returns the residual
|
||||||
|
/// of the polynomial division with POLY as divisor
|
||||||
|
#[inline]
|
||||||
|
fn xtimes(poly: u8) -> u8 {
|
||||||
|
(poly << 1) ^ (mask(poly >> 7) & POLY)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tables used for multiplication and division
|
||||||
|
struct Tables {
|
||||||
|
exp: [u8; 256],
|
||||||
|
log: [u8; 256],
|
||||||
|
inv: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
static mut TABLES: Tables = Tables {
|
||||||
|
exp: [0; 256],
|
||||||
|
log: [0; 256],
|
||||||
|
inv: [0; 256],
|
||||||
|
};
|
||||||
|
|
||||||
|
fn get_tables() -> &'static Tables {
|
||||||
|
INIT.call_once(|| {
|
||||||
|
// mutable access is fine because of synchronization via INIT
|
||||||
|
let tabs = unsafe { &mut TABLES };
|
||||||
|
let mut tmp = 1;
|
||||||
|
for power in 0..255usize {
|
||||||
|
tabs.exp[power] = tmp;
|
||||||
|
tabs.log[tmp as usize] = power as u8;
|
||||||
|
tmp = xtimes(tmp);
|
||||||
|
}
|
||||||
|
tabs.exp[255] = 1;
|
||||||
|
for x in 1..256usize {
|
||||||
|
let l = tabs.log[x];
|
||||||
|
let nl = if l == 0 { 0 } else { 255 - l };
|
||||||
|
let i = tabs.exp[nl as usize];
|
||||||
|
tabs.inv[x] = i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// We're guaranteed to have TABLES initialized by now
|
||||||
|
return unsafe { &TABLES };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type for elements of a finite field with 256 elements
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Gf256 {
|
||||||
|
pub poly: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gf256 {
|
||||||
|
/// returns the additive neutral element of the field
|
||||||
|
#[inline]
|
||||||
|
pub fn zero() -> Gf256 {
|
||||||
|
Gf256 { poly: 0 }
|
||||||
|
}
|
||||||
|
/// returns the multiplicative neutral element of the field
|
||||||
|
#[inline]
|
||||||
|
pub fn one() -> Gf256 {
|
||||||
|
Gf256 { poly: 1 }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn from_byte(b: u8) -> Gf256 {
|
||||||
|
Gf256 { poly: b }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn to_byte(&self) -> u8 {
|
||||||
|
self.poly
|
||||||
|
}
|
||||||
|
pub fn log(&self) -> Option<u8> {
|
||||||
|
if self.poly == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let tabs = get_tables();
|
||||||
|
Some(tabs.log[self.poly as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn exp(power: u8) -> Gf256 {
|
||||||
|
let tabs = get_tables();
|
||||||
|
Gf256 { poly: tabs.exp[power as usize] }
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
pub fn inv(&self) -> Option<Gf256> {
|
||||||
|
self.log().map(|l| Gf256::exp(255 - l))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Gf256> for Gf256 {
|
||||||
|
type Output = Gf256;
|
||||||
|
#[inline]
|
||||||
|
fn add(self, rhs: Gf256) -> Gf256 {
|
||||||
|
Gf256::from_byte(self.poly ^ rhs.poly)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Gf256> for Gf256 {
|
||||||
|
type Output = Gf256;
|
||||||
|
#[inline]
|
||||||
|
fn sub(self, rhs: Gf256) -> Gf256 {
|
||||||
|
Gf256::from_byte(self.poly ^ rhs.poly)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Gf256> for Gf256 {
|
||||||
|
type Output = Gf256;
|
||||||
|
fn mul(self, rhs: Gf256) -> Gf256 {
|
||||||
|
if let (Some(l1), Some(l2)) = (self.log(), rhs.log()) {
|
||||||
|
let tmp = ((l1 as u16) + (l2 as u16)) % 255;
|
||||||
|
Gf256::exp(tmp as u8)
|
||||||
|
} else {
|
||||||
|
Gf256 { poly: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Gf256> for Gf256 {
|
||||||
|
type Output = Gf256;
|
||||||
|
fn div(self, rhs: Gf256) -> Gf256 {
|
||||||
|
let l2 = rhs.log().expect("division by zero");
|
||||||
|
if let Some(l1) = self.log() {
|
||||||
|
let tmp = ((l1 as u16) + 255 - (l2 as u16)) % 255;
|
||||||
|
Gf256::exp(tmp as u8)
|
||||||
|
} else {
|
||||||
|
Gf256 { poly: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
345
__crypto/secretshare/src/main.rs
Normal file
345
__crypto/secretshare/src/main.rs
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
extern crate rustc_serialize as serialize;
|
||||||
|
extern crate getopts;
|
||||||
|
extern crate crc24;
|
||||||
|
extern crate rand;
|
||||||
|
|
||||||
|
use std::convert;
|
||||||
|
use std::env;
|
||||||
|
use std::error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::iter::repeat;
|
||||||
|
use std::num;
|
||||||
|
|
||||||
|
use rand::{Rng, OsRng};
|
||||||
|
use getopts::Options;
|
||||||
|
use serialize::base64::{self, FromBase64, ToBase64};
|
||||||
|
|
||||||
|
use gf256::Gf256;
|
||||||
|
|
||||||
|
mod gf256;
|
||||||
|
|
||||||
|
fn new_vec<T: Clone>(n: usize, x: T) -> Vec<T> {
|
||||||
|
repeat(x).take(n).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Error {
|
||||||
|
descr: &'static str,
|
||||||
|
detail: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
fn new(descr: &'static str, detail: Option<String>) -> Error {
|
||||||
|
Error { descr: descr, detail: detail }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self.detail {
|
||||||
|
None => write!(f, "{}", self.descr),
|
||||||
|
Some(ref detail) => write!(f, "{} ({})", self.descr, detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn description(&self) -> &str { self.descr }
|
||||||
|
fn cause(&self) -> Option<&dyn error::Error> { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl convert::From<Error> for io::Error {
|
||||||
|
fn from(me: Error) -> io::Error {
|
||||||
|
io::Error::new(io::ErrorKind::Other, me)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a try!-like macro for Option<T> expressions that takes
|
||||||
|
// a &'static str as error message as 2nd parameter
|
||||||
|
// and creates an Error out of it if necessary.
|
||||||
|
macro_rules! otry {
|
||||||
|
($o:expr, $e:expr) => (
|
||||||
|
match $o {
|
||||||
|
Some(thing_) => thing_,
|
||||||
|
None => return Err(convert::From::from(Error::new($e, None)))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// maps a ParseIntError to an io::Error
|
||||||
|
fn pie2io(p: num::ParseIntError) -> io::Error {
|
||||||
|
convert::From::from(
|
||||||
|
Error::new("Integer parsing error", Some(p.to_string()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn other_io_err(descr: &'static str, detail: Option<String>) -> io::Error {
|
||||||
|
convert::From::from(
|
||||||
|
Error::new(descr, detail)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// evaluates a polynomial at x=1, 2, 3, ... n (inclusive)
|
||||||
|
fn encode<W: Write>(src: &[u8], n: u8, w: &mut W) -> io::Result<()> {
|
||||||
|
for raw_x in 1..((n as u16) + 1) {
|
||||||
|
let x = Gf256::from_byte(raw_x as u8);
|
||||||
|
let mut fac = Gf256::one();
|
||||||
|
let mut acc = Gf256::zero();
|
||||||
|
for &coeff in src.iter() {
|
||||||
|
acc = acc + fac * Gf256::from_byte(coeff);
|
||||||
|
fac = fac * x;
|
||||||
|
}
|
||||||
|
w.write(&[acc.to_byte()])?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// evaluates an interpolated polynomial at `raw_x` where
|
||||||
|
/// the polynomial is determined using Lagrangian interpolation
|
||||||
|
/// based on the given x/y coordinates `src`.
|
||||||
|
fn lagrange_interpolate(src: &[(u8, u8)], raw_x: u8) -> u8 {
|
||||||
|
let x = Gf256::from_byte(raw_x);
|
||||||
|
let mut sum = Gf256::zero();
|
||||||
|
for (i, &(raw_xi, raw_yi)) in src.iter().enumerate() {
|
||||||
|
let xi = Gf256::from_byte(raw_xi);
|
||||||
|
let yi = Gf256::from_byte(raw_yi);
|
||||||
|
let mut lix = Gf256::one();
|
||||||
|
for (j, &(raw_xj, _)) in src.iter().enumerate() {
|
||||||
|
if i != j {
|
||||||
|
let xj = Gf256::from_byte(raw_xj);
|
||||||
|
let delta = xi - xj;
|
||||||
|
assert!(delta.poly != 0, "Duplicate shares");
|
||||||
|
lix = lix * (x - xj) / delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum = sum + lix * yi;
|
||||||
|
}
|
||||||
|
sum.to_byte()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn secret_share(src: &[u8], k: u8, n: u8) -> io::Result<Vec<Vec<u8>>> {
|
||||||
|
let mut result = Vec::with_capacity(n as usize);
|
||||||
|
for _ in 0..(n as usize) {
|
||||||
|
result.push(new_vec(src.len(), 0u8));
|
||||||
|
}
|
||||||
|
let mut col_in = new_vec(k as usize, 0u8);
|
||||||
|
let mut col_out = Vec::with_capacity(n as usize);
|
||||||
|
let mut osrng = OsRng::new()?;
|
||||||
|
for (c, &s) in src.iter().enumerate() {
|
||||||
|
col_in[0] = s;
|
||||||
|
osrng.fill_bytes(&mut col_in[1..]);
|
||||||
|
col_out.clear();
|
||||||
|
encode(&*col_in, n, &mut col_out)?;
|
||||||
|
for (&y, share) in col_out.iter().zip(result.iter_mut()) {
|
||||||
|
share[c] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Action {
|
||||||
|
Encode(u8, u8),
|
||||||
|
// k and n parameter
|
||||||
|
Decode,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_k_n(s: &str) -> io::Result<(u8, u8)> {
|
||||||
|
let mut iter = s.split(',');
|
||||||
|
let msg = "K and N have to be separated with a comma";
|
||||||
|
let s1 = otry!(iter.next(), msg).trim();
|
||||||
|
let s2 = otry!(iter.next(), msg).trim();
|
||||||
|
let k = s1.parse().map_err(pie2io)?;
|
||||||
|
let n = s2.parse().map_err(pie2io)?;
|
||||||
|
Ok((k, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// computes a CRC-24 hash over the concatenated coding parameters k, n
|
||||||
|
/// and the raw share data
|
||||||
|
fn crc24_as_bytes(k: u8, n: u8, octets: &[u8]) -> [u8; 3] {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
let mut h = crc24::Crc24Hasher::new();
|
||||||
|
h.write(&[k, n]);
|
||||||
|
h.write(octets);
|
||||||
|
let v = h.finish();
|
||||||
|
|
||||||
|
[((v >> 16) & 0xFF) as u8,
|
||||||
|
((v >> 8) & 0xFF) as u8,
|
||||||
|
(v & 0xFF) as u8]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_encode(k: u8, n: u8, with_checksums: bool) -> io::Result<()> {
|
||||||
|
let secret = {
|
||||||
|
let limit: usize = 0x10000;
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let mut locked = stdin.lock();
|
||||||
|
let mut tmp: Vec<u8> = Vec::new();
|
||||||
|
locked.by_ref().take(limit as u64).read_to_end(&mut tmp)?;
|
||||||
|
if tmp.len() == limit {
|
||||||
|
let mut dummy = [0u8];
|
||||||
|
if locked.read(&mut dummy)? > 0 {
|
||||||
|
return Err(other_io_err("Secret too large",
|
||||||
|
Some(format!("My limit is at {} bytes.", limit))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp
|
||||||
|
};
|
||||||
|
let shares = secret_share(&*secret, k, n)?;
|
||||||
|
let config = base64::Config {
|
||||||
|
pad: false,
|
||||||
|
..base64::STANDARD
|
||||||
|
};
|
||||||
|
for (index, share) in shares.iter().enumerate() {
|
||||||
|
let salad = share.to_base64(config);
|
||||||
|
if with_checksums {
|
||||||
|
let crc_bytes = crc24_as_bytes(k, (index + 1) as u8, &**share);
|
||||||
|
println!("{}-{}-{}-{}", k, index + 1, salad, crc_bytes.to_base64(config));
|
||||||
|
} else {
|
||||||
|
println!("{}-{}-{}", k, index + 1, salad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// reads shares from stdin and returns Ok(k, shares) on success
|
||||||
|
/// where shares is a Vec<(u8, Vec<u8>)> representing x-coordinates
|
||||||
|
/// and share data.
|
||||||
|
fn read_shares() -> io::Result<(u8, Vec<(u8, Vec<u8>)>)> {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let stdin = io::BufReader::new(stdin.lock());
|
||||||
|
let mut opt_k_l: Option<(u8, usize)> = None;
|
||||||
|
let mut counter = 0u8;
|
||||||
|
let mut shares: Vec<(u8, Vec<u8>)> = Vec::new();
|
||||||
|
for line in stdin.lines() {
|
||||||
|
let line = line?;
|
||||||
|
let parts: Vec<_> = line.trim().split('-').collect();
|
||||||
|
if parts.len() < 3 || parts.len() > 4 {
|
||||||
|
return Err(other_io_err("Share parse error: Expected 3 or 4 \
|
||||||
|
parts searated by a minus sign", None));
|
||||||
|
}
|
||||||
|
let (k, n, p3, opt_p4) = {
|
||||||
|
let mut iter = parts.into_iter();
|
||||||
|
let k = iter.next().unwrap().parse::<u8>().map_err(pie2io)?;
|
||||||
|
let n = iter.next().unwrap().parse::<u8>().map_err(pie2io)?;
|
||||||
|
let p3 = iter.next().unwrap();
|
||||||
|
let opt_p4 = iter.next();
|
||||||
|
(k, n, p3, opt_p4)
|
||||||
|
};
|
||||||
|
if k < 1 || n < 1 {
|
||||||
|
return Err(other_io_err("Share parse error: Illegal K,N parameters", None));
|
||||||
|
}
|
||||||
|
let data = p3.from_base64().map_err(|_| other_io_err(
|
||||||
|
"Share parse error: Base64 decoding of data block failed", None))?;
|
||||||
|
if let Some(check) = opt_p4 {
|
||||||
|
if check.len() != 4 {
|
||||||
|
return Err(other_io_err("Share parse error: Checksum part is \
|
||||||
|
expected to be four characters", None));
|
||||||
|
}
|
||||||
|
let crc_bytes = check.from_base64().map_err(|_| other_io_err(
|
||||||
|
"Share parse error: Base64 decoding of checksum failed", None))?;
|
||||||
|
let mychksum = crc24_as_bytes(k, n, &*data);
|
||||||
|
if crc_bytes != mychksum {
|
||||||
|
return Err(other_io_err("Share parse error: Checksum mismatch", None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some((ck, cl)) = opt_k_l {
|
||||||
|
if ck != k || cl != data.len() {
|
||||||
|
return Err(other_io_err("Incompatible shares", None));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt_k_l = Some((k, data.len()));
|
||||||
|
}
|
||||||
|
if shares.iter().all(|s| s.0 != n) {
|
||||||
|
shares.push((n, data));
|
||||||
|
counter += 1;
|
||||||
|
if counter == k {
|
||||||
|
return Ok((k, shares));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(other_io_err("Not enough shares provided!", None))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_decode() -> io::Result<()> {
|
||||||
|
let (k, shares) = read_shares()?;
|
||||||
|
assert!(!shares.is_empty());
|
||||||
|
let slen = shares[0].1.len();
|
||||||
|
let mut col_in = Vec::with_capacity(k as usize);
|
||||||
|
let mut secret = Vec::with_capacity(slen);
|
||||||
|
for byteindex in 0..slen {
|
||||||
|
col_in.clear();
|
||||||
|
for s in shares.iter().take(k as usize) {
|
||||||
|
col_in.push((s.0, s.1[byteindex]));
|
||||||
|
}
|
||||||
|
secret.push(lagrange_interpolate(&*col_in, 0u8));
|
||||||
|
}
|
||||||
|
let mut out = io::stdout();
|
||||||
|
out.write_all(&*secret)?;
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
let mut opts = Options::new();
|
||||||
|
opts.optflag("h", "help", "print this help text");
|
||||||
|
opts.optflag("d", "decode", "for decoding");
|
||||||
|
opts.optopt("e", "encode", "for encoding, K is the required number of \
|
||||||
|
shares for decoding, N is the number of shares \
|
||||||
|
to generate. 1 <= K <= N <= 255", "K,N");
|
||||||
|
let opt_matches = match opts.parse(&args[1..]) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(f) => {
|
||||||
|
drop(writeln!(&mut stderr, "Error: {}", f));
|
||||||
|
// env::set_exit_status(1); // FIXME: unstable feature
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.len() < 2 || opt_matches.opt_present("h") {
|
||||||
|
println!(
|
||||||
|
"The program secretshare is an implementation of Shamir's secret sharing scheme.\n\
|
||||||
|
It is applied byte-wise within a finite field for arbitrarily long secrets.\n");
|
||||||
|
println!("{}", opts.usage("Usage: secretshare [options]"));
|
||||||
|
println!("Input is read from STDIN and output is written to STDOUT.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action: Result<_, _> =
|
||||||
|
match (opt_matches.opt_present("e"), opt_matches.opt_present("d")) {
|
||||||
|
(false, false) => Err("Nothing to do! Use -e or -d"),
|
||||||
|
(true, true) => Err("Use either -e or -d and not both"),
|
||||||
|
(false, true) => Ok(Action::Decode),
|
||||||
|
(true, false) => {
|
||||||
|
if let Some(param) = opt_matches.opt_str("e") {
|
||||||
|
if let Ok((k, n)) = parse_k_n(&*param) {
|
||||||
|
if 0 < k && k <= n {
|
||||||
|
Ok(Action::Encode(k, n))
|
||||||
|
} else {
|
||||||
|
Err("Invalid encoding parameters K,N")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Could not parse K,N parameters")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("No parameter for -e or -d provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match action {
|
||||||
|
Ok(Action::Encode(k, n)) => perform_encode(k, n, true),
|
||||||
|
Ok(Action::Decode) => perform_decode(),
|
||||||
|
Err(e) => Err(other_io_err(e, None))
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
drop(writeln!(&mut stderr, "{}", e));
|
||||||
|
// env::set_exit_status(1); // FIXME: unstable feature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user