├── .gitignore ├── src ├── crypto │ ├── mod.rs │ ├── openssl.rs │ └── eccoxide.rs ├── types.rs ├── math.rs ├── dleq.rs ├── pdleq.rs ├── main.rs ├── lib.rs ├── simple.rs └── scrape.rs ├── Cargo.toml ├── LICENSE ├── README.md └── .github └── workflows └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "openssl")] 2 | mod openssl; 3 | 4 | #[cfg(feature = "eccoxide")] 5 | mod eccoxide; 6 | 7 | #[cfg(feature = "eccoxide")] 8 | pub use self::eccoxide::*; 9 | 10 | #[cfg(feature = "openssl")] 11 | pub use self::openssl::*; 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pvss" 3 | version = "0.2.0" 4 | authors = ["Vincent Hanquez "] 5 | description = "Publicly verifiable secret sharing implementations" 6 | homepage = "https://github.com/vincenthz/rust-pvss" 7 | repository = "https://github.com/vincenthz/rust-pvss" 8 | license = "MIT" 9 | keywords = [ "Crypto", "Cryptography", "Secret", "Sharing" ] 10 | categories = [ "cryptography" ] 11 | edition = "2024" 12 | 13 | [lib] 14 | name = "pvss" 15 | 16 | [dependencies] 17 | openssl = { version = "0.10", optional = true } 18 | eccoxide = { version = "0.3", optional = true } 19 | cryptoxide = { version = "0.5", optional = true } 20 | getrandom = { version = "0.2", optional = true } 21 | 22 | [features] 23 | default = ["eccoxide"] 24 | openssl = ["dep:openssl"] 25 | eccoxide = ["dep:eccoxide", "dep:cryptoxide", "dep:getrandom"] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2023 Typed IO 2 | Copyright (c) 2016-2017 IOHK 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to 9 | do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::Scalar; 2 | use core::num::NonZeroU32; 3 | 4 | // threshold need to >= 2 5 | pub type Threshold = u32; 6 | 7 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] 8 | pub struct ShareId(NonZeroU32); 9 | 10 | impl ShareId { 11 | pub fn first() -> Self { 12 | ShareId(NonZeroU32::new(1).unwrap()) 13 | } 14 | 15 | pub fn next(self) -> Self { 16 | ShareId(self.0.checked_add(1).unwrap()) 17 | } 18 | 19 | pub fn from_u32(v: u32) -> Option { 20 | NonZeroU32::new(v).map(ShareId) 21 | } 22 | 23 | pub fn to_scalar(self) -> Scalar { 24 | Scalar::from_u32(self.0.get()) 25 | } 26 | 27 | pub fn as_index(self) -> usize { 28 | self.0.get() as usize - 1 29 | } 30 | } 31 | 32 | pub struct ShareIdsSequence(ShareId); 33 | 34 | impl ShareIdsSequence { 35 | pub fn new() -> Self { 36 | Self(ShareId::first()) 37 | } 38 | } 39 | 40 | impl Iterator for ShareIdsSequence { 41 | type Item = ShareId; 42 | 43 | fn next(&mut self) -> Option { 44 | let c = self.0; 45 | self.0 = self.0.next(); 46 | Some(c) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/math.rs: -------------------------------------------------------------------------------- 1 | // Math module define polynomial types and operations that is used to setup the scheme. 2 | use super::crypto; 3 | 4 | #[derive(Clone)] 5 | pub struct Polynomial { 6 | pub elements: Vec, 7 | } 8 | 9 | impl Polynomial { 10 | /// generate a new polynomial of specific degree 11 | pub fn generate(drg: &mut crypto::Drg, degree: u32) -> Polynomial { 12 | let vec_size = degree + 1; 13 | let mut vec = Vec::with_capacity(vec_size as usize); 14 | 15 | for _ in 0..vec_size { 16 | let r = crypto::Scalar::generate(drg); 17 | vec.push(r) 18 | } 19 | Polynomial { elements: vec } 20 | } 21 | 22 | pub fn len(&self) -> usize { 23 | self.elements.len() 24 | } 25 | 26 | /// get the value of a polynomial a0 + a1 * x^1 + a2 * x^2 + .. + an * x^n for a value x=at 27 | pub fn evaluate(&self, at: crypto::Scalar) -> crypto::Scalar { 28 | let mut r = crypto::Scalar::from_u32(0); 29 | for degree in 0..(self.elements.len()) { 30 | let v = &self.elements[degree]; 31 | r = r + v * &at.pow(degree as u32); 32 | } 33 | r 34 | } 35 | pub fn at_zero(&self) -> &crypto::Scalar { 36 | &self.elements[0] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/dleq.rs: -------------------------------------------------------------------------------- 1 | // DLEQ proof through g^a 2 | use super::crypto::*; 3 | 4 | type Challenge = Scalar; 5 | 6 | #[derive(Clone)] 7 | pub struct DLEQ<'a, 'b, 'c, 'd> { 8 | pub g1: &'a Point, 9 | pub h1: &'b Point, 10 | pub g2: &'c Point, 11 | pub h2: &'d Point, 12 | } 13 | 14 | #[derive(Clone)] 15 | pub struct Proof { 16 | c: Challenge, 17 | z: Scalar, 18 | } 19 | 20 | impl Proof { 21 | pub fn create(w: &Scalar, a: &Scalar, dleq: &DLEQ) -> Proof { 22 | let a1 = dleq.g1.mul(&w); 23 | let a2 = dleq.g2.mul(&w); 24 | let c = PointHasher::new() 25 | .update(&dleq.h1) 26 | .update(&dleq.h2) 27 | .update(&a1) 28 | .update(&a2) 29 | .finalize(); 30 | let r = w + &(a * &c); 31 | Proof { c, z: r } 32 | } 33 | 34 | pub fn verify(&self, dleq: &DLEQ) -> bool { 35 | let r1 = dleq.g1.mul(&self.z); 36 | let r2 = dleq.g2.mul(&self.z); 37 | let a1 = r1 - dleq.h1.mul(&self.c); 38 | let a2 = r2 - dleq.h2.mul(&self.c); 39 | let c = PointHasher::new() 40 | .update(&dleq.h1) 41 | .update(&dleq.h2) 42 | .update(&a1) 43 | .update(&a2) 44 | .finalize(); 45 | self.c == c 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust PVSS (Publicly Verifiable Secret Sharing) 2 | 3 | [![Crates.io][crates-badge]][crates-url] 4 | [![MIT licensed][mit-badge]][mit-url] 5 | [![APACHE-2 licensed][apache2-badge]][apache2-url] 6 | [![Build Status][actions-badge]][actions-url] 7 | 8 | [crates-badge]: https://img.shields.io/crates/v/pvss.svg 9 | [crates-url]: https://crates.io/crates/pvss 10 | [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg 11 | [apache2-badge]: https://img.shields.io/badge/license-APACHE--2-blue.svg 12 | [mit-url]: https://github.com/vincenthz/rust-pvss/blob/master/LICENSE-MIT 13 | [apache2-url]: https://github.com/vincenthz/rust-pvss/blob/master/LICENSE-APACHE 14 | [actions-badge]: https://github.com/vincenthz/rust-pvss/workflows/CI/badge.svg 15 | [actions-url]: https://github.com/vincenthz/rust-pvss/actions?query=workflow%3ACI+branch%3Amaster 16 | 17 | [API Docs](https://docs.rs/pvss/latest/pvss) 18 | 19 | This package provide secret sharing schemes which are publicly veriable and recoverable 20 | using a simple `t` out of `n` `(t,n)` threshold system. 21 | 22 | A secret value can be **escrow** to N encrypted shares. 23 | 24 | This secret value can be recovered by decrypting at least `t` amount of shares, 25 | and combining them. 26 | 27 | Publicly Verifiable Secret Sharing (PVSS) scheme implemented: 28 | 29 | * [Schoenmaker](http://www.win.tue.nl/~berry/papers/crypto99.pdf) 30 | * [SCRAPE](https://eprint.iacr.org/2017/216.pdf) 31 | 32 | ## Crypto 33 | 34 | For now the implementation uses the P256R1 elliptic curve by default, through 35 | the eccoxide package which wrap fiat-crypto code for this specific curve. 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # copy of https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md 2 | on: [push, pull_request] 3 | 4 | name: CI 5 | 6 | jobs: 7 | check: 8 | name: Check 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macos-latest] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: stable 19 | override: true 20 | - uses: actions-rs/cargo@v1 21 | with: 22 | command: check 23 | 24 | test: 25 | name: Test Suite 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, windows-latest, macos-latest] 29 | runs-on: ${{ matrix.os }} 30 | steps: 31 | - uses: actions/checkout@v1 32 | - uses: actions-rs/toolchain@v1 33 | with: 34 | profile: minimal 35 | toolchain: stable 36 | override: true 37 | - uses: actions-rs/cargo@v1 38 | with: 39 | command: test 40 | 41 | fmt: 42 | name: Rustfmt 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v1 46 | - uses: actions-rs/toolchain@v1 47 | with: 48 | profile: minimal 49 | toolchain: stable 50 | override: true 51 | - run: rustup component add rustfmt 52 | - uses: actions-rs/cargo@v1 53 | with: 54 | command: fmt 55 | args: --all -- --check 56 | 57 | #clippy: 58 | # name: Clippy 59 | # runs-on: ubuntu-latest 60 | # steps: 61 | # - uses: actions/checkout@v1 62 | # - uses: actions-rs/toolchain@v1 63 | # with: 64 | # profile: minimal 65 | # toolchain: stable 66 | # override: true 67 | # - run: rustup component add clippy 68 | # - uses: actions-rs/cargo@v1 69 | # with: 70 | # command: clippy 71 | # args: -- -D warnings 72 | -------------------------------------------------------------------------------- /src/pdleq.rs: -------------------------------------------------------------------------------- 1 | // Parallel DLEQ proofs 2 | use super::crypto::*; 3 | use super::dleq; 4 | 5 | type Challenge = Scalar; 6 | 7 | #[derive(Clone)] 8 | pub struct Proof { 9 | c: Challenge, 10 | zs: Vec, 11 | } 12 | 13 | impl Proof { 14 | pub fn create(params: &[(Scalar, &Scalar, dleq::DLEQ)]) -> Proof { 15 | let mut zs = Vec::with_capacity(params.len()); 16 | 17 | let mut hasher = PointHasher::new(); 18 | 19 | // create the list [h1_1 ,h2_1 , h1_2 , h2_2, ... h2_n, a1_1, a2_1, .., a1_n, a2_n ] 20 | // to compute the challenge 21 | for param in params.iter() { 22 | let &(ref w, _, ref dleq) = param; 23 | hasher = hasher 24 | .update(&dleq.h1) 25 | .update(&dleq.h2) 26 | .update(&dleq.g1.mul(&w)) 27 | .update(&dleq.g2.mul(&w)); 28 | } 29 | 30 | // compute the challenge 31 | let c = hasher.finalize(); 32 | 33 | // finally create each proofs 34 | for (w, a, _) in params.iter() { 35 | let z = w + &(*a * &c); 36 | zs.push(z); 37 | } 38 | Proof { c, zs } 39 | } 40 | 41 | pub fn verify(&self, dleqs: &[dleq::DLEQ]) -> bool { 42 | if dleqs.len() != self.zs.len() { 43 | // FIXME probably an Err() .. instead of silent verify failure 44 | return false; 45 | }; 46 | 47 | let mut hasher = PointHasher::new(); 48 | 49 | // recompute the challenge 50 | for (i, z) in self.zs.iter().enumerate() { 51 | let dleq = &dleqs[i]; 52 | let r1 = dleq.g1.mul(z); 53 | let r2 = dleq.g2.mul(z); 54 | let a1 = r1 - dleq.h1.mul(&self.c); 55 | let a2 = r2 - dleq.h2.mul(&self.c); 56 | 57 | hasher = hasher 58 | .update(&dleq.h1) 59 | .update(&dleq.h2) 60 | .update(&a1) 61 | .update(&a2); 62 | } 63 | 64 | let c = hasher.finalize(); 65 | 66 | self.c == c 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate pvss; 2 | 3 | use std::fmt; 4 | 5 | /// Slice pretty print helper 6 | pub struct PrettySlice<'a>(&'a [u8]); 7 | 8 | impl<'a> fmt::Display for PrettySlice<'a> { 9 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 10 | for i in 0..self.0.len() { 11 | write!(f, "{:02x}", self.0[i])?; 12 | } 13 | Ok(()) 14 | } 15 | } 16 | 17 | /// Trait to allow a type to be pretty-printed in `format!`, where unoverridable 18 | /// defaults cannot otherwise be avoided. 19 | pub trait ToPretty { 20 | /// Convert a type into a derivative form in order to make `format!` print it prettily. 21 | fn pretty<'a>(&'a self) -> PrettySlice<'a>; 22 | /// Express the object as a hex string. 23 | fn to_hex(&self) -> String { 24 | format!("{}", self.pretty()) 25 | } 26 | } 27 | 28 | impl> ToPretty for T { 29 | fn pretty<'a>(&'a self) -> PrettySlice<'a> { 30 | PrettySlice(self.as_ref()) 31 | } 32 | } 33 | 34 | fn main() { 35 | let t = 10; 36 | 37 | let mut drg = pvss::crypto::Drg::new(); 38 | 39 | let mut keys = Vec::with_capacity(100); 40 | let mut pubs = Vec::with_capacity(100); 41 | for _ in 0..100 { 42 | let (public, private) = pvss::crypto::create_keypair(&mut drg); 43 | 44 | keys.push(private); 45 | pubs.push(public); 46 | } 47 | 48 | // Round trip public key through bytes 49 | let pub_bytes = pubs[1].to_bytes(); 50 | println!("pub_bytes = {}", pub_bytes.to_hex()); 51 | let pub_0 = pvss::crypto::PublicKey::from_bytes(&pub_bytes); 52 | assert!(pub_0 == pubs[1]); 53 | 54 | // Round trip private key through bytes 55 | let priv_bytes = keys[1].to_bytes(); 56 | println!("priv_bytes = {}", priv_bytes.to_hex()); 57 | let priv_0 = pvss::crypto::PrivateKey::from_bytes(&priv_bytes); 58 | assert!(priv_0 == keys[1]); 59 | 60 | let escrow = pvss::simple::escrow(&mut drg, t); 61 | 62 | let commitments = pvss::simple::commitments(&escrow); 63 | let shares = pvss::simple::create_shares(&mut drg, &escrow, &pubs); 64 | 65 | let mut decrypted = Vec::with_capacity(100); 66 | 67 | println!("publickeys: {nb_keys}", nb_keys = pubs.len()); 68 | println!("shares: {nb_shares}", nb_shares = shares.len()); 69 | 70 | for share in shares { 71 | let idx = share.id.as_index(); 72 | let verified_encrypted = 73 | share.verify(share.id, &pubs[idx], &escrow.extra_generator, &commitments); 74 | println!( 75 | "encrypted share {id:?}: {verified}", 76 | id = share.id, 77 | verified = verified_encrypted 78 | ); 79 | 80 | let d = pvss::simple::decrypt_share(&mut drg, &keys[idx], &pubs[idx], &share); 81 | let verified_decrypted = d.verify(&pubs[idx], &share); 82 | println!( 83 | "decrypted share {id:?}: {verified}", 84 | id = share.id, 85 | verified = verified_decrypted 86 | ); 87 | decrypted.push(d); 88 | } 89 | 90 | let recovered = pvss::simple::recover(t, decrypted.as_slice()).unwrap(); 91 | println!("equal: {b}", b = (recovered == escrow.secret)); 92 | } 93 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod crypto; 2 | 3 | mod dleq; 4 | mod math; 5 | mod pdleq; 6 | pub mod scrape; 7 | pub mod simple; 8 | mod types; 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::crypto; 13 | use super::crypto::*; 14 | use super::dleq; 15 | use super::scrape; 16 | use super::simple; 17 | 18 | pub const NB_TESTS: usize = 100; 19 | #[test] 20 | fn crypto_point_add_identity() { 21 | let mut drg = Drg::new(); 22 | for _ in 0..NB_TESTS { 23 | let i = Scalar::generate(&mut drg); 24 | let p = Point::from_scalar(&i); 25 | assert!(p.clone() + Point::infinity() == p); 26 | } 27 | } 28 | 29 | #[test] 30 | fn crypto_point_generator() { 31 | let g = Point::generator(); 32 | let mut drg = Drg::new(); 33 | for _ in 0..NB_TESTS { 34 | let i = Scalar::generate(&mut drg); 35 | let p1 = Point::from_scalar(&i); 36 | let p2 = g.mul(&i); 37 | assert!(p1 == p2); 38 | } 39 | } 40 | 41 | #[test] 42 | fn dleq_works() { 43 | let mut drg = Drg::new(); 44 | for _ in 0..NB_TESTS { 45 | let a = Scalar::generate(&mut drg); 46 | let w = Scalar::generate(&mut drg); 47 | let extra_gen = Point::from_scalar(&Scalar::generate(&mut drg)); 48 | 49 | let lifted_a = Point::from_scalar(&a); 50 | let lifted_extra_a = extra_gen.mul(&a); 51 | 52 | let dleq = dleq::DLEQ { 53 | g1: &Point::generator(), 54 | h1: &lifted_a, 55 | g2: &extra_gen, 56 | h2: &lifted_extra_a, 57 | }; 58 | let proof = dleq::Proof::create(&w, &a, &dleq); 59 | assert!(proof.verify(&dleq)); 60 | } 61 | } 62 | 63 | #[test] 64 | fn pvss_works() { 65 | let tests = [ 66 | (1, 4), 67 | (5, 5), 68 | (2, 8), 69 | (10, 50), 70 | (48, 50), 71 | (2, 20), 72 | (10, 100), 73 | ]; 74 | let mut drg = Drg::new(); 75 | for test in tests.iter() { 76 | let &(t, nb_keys) = test; 77 | println!("t={} n={}", t, nb_keys); 78 | 79 | let mut keys = Vec::with_capacity(nb_keys); 80 | let mut pubs = Vec::with_capacity(nb_keys); 81 | for _ in 0..nb_keys { 82 | let (public, private) = crypto::create_keypair(&mut drg); 83 | keys.push(private); 84 | pubs.push(public); 85 | } 86 | 87 | let escrow = simple::escrow(&mut drg, t); 88 | 89 | let commitments = simple::commitments(&escrow); 90 | let shares = simple::create_shares(&mut drg, &escrow, &pubs); 91 | 92 | let mut decrypted = Vec::with_capacity(100); 93 | 94 | assert_eq!(t as usize, commitments.len()); 95 | assert_eq!(pubs.len(), shares.len()); 96 | 97 | for share in shares { 98 | /* share ids start at 1 */ 99 | let idx = share.id.as_index(); 100 | let verified_encrypted = 101 | share.verify(share.id, &pubs[idx], &escrow.extra_generator, &commitments); 102 | assert!( 103 | verified_encrypted, 104 | "encrypted share {:?} verification failed", 105 | share.id 106 | ); 107 | 108 | let d = simple::decrypt_share(&mut drg, &keys[idx], &pubs[idx], &share); 109 | let verified_decrypted = d.verify(&pubs[idx], &share); 110 | assert!(verified_decrypted); 111 | decrypted.push(d); 112 | } 113 | 114 | let recovered = simple::recover(t, decrypted.as_slice()).unwrap(); 115 | 116 | assert!(recovered == escrow.secret); 117 | let verify_secret = simple::verify_secret( 118 | recovered, 119 | escrow.extra_generator, 120 | &commitments, 121 | escrow.proof, 122 | ); 123 | assert!(verify_secret, "secret not verified"); 124 | } 125 | } 126 | 127 | #[test] 128 | fn scrape_works() { 129 | let tests = [(1, 4), (2, 8), (10, 50), (48, 50), (2, 20), (10, 100)]; 130 | let mut drg = Drg::new(); 131 | for test in tests.iter() { 132 | let &(t, nb_keys) = test; 133 | println!("t={} n={}", t, nb_keys); 134 | 135 | let mut keys = Vec::with_capacity(nb_keys); 136 | let mut pubs = Vec::with_capacity(nb_keys); 137 | for _ in 0..nb_keys { 138 | let (public, private) = crypto::create_keypair(&mut drg); 139 | keys.push(private); 140 | pubs.push(public); 141 | } 142 | 143 | let escrow = scrape::escrow(&mut drg, t); 144 | 145 | let public_shares = scrape::create_shares(&mut drg, &escrow, &pubs); 146 | 147 | let mut decrypted = Vec::with_capacity(100); 148 | 149 | assert_eq!(nb_keys, public_shares.commitments.len()); 150 | assert_eq!(nb_keys, public_shares.encrypted_shares.len()); 151 | 152 | assert!(public_shares.verify(&mut drg, &pubs)); 153 | 154 | for share in &public_shares.encrypted_shares { 155 | let idx = share.id.as_index(); 156 | let d = scrape::decrypt_share(&mut drg, &keys[idx], &pubs[idx], &share); 157 | let verified_decrypted = d.verify(&pubs[idx], &share); 158 | assert!(verified_decrypted); 159 | decrypted.push(d); 160 | } 161 | 162 | let recovered = scrape::recover(t, decrypted.as_slice()).unwrap(); 163 | assert!(recovered == escrow.secret); 164 | 165 | let verify_secret = scrape::verify_secret(recovered, &public_shares); 166 | assert!(verify_secret, "secret not verified"); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/simple.rs: -------------------------------------------------------------------------------- 1 | // implementation of the simple publicly verifiable secret sharing scheme 2 | // http://www.win.tue.nl/~berry/papers/crypto99.pdf 3 | 4 | use super::dleq; 5 | use super::math; 6 | use super::types::*; 7 | 8 | use super::crypto::*; 9 | 10 | use std::borrow::Borrow; 11 | 12 | pub type Secret = Point; 13 | 14 | #[derive(Clone)] 15 | pub struct Escrow { 16 | pub extra_generator: Point, 17 | pub polynomial: math::Polynomial, 18 | pub secret: Secret, 19 | pub proof: dleq::Proof, 20 | } 21 | 22 | #[derive(Clone)] 23 | pub struct Commitment { 24 | point: Point, 25 | } 26 | 27 | #[derive(Clone)] 28 | pub struct EncryptedShare { 29 | pub id: ShareId, 30 | encrypted_val: Point, 31 | proof: dleq::Proof, 32 | } 33 | 34 | #[derive(Clone)] 35 | pub struct DecryptedShare { 36 | pub id: ShareId, 37 | decrypted_val: Point, 38 | proof: dleq::Proof, 39 | } 40 | 41 | // create a new escrow parameter. 42 | // the only parameter needed is the threshold necessary to be able to reconstruct. 43 | pub fn escrow(drg: &mut Drg, t: Threshold) -> Escrow { 44 | assert!(t >= 1, "threshold is invalid; < 1"); 45 | 46 | let poly = math::Polynomial::generate(drg, t - 1); 47 | let generator = Point::random_generator(drg); 48 | 49 | let secret = poly.at_zero(); 50 | let g_s = Point::from_scalar(&secret); 51 | 52 | let challenge = Scalar::generate(drg); 53 | let dleq = dleq::DLEQ { 54 | g1: &Point::generator(), 55 | h1: &g_s, 56 | g2: &generator, 57 | h2: &generator.mul(&secret), 58 | }; 59 | let proof = dleq::Proof::create(&challenge, &secret, &dleq); 60 | 61 | Escrow { 62 | extra_generator: generator, 63 | polynomial: poly, 64 | secret: g_s, 65 | proof, 66 | } 67 | } 68 | 69 | pub fn commitments(escrow: &Escrow) -> Vec { 70 | let mut commitments = Vec::with_capacity(escrow.polynomial.len()); 71 | 72 | for i in 0..(escrow.polynomial.len()) { 73 | let com = Commitment { 74 | point: escrow.extra_generator.mul(&escrow.polynomial.elements[i]), 75 | }; 76 | commitments.push(com); 77 | } 78 | commitments 79 | } 80 | 81 | pub fn create_share( 82 | drg: &mut Drg, 83 | escrow: &Escrow, 84 | share_id: ShareId, 85 | public: &PublicKey, 86 | ) -> EncryptedShare { 87 | let peval = escrow.polynomial.evaluate(share_id.to_scalar()); 88 | let challenge = Scalar::generate(drg); 89 | let xi = escrow.extra_generator.mul(&peval); 90 | let yi = public.point.mul(&peval); 91 | let dleq = dleq::DLEQ { 92 | g1: &escrow.extra_generator, 93 | h1: &xi, 94 | g2: &public.point, 95 | h2: &yi, 96 | }; 97 | let proof = dleq::Proof::create(&challenge, &peval, &dleq); 98 | EncryptedShare { 99 | id: share_id, 100 | encrypted_val: yi, 101 | proof, 102 | } 103 | } 104 | 105 | pub fn create_shares(drg: &mut Drg, escrow: &Escrow, pubs: I) -> Vec 106 | where 107 | I: IntoIterator, 108 | K: Borrow, 109 | { 110 | ShareIdsSequence::new() 111 | .zip(pubs.into_iter()) 112 | .map(|(i, pub_key)| create_share(drg, escrow, i, pub_key.borrow())) 113 | .collect() 114 | } 115 | 116 | fn create_xi(id: ShareId, commitments: &[Commitment]) -> Point { 117 | let mut r = Point::infinity(); 118 | for (j, com) in commitments.iter().enumerate() { 119 | let e = id.to_scalar().pow(j as u32); 120 | r = r + com.point.mul(&e); 121 | } 122 | r 123 | } 124 | 125 | impl EncryptedShare { 126 | pub fn verify( 127 | &self, 128 | id: ShareId, 129 | public: &PublicKey, 130 | extra_generator: &Point, 131 | commitments: &[Commitment], 132 | ) -> bool { 133 | let xi = create_xi(id, commitments); 134 | let dleq = dleq::DLEQ { 135 | g1: &extra_generator, 136 | h1: &xi, 137 | g2: &public.point, 138 | h2: &self.encrypted_val, 139 | }; 140 | self.proof.verify(&dleq) 141 | } 142 | } 143 | 144 | impl DecryptedShare { 145 | pub fn verify(&self, public: &PublicKey, eshare: &EncryptedShare) -> bool { 146 | let dleq = dleq::DLEQ { 147 | g1: &Point::generator(), 148 | h1: &public.point, 149 | g2: &self.decrypted_val, 150 | h2: &eshare.encrypted_val, 151 | }; 152 | self.proof.verify(&dleq) 153 | } 154 | } 155 | 156 | pub fn decrypt_share( 157 | drg: &mut Drg, 158 | private: &PrivateKey, 159 | public: &PublicKey, 160 | share: &EncryptedShare, 161 | ) -> DecryptedShare { 162 | let challenge = Scalar::generate(drg); 163 | let xi = &private.scalar; 164 | let yi = &public.point; 165 | let lifted_yi = &share.encrypted_val; 166 | let xi_inverse = xi.inverse(); 167 | let si = lifted_yi.mul(&xi_inverse); 168 | let dleq = dleq::DLEQ { 169 | g1: &Point::generator(), 170 | h1: &yi, 171 | g2: &si, 172 | h2: &lifted_yi, 173 | }; 174 | let proof = dleq::Proof::create(&challenge, xi, &dleq); 175 | DecryptedShare { 176 | id: share.id, 177 | decrypted_val: si, 178 | proof, 179 | } 180 | } 181 | 182 | fn interpolate_one(t: Threshold, sid: usize, shares: &[DecryptedShare]) -> Scalar { 183 | let mut v = Scalar::from_u32(1); 184 | for j in 0..(t as usize) { 185 | if j != sid { 186 | let sj = shares[j].id.to_scalar(); 187 | let si = shares[sid].id.to_scalar(); 188 | let d = &sj - &si; 189 | let dinv = d.inverse(); 190 | let e = sj * dinv; 191 | v = v * e; 192 | } 193 | } 194 | v 195 | } 196 | 197 | // Try to recover a secret 198 | pub fn recover(t: Threshold, shares: &[DecryptedShare]) -> Result { 199 | if t as usize > shares.len() { 200 | return Err(()); 201 | }; 202 | let mut result = Point::infinity(); 203 | for i in 0..(t as usize) { 204 | let v = interpolate_one(t, i, shares); 205 | result = result + shares[i].decrypted_val.mul(&v); 206 | } 207 | Ok(result) 208 | } 209 | 210 | pub fn verify_secret( 211 | secret: Secret, 212 | extra_generator: Point, 213 | commitments: &[Commitment], 214 | proof: dleq::Proof, 215 | ) -> bool { 216 | let dleq = dleq::DLEQ { 217 | g1: &Point::generator(), 218 | h1: &secret, 219 | g2: &extra_generator, 220 | h2: &commitments[0].point, 221 | }; 222 | proof.verify(&dleq) 223 | } 224 | -------------------------------------------------------------------------------- /src/scrape.rs: -------------------------------------------------------------------------------- 1 | // implementation of SCRAPE: Scalable Randomness Attested by Public Entities 2 | // https://eprint.iacr.org/2017/216.pdf 3 | 4 | use super::crypto::*; 5 | use super::dleq; 6 | use super::math; 7 | use super::pdleq; 8 | use super::types::*; 9 | 10 | pub type Secret = Point; 11 | 12 | // a new escrowing context. 13 | // this contains secret values (polynomial & secret) that are newly created. 14 | // this also contains by-product (extra_generator & proof) which are useful for 15 | // the protocol 16 | #[derive(Clone)] 17 | pub struct Escrow { 18 | pub threshold: Threshold, 19 | pub extra_generator: Point, 20 | pub polynomial: math::Polynomial, 21 | pub secret: Secret, 22 | pub proof: dleq::Proof, 23 | } 24 | 25 | // Public values for a successful run of secret sharing. 26 | // 27 | // This contains everything for self verification and 28 | // and the shares of each participants 29 | // 30 | // there should be N encrypted_shares and N commitments 31 | // the parallel proofs should N elements too. 32 | #[derive(Clone)] 33 | pub struct PublicShares { 34 | pub threshold: Threshold, 35 | pub extra_generator: Point, 36 | pub secret_proof: dleq::Proof, 37 | pub encrypted_shares: Vec, 38 | pub commitments: Vec, 39 | pub proofs: pdleq::Proof, 40 | } 41 | 42 | #[derive(Clone)] 43 | pub struct Commitment { 44 | point: Point, 45 | } 46 | 47 | #[derive(Clone)] 48 | pub struct EncryptedShare { 49 | pub id: ShareId, 50 | encrypted_val: Point, 51 | } 52 | 53 | #[derive(Clone)] 54 | pub struct DecryptedShare { 55 | pub id: ShareId, 56 | decrypted_val: Point, 57 | proof: dleq::Proof, 58 | } 59 | 60 | // create a new escrow parameter. 61 | // the only parameter needed is the threshold necessary to be able to reconstruct. 62 | pub fn escrow(drg: &mut Drg, t: Threshold) -> Escrow { 63 | assert!(t >= 1, "threshold is invalid; < 1"); 64 | 65 | let poly = math::Polynomial::generate(drg, t - 1); 66 | let generator = Point::random_generator(drg); 67 | 68 | let secret = poly.at_zero(); 69 | let g_s = Point::from_scalar(&secret); 70 | 71 | let challenge = Scalar::generate(drg); 72 | let dleq = dleq::DLEQ { 73 | g1: &Point::generator(), 74 | h1: &g_s, 75 | g2: &generator, 76 | h2: &generator.mul(&secret), 77 | }; 78 | let proof = dleq::Proof::create(&challenge, &secret, &dleq); 79 | 80 | Escrow { 81 | threshold: t, 82 | extra_generator: generator, 83 | polynomial: poly, 84 | secret: g_s, 85 | proof, 86 | } 87 | } 88 | 89 | pub fn create_shares(drg: &mut Drg, escrow: &Escrow, pubs: &[PublicKey]) -> PublicShares { 90 | let n = pubs.len(); 91 | let mut shares = Vec::with_capacity(n); 92 | let mut commitments = Vec::with_capacity(n); 93 | let mut sis = Vec::with_capacity(n); 94 | let mut pparams = Vec::with_capacity(n); 95 | 96 | for (i, public) in ShareIdsSequence::new().zip(pubs.iter()) { 97 | let si = escrow.polynomial.evaluate(i.to_scalar()); 98 | let esi = public.point.mul(&si); 99 | let vi = escrow.extra_generator.mul(&si); 100 | 101 | shares.push(EncryptedShare { 102 | id: i, 103 | encrypted_val: esi, 104 | }); 105 | commitments.push(Commitment { point: vi }); 106 | sis.push(si); 107 | } 108 | 109 | for (((s, c), public), si) in shares 110 | .iter() 111 | .zip(commitments.iter()) 112 | .zip(pubs.iter()) 113 | .zip(sis.iter()) 114 | { 115 | { 116 | let w = Scalar::generate(drg); 117 | let dleq = dleq::DLEQ { 118 | g1: &escrow.extra_generator, 119 | h1: &c.point, 120 | g2: &public.point, 121 | h2: &s.encrypted_val, 122 | }; 123 | pparams.push((w, si, dleq)); 124 | } 125 | } 126 | 127 | // now create the parallel proof for all shares 128 | let pdleq = pdleq::Proof::create(pparams.as_slice()); 129 | 130 | PublicShares { 131 | threshold: escrow.threshold, 132 | extra_generator: escrow.extra_generator.clone(), 133 | secret_proof: escrow.proof.clone(), 134 | encrypted_shares: shares, 135 | commitments, 136 | proofs: pdleq, 137 | } 138 | } 139 | 140 | impl PublicShares { 141 | pub fn number_participants(&self) -> u32 { 142 | self.commitments.len() as u32 143 | } 144 | 145 | pub fn verify(&self, drg: &mut Drg, publics: &[PublicKey]) -> bool { 146 | // recreate all the DLEQs 147 | let mut dleqs = Vec::with_capacity(publics.len()); 148 | for (i, public) in publics.iter().enumerate() { 149 | let vi = &self.commitments[i].point; 150 | let esi = &self.encrypted_shares[i].encrypted_val; 151 | let dleq = dleq::DLEQ { 152 | g1: &self.extra_generator, 153 | h1: &vi, 154 | g2: &public.point, 155 | h2: &esi, 156 | }; 157 | dleqs.push(dleq); 158 | } 159 | // verify the parallel proof 160 | if !self.proofs.verify(dleqs.as_slice()) { 161 | return false; 162 | } 163 | 164 | // reed solomon check 165 | let n = self.number_participants(); 166 | let poly = math::Polynomial::generate(drg, n - self.threshold - 1); 167 | 168 | let mut v = Point::infinity(); 169 | for i in 0..n { 170 | let idx = i as usize; 171 | 172 | let mut cperp = poly.evaluate(Scalar::from_u32(i)); 173 | for j in 0..n { 174 | if i != j { 175 | cperp = cperp * (Scalar::from_u32(i) - Scalar::from_u32(j)).inverse(); 176 | } 177 | } 178 | 179 | let commitment = &self.commitments[idx]; 180 | v = v + commitment.point.mul(&cperp); 181 | } 182 | 183 | v == Point::infinity() 184 | } 185 | } 186 | 187 | impl DecryptedShare { 188 | pub fn verify(&self, public: &PublicKey, eshare: &EncryptedShare) -> bool { 189 | let dleq = dleq::DLEQ { 190 | g1: &Point::generator(), 191 | h1: &public.point, 192 | g2: &self.decrypted_val, 193 | h2: &eshare.encrypted_val, 194 | }; 195 | self.proof.verify(&dleq) 196 | } 197 | } 198 | 199 | pub fn decrypt_share( 200 | drg: &mut Drg, 201 | private: &PrivateKey, 202 | public: &PublicKey, 203 | share: &EncryptedShare, 204 | ) -> DecryptedShare { 205 | let challenge = Scalar::generate(drg); 206 | let xi = &private.scalar; 207 | let yi = &public.point; 208 | let lifted_yi = &share.encrypted_val; 209 | let si = lifted_yi.mul(&xi.inverse()); 210 | let dleq = dleq::DLEQ { 211 | g1: &Point::generator(), 212 | h1: &yi, 213 | g2: &si, 214 | h2: &lifted_yi, 215 | }; 216 | let proof = dleq::Proof::create(&challenge, &xi, &dleq); 217 | DecryptedShare { 218 | id: share.id, 219 | decrypted_val: si, 220 | proof, 221 | } 222 | } 223 | 224 | fn interpolate_one(t: Threshold, sid: usize, shares: &[DecryptedShare]) -> Scalar { 225 | let mut v = Scalar::multiplicative_identity(); 226 | for j in 0..(t as usize) { 227 | if j != sid { 228 | let sj = shares[j].id.to_scalar(); 229 | let si = shares[sid].id.to_scalar(); 230 | let d = &sj - &si; 231 | v = v * sj * d.inverse(); 232 | } 233 | } 234 | v 235 | } 236 | 237 | // Try to recover a secret 238 | pub fn recover(t: Threshold, shares: &[DecryptedShare]) -> Result { 239 | if t as usize > shares.len() { 240 | return Err(()); 241 | }; 242 | let mut result = Point::infinity(); 243 | for i in 0..(t as usize) { 244 | let v = interpolate_one(t, i, shares); 245 | result = result + shares[i].decrypted_val.mul(&v); 246 | } 247 | Ok(result) 248 | } 249 | 250 | pub fn verify_secret(secret: Secret, public_shares: &PublicShares) -> bool { 251 | let mut commitment_interpolate = Point::infinity(); 252 | for i in 0..(public_shares.threshold as usize) { 253 | let x = &public_shares.commitments[i].point; 254 | let li = { 255 | let mut v = Scalar::multiplicative_identity(); 256 | for j in 0..(public_shares.threshold as usize) { 257 | if j != i { 258 | let sj = Scalar::from_u32((j + 1) as u32); 259 | let si = Scalar::from_u32((i + 1) as u32); 260 | let d = &sj - &si; 261 | v = v * sj * d.inverse(); 262 | } 263 | } 264 | v 265 | }; 266 | commitment_interpolate = commitment_interpolate + x.mul(&li); 267 | } 268 | let dleq = dleq::DLEQ { 269 | g1: &Point::generator(), 270 | h1: &secret, 271 | g2: &public_shares.extra_generator, 272 | h2: &commitment_interpolate, 273 | }; 274 | public_shares.secret_proof.verify(&dleq) 275 | } 276 | -------------------------------------------------------------------------------- /src/crypto/openssl.rs: -------------------------------------------------------------------------------- 1 | use openssl::bn::*; 2 | use openssl::ec::*; 3 | use std::ops::Add; 4 | use std::ops::Mul; 5 | use std::ops::Sub; 6 | 7 | // currently hardcode curve to P256R1, but in the future probably a good idea 8 | // to generalize the interface, and make it more generics (with generics for all crypto types) 9 | pub const CURVE: openssl::nid::Nid = openssl::nid::Nid::X9_62_PRIME256V1; 10 | 11 | pub struct Scalar { 12 | bn: BigNum, 13 | } 14 | 15 | pub struct Point { 16 | point: EcPoint, 17 | } 18 | 19 | #[derive(PartialEq)] 20 | pub struct PrivateKey { 21 | pub scalar: Scalar, 22 | } 23 | 24 | #[derive(PartialEq)] 25 | pub struct PublicKey { 26 | pub point: Point, 27 | } 28 | 29 | impl PublicKey { 30 | pub fn to_bytes(&self) -> Vec { 31 | self.point.to_bytes() 32 | } 33 | 34 | pub fn from_bytes(bytes: &[u8]) -> PublicKey { 35 | let mut ctx = BigNumContext::new().unwrap(); 36 | PublicKey { 37 | point: Point { 38 | point: EcPoint::from_bytes(&get_grp(), bytes, &mut ctx) 39 | .expect("Could not create PublicKey from bytes"), 40 | }, 41 | } 42 | } 43 | } 44 | 45 | impl PrivateKey { 46 | // to_hex_str?? https://docs.rs/openssl/0.9.14/openssl/bn/struct.BigNum.html#method.to_hex_str 47 | pub fn to_bytes(&self) -> Vec { 48 | self.scalar.bn.to_vec() 49 | } 50 | 51 | pub fn from_bytes(bytes: &[u8]) -> PrivateKey { 52 | PrivateKey { 53 | scalar: Scalar { 54 | bn: BigNum::from_slice(bytes).expect("Could not create PrivateKey from bytes"), 55 | }, 56 | } 57 | } 58 | } 59 | 60 | pub fn create_keypair() -> (PublicKey, PrivateKey) { 61 | let s = Scalar::generate(); 62 | let p = Point::from_scalar(&s); 63 | (PublicKey { point: p }, PrivateKey { scalar: s }) 64 | } 65 | 66 | fn get_grp() -> EcGroup { 67 | openssl::ec::EcGroup::from_curve_name(CURVE).unwrap() 68 | } 69 | 70 | fn get_order() -> BigNum { 71 | let mut ctx = BigNumContext::new().unwrap(); 72 | let grp = openssl::ec::EcGroup::from_curve_name(CURVE).unwrap(); 73 | let mut order = BigNum::new().unwrap(); 74 | grp.order(&mut order, &mut ctx).unwrap(); 75 | order 76 | } 77 | 78 | fn get_point_at_infinity() -> EcPoint { 79 | let mut ctx = BigNumContext::new().unwrap(); 80 | let grp = openssl::ec::EcGroup::from_curve_name(CURVE).unwrap(); 81 | let mut order = BigNum::new().unwrap(); 82 | grp.order(&mut order, &mut ctx).unwrap(); 83 | let mut p = EcPoint::new(&grp).unwrap(); 84 | p.mul_generator(&grp, &order, &ctx).unwrap(); 85 | p 86 | } 87 | 88 | fn curve_generator() -> EcPoint { 89 | let ctx = BigNumContext::new().unwrap(); 90 | let grp = openssl::ec::EcGroup::from_curve_name(CURVE).unwrap(); 91 | let pow = BigNum::from_u32(1).unwrap(); 92 | let mut p = EcPoint::new(&grp).unwrap(); 93 | p.mul_generator(&grp, &pow, &ctx).unwrap(); 94 | p 95 | } 96 | 97 | impl Scalar { 98 | pub fn from_u32(v: u32) -> Scalar { 99 | Scalar { 100 | bn: BigNum::from_u32(v).unwrap(), 101 | } 102 | } 103 | pub fn generate() -> Scalar { 104 | let order = get_order(); 105 | let mut r = BigNum::new().unwrap(); 106 | order.rand_range(&mut r).unwrap(); 107 | Scalar { bn: r } 108 | } 109 | 110 | pub fn multiplicative_identity() -> Scalar { 111 | Self::from_u32(1) 112 | } 113 | 114 | pub fn hash_points(points: Vec) -> Scalar { 115 | let mut data = Vec::new(); 116 | for p in points { 117 | data.extend_from_slice(p.to_bytes().as_slice()); 118 | } 119 | let dig = openssl::sha::sha256(data.as_slice()); 120 | let mut ctx = BigNumContext::new().unwrap(); 121 | let order = get_order(); 122 | let b = BigNum::from_slice(&dig).unwrap(); 123 | let mut r = BigNum::new().unwrap(); 124 | r.nnmod(&b, &order, &mut ctx).unwrap(); 125 | Scalar { bn: r } 126 | } 127 | 128 | pub fn pow(&self, pow: u32) -> Scalar { 129 | let mut ctx = BigNumContext::new().unwrap(); 130 | let order = get_order(); 131 | 132 | let mut r = BigNum::new().unwrap(); 133 | let bn_pow = BigNum::from_u32(pow).unwrap(); 134 | r.mod_exp(&self.bn, &bn_pow, &order, &mut ctx).unwrap(); 135 | Scalar { bn: r } 136 | } 137 | 138 | pub fn inverse(&self) -> Scalar { 139 | let mut ctx = BigNumContext::new().unwrap(); 140 | let mut r = BigNum::new().unwrap(); 141 | let order = get_order(); 142 | r.mod_inverse(&self.bn, &order, &mut ctx).unwrap(); 143 | Scalar { bn: r } 144 | } 145 | } 146 | 147 | impl Clone for Scalar { 148 | fn clone(&self) -> Scalar { 149 | Scalar { 150 | bn: BigNum::from_slice(&self.bn.to_vec()).unwrap(), 151 | } 152 | } 153 | } 154 | 155 | impl Add for Scalar { 156 | type Output = Self; 157 | fn add(self, s: Self) -> Self { 158 | let mut ctx = BigNumContext::new().unwrap(); 159 | let order = get_order(); 160 | 161 | let mut r = BigNum::new().unwrap(); 162 | r.mod_add(&self.bn, &s.bn, &order, &mut ctx).unwrap(); 163 | Scalar { bn: r } 164 | } 165 | } 166 | 167 | impl Sub for Scalar { 168 | type Output = Self; 169 | fn sub(self, s: Self) -> Self { 170 | let mut ctx = BigNumContext::new().unwrap(); 171 | let order = get_order(); 172 | 173 | let mut r = BigNum::new().unwrap(); 174 | r.mod_sub(&self.bn, &s.bn, &order, &mut ctx).unwrap(); 175 | Scalar { bn: r } 176 | } 177 | } 178 | 179 | impl Mul for Scalar { 180 | type Output = Self; 181 | fn mul(self, s: Self) -> Self { 182 | let mut ctx = BigNumContext::new().unwrap(); 183 | let order = get_order(); 184 | 185 | let mut r = BigNum::new().unwrap(); 186 | r.mod_mul(&self.bn, &s.bn, &order, &mut ctx).unwrap(); 187 | Scalar { bn: r } 188 | } 189 | } 190 | 191 | impl PartialEq for Scalar { 192 | fn eq(&self, other: &Self) -> bool { 193 | self.bn.to_vec() == other.bn.to_vec() 194 | } 195 | } 196 | 197 | impl Point { 198 | pub fn infinity() -> Point { 199 | Point { 200 | point: get_point_at_infinity(), 201 | } 202 | } 203 | 204 | pub fn generator() -> Point { 205 | Point { 206 | point: curve_generator(), 207 | } 208 | } 209 | 210 | pub fn from_scalar(s: &Scalar) -> Point { 211 | let ctx = BigNumContext::new().unwrap(); 212 | let grp = get_grp(); 213 | let mut p = EcPoint::new(&grp).unwrap(); 214 | p.mul_generator(&grp, &s.bn, &ctx).unwrap(); 215 | Point { point: p } 216 | } 217 | 218 | pub fn mul(&self, s: &Scalar) -> Point { 219 | let grp = get_grp(); 220 | let ctx = BigNumContext::new().unwrap(); 221 | let mut r = EcPoint::new(&grp).unwrap(); 222 | r.mul(&grp, &self.point, &s.bn, &ctx).unwrap(); 223 | Point { point: r } 224 | } 225 | 226 | pub fn inverse(&self) -> Point { 227 | let grp = get_grp(); 228 | let mut ctx = BigNumContext::new().unwrap(); 229 | let bytes = self 230 | .point 231 | .to_bytes(&grp, PointConversionForm::UNCOMPRESSED, &mut ctx) 232 | .unwrap(); 233 | let mut p = EcPoint::from_bytes(&grp, &bytes, &mut ctx).unwrap(); 234 | p.invert(&grp, &ctx).unwrap(); 235 | Point { point: p } 236 | } 237 | 238 | pub fn to_bytes(&self) -> Vec { 239 | let grp = get_grp(); 240 | let mut ctx = BigNumContext::new().unwrap(); 241 | self.point 242 | .to_bytes(&grp, PointConversionForm::COMPRESSED, &mut ctx) 243 | .unwrap() 244 | } 245 | } 246 | 247 | impl Clone for Point { 248 | fn clone(&self) -> Point { 249 | let mut ctx = BigNumContext::new().unwrap(); 250 | let grp = get_grp(); 251 | let bytes = self 252 | .point 253 | .to_bytes(&grp, PointConversionForm::UNCOMPRESSED, &mut ctx) 254 | .unwrap(); 255 | Point { 256 | point: EcPoint::from_bytes(&grp, &bytes, &mut ctx).unwrap(), 257 | } 258 | } 259 | } 260 | 261 | impl Add for Point { 262 | type Output = Self; 263 | fn add(self, p: Self) -> Self { 264 | let grp = get_grp(); 265 | let mut ctx = BigNumContext::new().unwrap(); 266 | let mut r = EcPoint::new(&grp).unwrap(); 267 | r.add(&grp, &self.point, &p.point, &mut ctx).unwrap(); 268 | Point { point: r } 269 | } 270 | } 271 | impl Sub for Point { 272 | type Output = Point; 273 | fn sub(self, p: Self) -> Self { 274 | let grp = get_grp(); 275 | let mut ctx = BigNumContext::new().unwrap(); 276 | let p_inv = p.inverse(); 277 | let mut r = EcPoint::new(&grp).unwrap(); 278 | r.add(&grp, &self.point, &p_inv.point, &mut ctx).unwrap(); 279 | Point { point: r } 280 | } 281 | } 282 | 283 | impl PartialEq for Point { 284 | fn eq(&self, other: &Self) -> bool { 285 | let mut ctx = BigNumContext::new().unwrap(); 286 | let grp = get_grp(); 287 | let b1 = self 288 | .point 289 | .to_bytes(&grp, PointConversionForm::UNCOMPRESSED, &mut ctx) 290 | .unwrap(); 291 | let b2 = other 292 | .point 293 | .to_bytes(&grp, PointConversionForm::UNCOMPRESSED, &mut ctx) 294 | .unwrap(); 295 | b1 == b2 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/crypto/eccoxide.rs: -------------------------------------------------------------------------------- 1 | use cryptoxide::drg::chacha; 2 | use cryptoxide::hashing::sha2::Sha256; 3 | use eccoxide::curve::sec2::p256r1 as curve; 4 | use std::convert::TryFrom; 5 | use std::ops::Add; 6 | use std::ops::Mul; 7 | use std::ops::Sub; 8 | 9 | pub struct Drg(chacha::Drg<8>); 10 | 11 | impl Drg { 12 | pub fn new() -> Self { 13 | loop { 14 | let mut out = [0u8; 32]; 15 | if let Err(_) = getrandom::getrandom(&mut out) { 16 | continue; 17 | } 18 | let drg = chacha::Drg::new(&out); 19 | return Drg(drg); 20 | } 21 | } 22 | } 23 | 24 | #[derive(Clone)] 25 | pub struct Scalar { 26 | bn: curve::Scalar, 27 | } 28 | 29 | #[derive(Clone)] 30 | pub struct Point { 31 | point: curve::Point, 32 | } 33 | 34 | #[derive(PartialEq)] 35 | pub struct PrivateKey { 36 | pub scalar: Scalar, 37 | } 38 | 39 | #[derive(PartialEq, Clone)] 40 | pub struct PublicKey { 41 | pub point: Point, 42 | } 43 | 44 | impl PublicKey { 45 | pub fn to_bytes(&self) -> Vec { 46 | self.point.to_bytes() 47 | } 48 | 49 | pub fn from_bytes(bytes: &[u8]) -> PublicKey { 50 | PublicKey { 51 | point: Point::from_bytes(bytes).unwrap(), 52 | } 53 | } 54 | } 55 | 56 | impl PrivateKey { 57 | pub fn to_bytes(&self) -> Vec { 58 | self.scalar.bn.to_bytes().to_vec() 59 | } 60 | 61 | pub fn from_bytes(bytes: &[u8]) -> PrivateKey { 62 | PrivateKey { 63 | scalar: Scalar::from_slice(bytes).unwrap(), 64 | } 65 | } 66 | } 67 | 68 | pub fn create_keypair(drg: &mut Drg) -> (PublicKey, PrivateKey) { 69 | let s = Scalar::generate(drg); 70 | let p = Point::from_scalar(&s); 71 | (PublicKey { point: p }, PrivateKey { scalar: s }) 72 | } 73 | 74 | pub struct PointHasher { 75 | context: cryptoxide::hashing::sha2::Context256, 76 | } 77 | 78 | impl PointHasher { 79 | pub fn new() -> Self { 80 | PointHasher { 81 | context: cryptoxide::hashing::sha2::Context256::new(), 82 | } 83 | } 84 | 85 | pub fn update_mut(&mut self, p: &Point) { 86 | self.context.update_mut(&p.to_bytes()); 87 | } 88 | 89 | pub fn update(self, p: &Point) -> Self { 90 | Self { 91 | context: self.context.update(&p.to_bytes()), 92 | } 93 | } 94 | 95 | pub fn update_iter<'a, I: Iterator>(self, it: I) -> Self { 96 | let mut context = self.context; 97 | for i in it { 98 | context = context.update(&i.to_bytes()) 99 | } 100 | Self { context } 101 | } 102 | 103 | pub fn finalize(self) -> Scalar { 104 | let dig = self.context.finalize(); 105 | // TODO need to modularise dig ! 106 | Scalar { 107 | bn: curve::Scalar::from_bytes(&dig).unwrap(), 108 | } 109 | } 110 | } 111 | 112 | impl Scalar { 113 | pub fn from_u32(v: u32) -> Scalar { 114 | Scalar { 115 | bn: curve::Scalar::from_u64(v as u64), 116 | } 117 | } 118 | 119 | pub fn generate(drg: &mut Drg) -> Scalar { 120 | loop { 121 | let out = drg.0.bytes(); 122 | if let Some(scalar) = Scalar::from_bytes(&out) { 123 | return scalar; 124 | } 125 | } 126 | } 127 | 128 | pub fn multiplicative_identity() -> Scalar { 129 | Self::from_u32(1) 130 | } 131 | 132 | pub fn hash_points(points: Vec<&Point>) -> Scalar { 133 | let mut context = cryptoxide::hashing::sha2::Context256::new(); 134 | 135 | for p in points { 136 | context.update_mut(&p.to_bytes()); 137 | } 138 | let dig = context.finalize(); 139 | // TODO need to modularise dig ! 140 | Scalar { 141 | bn: curve::Scalar::from_bytes(&dig).unwrap(), 142 | } 143 | } 144 | 145 | pub fn pow(&self, pow: u32) -> Scalar { 146 | Scalar { 147 | bn: self.bn.power_u64(pow as u64), 148 | } 149 | } 150 | 151 | pub fn inverse(&self) -> Scalar { 152 | Scalar { 153 | bn: self.bn.inverse(), 154 | } 155 | } 156 | 157 | pub fn from_bytes(bytes: &[u8; 32]) -> Option { 158 | curve::Scalar::from_bytes(bytes).map(|s| Scalar { bn: s }) 159 | } 160 | 161 | pub fn from_slice(slice: &[u8]) -> Option { 162 | let bytes = <&[u8; 32]>::try_from(slice).ok()?; 163 | curve::Scalar::from_bytes(bytes).map(|s| Scalar { bn: s }) 164 | } 165 | } 166 | 167 | impl Add for Scalar { 168 | type Output = Self; 169 | fn add(self, s: Self) -> Self { 170 | Scalar { bn: self.bn + s.bn } 171 | } 172 | } 173 | 174 | impl<'a> Add for &'a Scalar { 175 | type Output = Scalar; 176 | fn add(self, s: Self) -> Self::Output { 177 | Scalar { 178 | bn: &self.bn + &s.bn, 179 | } 180 | } 181 | } 182 | 183 | impl Sub for Scalar { 184 | type Output = Self; 185 | fn sub(self, s: Self) -> Self { 186 | Scalar { bn: self.bn - s.bn } 187 | } 188 | } 189 | 190 | impl<'a> Sub for &'a Scalar { 191 | type Output = Scalar; 192 | fn sub(self, s: Self) -> Self::Output { 193 | Scalar { 194 | bn: &self.bn - &s.bn, 195 | } 196 | } 197 | } 198 | 199 | impl Mul for Scalar { 200 | type Output = Self; 201 | fn mul(self, s: Self) -> Self { 202 | Scalar { bn: self.bn * s.bn } 203 | } 204 | } 205 | 206 | impl<'a> Mul for &'a Scalar { 207 | type Output = Scalar; 208 | fn mul(self, s: Self) -> Self::Output { 209 | Scalar { 210 | bn: &self.bn * &s.bn, 211 | } 212 | } 213 | } 214 | 215 | impl PartialEq for Scalar { 216 | fn eq(&self, other: &Self) -> bool { 217 | self.bn.eq(&other.bn) 218 | } 219 | } 220 | 221 | impl Point { 222 | pub fn infinity() -> Point { 223 | Point { 224 | point: curve::Point::infinity(), 225 | } 226 | } 227 | 228 | pub fn generator() -> Point { 229 | Point { 230 | point: curve::Point::generator(), 231 | } 232 | } 233 | 234 | pub fn try_hash_to_curve(slice: &[u8]) -> Option { 235 | let out = Sha256::new().update(slice).finalize(); 236 | if let Some(x) = curve::FieldElement::from_bytes(&out) { 237 | if let Some(p) = curve::PointAffine::decompress(&x, eccoxide::curve::Sign::Positive) { 238 | return Some(Self { 239 | point: curve::Point::from_affine(&p), 240 | }); 241 | } 242 | } 243 | None 244 | } 245 | 246 | pub fn hash_to_curve(slice: &[u8]) -> Point { 247 | let mut counter_slice = [0u8]; 248 | loop { 249 | let out = Sha256::new() 250 | .update(slice) 251 | .update(&counter_slice) 252 | .finalize(); 253 | if let Some(x) = curve::FieldElement::from_bytes(&out) { 254 | if let Some(p) = curve::PointAffine::decompress(&x, eccoxide::curve::Sign::Positive) 255 | { 256 | return Self { 257 | point: curve::Point::from_affine(&p), 258 | }; 259 | } 260 | } 261 | 262 | counter_slice[0] = counter_slice[0] + 1; 263 | } 264 | } 265 | 266 | pub fn random_generator(drg: &mut Drg) -> Point { 267 | loop { 268 | let out = drg.0.bytes::<32>(); 269 | if let Some(point) = Self::try_hash_to_curve(&out) { 270 | return point; 271 | } 272 | } 273 | } 274 | 275 | pub fn from_scalar(s: &Scalar) -> Point { 276 | let g = curve::Point::generator(); 277 | Point { point: &g * &s.bn } 278 | } 279 | 280 | pub fn mul(&self, s: &Scalar) -> Point { 281 | Point { 282 | point: &self.point * &s.bn, 283 | } 284 | } 285 | 286 | pub fn inverse(&self) -> Point { 287 | Point { 288 | point: -self.point.clone(), 289 | } 290 | } 291 | 292 | pub fn from_bytes(slice: &[u8]) -> Option { 293 | if slice.len() != 33 { 294 | return None; 295 | } 296 | let sign = match slice[0] { 297 | 0x2 => eccoxide::curve::Sign::Positive, 298 | 0x3 => eccoxide::curve::Sign::Negative, 299 | _ => return None, 300 | }; 301 | let bytes = <&[u8; 32]>::try_from(&slice[1..]).unwrap(); 302 | let fe = curve::FieldElement::from_bytes(bytes)?; 303 | 304 | let pa = curve::PointAffine::decompress(&fe, sign)?; 305 | Some(Point { 306 | point: curve::Point::from_affine(&pa), 307 | }) 308 | } 309 | 310 | pub fn to_bytes(&self) -> Vec { 311 | let p = self.point.to_affine().unwrap(); 312 | let (fe, sign) = p.compress(); 313 | let lead = match sign { 314 | eccoxide::curve::Sign::Positive => 0x2, 315 | eccoxide::curve::Sign::Negative => 0x3, 316 | }; 317 | let mut out = vec![lead]; 318 | out.extend_from_slice(&fe.to_bytes()); 319 | out 320 | } 321 | } 322 | 323 | impl Add for Point { 324 | type Output = Self; 325 | fn add(self, p: Self) -> Self { 326 | Point { 327 | point: self.point + p.point, 328 | } 329 | } 330 | } 331 | 332 | impl Sub for Point { 333 | type Output = Self; 334 | fn sub(self, p: Self) -> Self { 335 | Point { 336 | point: self.point - p.point, 337 | } 338 | } 339 | } 340 | 341 | impl PartialEq for Point { 342 | fn eq(&self, other: &Self) -> bool { 343 | self.point.eq(&other.point) 344 | } 345 | } 346 | --------------------------------------------------------------------------------