├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#* 3 | .\#* 4 | *.swp 5 | *.orig 6 | *.bak 7 | 8 | *.s 9 | target/ 10 | **/*.rs.bk 11 | Cargo.lock 12 | .cargo/* 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | env: 9 | - TEST_COMMAND=test FEATURES='' 10 | - TEST_COMMAND=bench FEATURES='bench' 11 | - TEST_COMMAND=build FEATURES='no-std' 12 | 13 | matrix: 14 | exclude: 15 | - rust: stable 16 | env: TEST_COMMAND=bench FEATURES='bench' 17 | - rust: beta 18 | env: TEST_COMMAND=bench FEATURES='bench' 19 | 20 | script: 21 | - cargo $TEST_COMMAND --features="$FEATURES" 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to dalek-rangeproofs 2 | 3 | If you have questions, feature requests, or comments, please feel free to join 4 | [our Slack channel](XXX). 5 | 6 | Patches are welcomed as pull requests on 7 | [our Github](https://github.com/isislovecruft/dalek-rangeproofs), as well as by 8 | email (preferably sent to all of the authors listed in `Cargo.toml`). 9 | 10 | All issues on dalek-rangeproofs are mentored, if you want help with a bug just ask 11 | @isislovecruft or @hdevalence. 12 | 13 | Some issues are easier than others. The `easy` label can be used to find the 14 | easy issues. If you want to work on an issue, please leave a comment so that we 15 | can assign it to you! 16 | 17 | # Code of Conduct 18 | 19 | We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html), 20 | with the following additional clauses: 21 | 22 | * We respect the rights to privacy and anonymity for contributors and people in 23 | the community. If someone wishes to contribute under a pseudonym different to 24 | their primary identity, that wish is to be respected by all contributors. 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dalek-rangeproofs" 3 | version = "0.1.0" 4 | authors = ["Henry de Valence ", 5 | "Isis Agora Lovecruft "] 6 | readme = "README.md" 7 | license = "CC0-1.0" 8 | repository = "https://github.com/isislovecruft/dalek-rangeproofs" 9 | documentation = "https://docs.rs/dalek-rangeproofs" 10 | categories = ["cryptography"] 11 | keywords = ["cryptography", "curve25519", "zero-knowledge", "NIZK", "rangeproof"] 12 | description = "A pure-Rust implementation of a non-interactive zero-knowledge rangeproof scheme." 13 | exclude = [ 14 | ".gitignore" 15 | ] 16 | 17 | [dependencies] 18 | sha2 = "^0.4" 19 | 20 | [dependencies.rand] 21 | version = "^0.3" 22 | 23 | [dependencies.serde] 24 | version = "1.0" 25 | 26 | [dependencies.serde_derive] 27 | version = "1.0" 28 | 29 | [dev-dependencies.serde_cbor] 30 | version = "0.6" 31 | 32 | [dependencies.curve25519-dalek] 33 | features = ["yolocrypto", "serde", "nightly"] 34 | version = "^0.8" 35 | 36 | [features] 37 | bench = [] 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dalek-rangeproofs 2 | 3 | This repo **was**: 4 | 5 | A pure-Rust implementation of the Back-Maxwell rangeproof scheme defined in 6 | ["Confidential Assets" (2017) by Poelstra, Back, Friedenbach, Maxwell, Wuille](https://blockstream.com/bitcoin17-final41.pdf). 7 | 8 | **THIS IS A PROOF-OF-CONCEPT OF A SCHEME WHICH IS NOW OBSOLETE, USE BULLETPROOFS** 9 | 10 | ## Warning 11 | 12 | This code has **not** yet received sufficient peer review by other qualified 13 | cryptographers to be considered in any way, shape, or form, safe. 14 | 15 | **USE AT YOUR OWN RISK** 16 | 17 | ## Documentation 18 | 19 | Extensive documentation is available [here](https://docs.rs/dalek-rangeproofs). 20 | 21 | # Installation 22 | 23 | To install, add the following to the dependencies section of your project's 24 | `Cargo.toml`: 25 | 26 | dalek-rangeproofs = "^0.1" 27 | 28 | Then, in your library or executable source, add: 29 | 30 | extern crate dalek_rangeproofs 31 | 32 | # Tests and benchmarks 33 | 34 | Tests may be run with: 35 | 36 | cargo test 37 | 38 | Benchmarks may be taken with: 39 | 40 | cargo bench --features 'bench' 41 | 42 | 43 | # Pre-Release TODOs 44 | 45 | * move RangeProof code to `back_maxwell.rs` module 46 | * double check documentation 47 | * double check code 48 | * don't use any yolocrypto features (i.e. stabilise decaf in curve25519-dalek) 49 | * make a CONTRIBUTING.md 50 | 51 | # Future TODOs 52 | 53 | * support other rangeproof schemes? 54 | * make hash function choice configurable? 55 | * make the code generic w.r.t. to a future Group trait 56 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8; mode: rust; -*- 2 | // 3 | // To the extent possible under law, the authors have waived all 4 | // copyright and related or neighboring rights to dalek-rangeproofs, 5 | // using the Creative Commons "CC0" public domain dedication. See 6 | // for full 7 | // details. 8 | // 9 | // Authors: 10 | // - Isis Agora Lovecruft 11 | // - Henry de Valence 12 | 13 | //! A pure-Rust implementation of the Back-Maxwell rangeproof scheme defined in 14 | //! [_Confidential Assets_ (2017) by Poelstra, Back, Friedenbach, Maxwell, 15 | //! Wuille](https://blockstream.com/bitcoin17-final41.pdf). 16 | //! 17 | //! The scheme is instantiated using the Decaf group on Curve25519, as 18 | //! implemented in 19 | //! [`curve25519-dalek`](https://github.com/isislovecruft/curve25519-dalek). 20 | //! Note that since the Decaf implementation in `curve25519-dalek` is 21 | //! currently **UNFINISHED, UNREVIEWED, AND EXPERIMENTAL**, so is this 22 | //! library. 23 | //! 24 | //! This implementation hardcodes the ring size `m = 3`, as this is 25 | //! the most efficient choice. The number of rings `n` determines the 26 | //! range `[0,3^n]`, as well as the size and speed of the proof. 27 | //! 28 | //! # Examples 29 | //! 30 | //! Suppose we want to prove that `134492616741` is within `[0,3^40]`. 31 | //! Since 3^40 ≅ 0.66 (2^64 ), this is just slightly narrower than the 32 | //! range of a `u64`. 33 | //! 34 | //! To construct a proof that `134492616741` is within `[0,3^40]`, 35 | //! first choose orthogonal basepoints. Usually the basepoints would 36 | //! be set or distributed as system-wide parameters. Here we choose 37 | //! `G` to be the Decaf coset containing the ed25519 basepoint and 38 | //! generate the second basepoint as `H = Hash(G)`. (Technically, `G` 39 | //! is actually a `DecafBasepointTable`, which implements 40 | //! `Mul<&Scalar>` using a precomputed table). 41 | //! 42 | //! ``` 43 | //! # extern crate curve25519_dalek; 44 | //! # extern crate sha2; 45 | //! # fn main() { 46 | //! use sha2::Sha256; 47 | //! 48 | //! use curve25519_dalek::constants as dalek_constants; 49 | //! use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint}; 50 | //! use curve25519_dalek::scalar::Scalar; 51 | //! 52 | //! let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 53 | //! let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 54 | //! # } 55 | //! 56 | //! ``` 57 | //! 58 | //! You'll also need your value and a CSPRNG: 59 | //! 60 | //! ``` 61 | //! # extern crate rand; 62 | //! # fn main() { 63 | //! use rand::OsRng; 64 | //! 65 | //! let mut csprng = OsRng::new().unwrap(); 66 | //! let value = 134492616741; 67 | //! # } 68 | //! ``` 69 | //! 70 | //! We can now create the rangeproof, like so: 71 | //! 72 | //! ``` 73 | //! # extern crate dalek_rangeproofs; 74 | //! # extern crate curve25519_dalek; 75 | //! # extern crate sha2; 76 | //! # extern crate rand; 77 | //! # fn main() { 78 | //! # use curve25519_dalek::constants as dalek_constants; 79 | //! # use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint}; 80 | //! # use curve25519_dalek::scalar::Scalar; 81 | //! # use rand::OsRng; 82 | //! # use sha2::Sha256; 83 | //! # 84 | //! # let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 85 | //! # let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 86 | //! # 87 | //! # let mut csprng = OsRng::new().unwrap(); 88 | //! # let value = 134492616741; 89 | //! use dalek_rangeproofs::RangeProof; 90 | //! 91 | //! let (proof, commitment, blinding) = 92 | //! RangeProof::create(40, value, G, &H, &mut csprng).unwrap(); 93 | //! # } 94 | //! ``` 95 | //! 96 | //! The output is the proof `proof`, as well as `commitment = 97 | //! blinding*G + value*H`. 98 | //! 99 | //! We can serialize the proof using [Serde](https://serde.rs). Here, we use [CBOR](http://cbor.io). 100 | //! 101 | //! ``` 102 | //! # extern crate dalek_rangeproofs; 103 | //! # extern crate curve25519_dalek; 104 | //! # extern crate rand; 105 | //! # extern crate sha2; 106 | //! extern crate serde_cbor; 107 | //! # fn main() { 108 | //! # use curve25519_dalek::constants as dalek_constants; 109 | //! # use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint}; 110 | //! # use curve25519_dalek::scalar::Scalar; 111 | //! # use rand::OsRng; 112 | //! # use sha2::Sha256; 113 | //! # 114 | //! # let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 115 | //! # let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 116 | //! # let mut csprng = OsRng::new().unwrap(); 117 | //! # let value = 134492616741; 118 | //! # use dalek_rangeproofs::RangeProof; 119 | //! # let (proof, commitment, blinding) 120 | //! # = RangeProof::create(40, value, G, &H, &mut csprng).unwrap(); 121 | //! 122 | //! let proof_bytes: Vec = serde_cbor::ser::to_vec_packed(&proof).unwrap(); 123 | //! assert_eq!(proof_bytes.len(), 4125); 124 | //! # } 125 | //! ``` 126 | //! 127 | //! In this case, the `serde_cbor`-encoded proof statement is 128 | //! approximately 6.5% larger than the optimal 3872 = 32(1+3*40) 129 | //! bytes. Another party can verify the proof statement from 130 | //! `proof_bytes` by doing: 131 | //! 132 | //! ``` 133 | //! # extern crate dalek_rangeproofs; 134 | //! # extern crate curve25519_dalek; 135 | //! # extern crate rand; 136 | //! # extern crate sha2; 137 | //! extern crate serde_cbor; 138 | //! # fn main() { 139 | //! # use curve25519_dalek::constants as dalek_constants; 140 | //! # use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint}; 141 | //! # use curve25519_dalek::scalar::Scalar; 142 | //! # use rand::OsRng; 143 | //! # use sha2::Sha256; 144 | //! # 145 | //! # let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 146 | //! # let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 147 | //! # let mut csprng = OsRng::new().unwrap(); 148 | //! # let value = 134492616741; 149 | //! use dalek_rangeproofs::RangeProof; 150 | //! # let (proof, commitment, blinding) 151 | //! # = RangeProof::create(40, value, G, &H, &mut csprng).unwrap(); 152 | //! # let proof_bytes: Vec = serde_cbor::to_vec(&proof).unwrap(); 153 | //! let proof: RangeProof = serde_cbor::from_slice(&proof_bytes).unwrap(); 154 | //! 155 | //! let C_option = proof.verify(40, G, &H); 156 | //! assert!(C_option.is_some()); 157 | //! 158 | //! let C = C_option.unwrap(); 159 | //! # } 160 | //! ``` 161 | //! 162 | //! If the proof is well-formed, `verify` returns the commitment to 163 | //! the value. Since the commitment is the output of the 164 | //! verification, the verifier is assured it opens to a value in the 165 | //! range `[0,3^n]`. However, without knowing both `blinding` and the 166 | //! actual `value`, the verifier cannot open this commitment, because 167 | //! Pedersen commitments are computationally binding and perfectly 168 | //! hiding (in addition to being additively homomorphic, a feature 169 | //! used within this scheme). 170 | //! 171 | //! Later, to open this commitment, the prover could reveal the value 172 | //! and the blinding, allowing others to check that `commitment = 173 | //! blinding*G + value*H`. 174 | //! 175 | //! ``` 176 | //! # extern crate dalek_rangeproofs; 177 | //! # extern crate curve25519_dalek; 178 | //! # extern crate rand; 179 | //! # extern crate sha2; 180 | //! # fn main() { 181 | //! # use curve25519_dalek::constants as dalek_constants; 182 | //! # use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint}; 183 | //! # use curve25519_dalek::scalar::Scalar; 184 | //! # use rand::OsRng; 185 | //! # use sha2::Sha256; 186 | //! # 187 | //! # let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 188 | //! # let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 189 | //! # let mut csprng = OsRng::new().unwrap(); 190 | //! # let value = 134492616741; 191 | //! # use dalek_rangeproofs::RangeProof; 192 | //! # let (proof, commitment, blinding) 193 | //! # = RangeProof::create(40, value, G, &H, &mut csprng).unwrap(); 194 | //! # let C = proof.verify(40, G, &H).unwrap(); 195 | //! let C_hat = &(G * &blinding) + &(&H * &Scalar::from_u64(value)); 196 | //! 197 | //! assert_eq!(C_hat, C); 198 | //! # } 199 | //! ``` 200 | 201 | #![cfg_attr(feature = "bench", feature(test))] 202 | 203 | #![allow(non_snake_case)] 204 | #![deny(missing_docs)] 205 | 206 | #[cfg(all(test, feature = "bench"))] 207 | extern crate test; 208 | 209 | extern crate curve25519_dalek; 210 | extern crate sha2; 211 | 212 | extern crate rand; 213 | 214 | #[macro_use] 215 | extern crate serde_derive; 216 | 217 | use rand::Rng; 218 | 219 | use sha2::Sha512; 220 | use sha2::Digest; 221 | 222 | // XXX rewrite curve25519_dalek to have nicer imports. 223 | use curve25519_dalek::scalar::Scalar; 224 | use curve25519_dalek::curve::{Identity}; 225 | use curve25519_dalek::decaf::{DecafPoint, DecafBasepointTable}; 226 | use curve25519_dalek::decaf::vartime; 227 | use curve25519_dalek::subtle::CTAssignable; 228 | use curve25519_dalek::subtle::bytes_equal_ct; 229 | use curve25519_dalek::subtle::byte_is_nonzero; 230 | 231 | /// A Back-Maxwell rangeproof, which proves in zero knowledge that a 232 | /// number is in a range `[0,m^n]`. We hardcode `m = 3` as this is 233 | /// the most efficient. 234 | /// 235 | /// The size of the proof and the cost of verification are 236 | /// proportional to `n`. 237 | #[derive(Serialize, Deserialize)] 238 | pub struct RangeProof { 239 | e_0: Scalar, 240 | C: Vec, 241 | s_1: Vec, 242 | s_2: Vec, 243 | } 244 | 245 | /// The maximum allowed bound for the rangeproof. Currently this is 246 | /// set to 41, because we only implement conversion to base 3 digits 247 | /// for `u64`s, and 3^41 is the least power of 3 greater than `2^64`. 248 | pub const RANGEPROOF_MAX_N: usize = 41; 249 | 250 | impl RangeProof { 251 | /// Verify the rangeproof, returning a Pedersen commitment to the 252 | /// in-range value if successful. 253 | pub fn verify( 254 | &self, 255 | n: usize, 256 | G: &DecafBasepointTable, 257 | H: &DecafPoint, 258 | ) -> Option { 259 | // Calling verify with n out of bounds is a programming error. 260 | if n > RANGEPROOF_MAX_N { 261 | panic!("Error: called create_vartime with too large bound 3^n, n = {}", n); 262 | } 263 | 264 | // If the lengths of any of the arrays don't match, the proof 265 | // is malformed. 266 | if n != self.C.len() { 267 | return None; 268 | } else if n != self.s_1.len() { 269 | return None; 270 | } else if n != self.s_2.len() { 271 | return None; 272 | } 273 | 274 | let mut e_0_hash = Sha512::default(); 275 | let mut C = DecafPoint::identity(); 276 | // mi_H = m^i * H = 3^i * H in the loop below 277 | let mut mi_H = *H; 278 | 279 | for i in 0..n { 280 | let mi2_H = &mi_H + &mi_H; 281 | 282 | let Ci_minus_miH = &self.C[i] - &mi_H; 283 | let P = vartime::k_fold_scalar_mult(&[self.s_1[i], -&self.e_0], 284 | &[G.basepoint(), Ci_minus_miH]); 285 | let ei_1 = Scalar::hash_from_bytes::(P.compress().as_bytes()); 286 | 287 | let Ci_minus_2miH = &self.C[i] - &mi2_H; 288 | let P = vartime::k_fold_scalar_mult(&[self.s_2[i], -&ei_1], 289 | &[G.basepoint(), Ci_minus_2miH]); 290 | let ei_2 = Scalar::hash_from_bytes::(P.compress().as_bytes()); 291 | 292 | let Ri = &self.C[i] * &ei_2; 293 | e_0_hash.input(Ri.compress().as_bytes()); 294 | C = &C + &self.C[i]; 295 | 296 | // Set mi_H <-- 3*m_iH, so that mi_H is always 3^i * H in the loop 297 | mi_H = &mi_H + &mi2_H; 298 | } 299 | 300 | let e_0_hat = Scalar::from_hash(e_0_hash); 301 | 302 | if e_0_hat == self.e_0 { 303 | return Some(C); 304 | } else { 305 | return None; 306 | } 307 | } 308 | 309 | /// Construct a rangeproof for `value`, in variable time. 310 | /// 311 | /// # Inputs 312 | /// 313 | /// * `n`, so that the range is `[0,3^n]` with `n < RANGEPROOF_MAX_N`; 314 | /// * The `value` to prove within range `[0,3^n]`; 315 | /// * `csprng`, an implementation of `rand::Rng`, which should be 316 | /// cryptographically secure. 317 | /// 318 | /// # Returns 319 | /// 320 | /// If `value` is not in the range `[0,3^n]`, return None. 321 | /// 322 | /// Otherwise, returns `Some((proof, commitment, blinding))`, where: 323 | /// `proof` is the rangeproof, and `commitment = blinding*G + value*H`. 324 | /// 325 | /// Only the `RangeProof` should be sent to the verifier. The 326 | /// commitment and blinding are for the use of the prover. 327 | pub fn create_vartime( 328 | n: usize, 329 | value: u64, 330 | G: &DecafBasepointTable, 331 | H: &DecafPoint, 332 | mut csprng: &mut T, 333 | ) -> Option<(RangeProof, DecafPoint, Scalar)> { 334 | // Calling verify with n out of bounds is a programming error. 335 | if n > RANGEPROOF_MAX_N { 336 | panic!("Error: called create_vartime with too large bound 3^n, n = {}", n); 337 | } 338 | 339 | // Check that value is in range: all digits above n should be 0 340 | let v = base3_digits(value); 341 | for i in n..41 { 342 | if v[i] != 0 { return None; } 343 | } 344 | 345 | let mut R = vec![DecafPoint::identity(); n]; 346 | let mut C = vec![DecafPoint::identity(); n]; 347 | let mut k = vec![Scalar::zero(); n]; 348 | let mut r = vec![Scalar::zero(); n]; 349 | let mut s_1 = vec![Scalar::zero(); n]; 350 | let mut s_2 = vec![Scalar::zero(); n]; 351 | let mut e_1 = vec![Scalar::zero(); n]; 352 | let mut e_2 = vec![Scalar::zero(); n]; 353 | 354 | let mut mi_H = *H; 355 | for i in 0..n { 356 | let mi2_H = &mi_H + &mi_H; 357 | k[i] = Scalar::random(&mut csprng); 358 | 359 | if v[i] == 0 { 360 | R[i] = G * &k[i]; 361 | } else if v[i] == 1 { 362 | // Commitment to i-th digit is r^i G + 1 * m^i H 363 | r[i] = Scalar::random(&mut csprng); 364 | C[i] = &(G * &r[i]) + &mi_H; 365 | // Begin at index 1 in the ring, choosing random e_1 366 | let P = G * &k[i]; 367 | e_1[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 368 | // Choose random scalar for s_2 369 | s_2[i] = Scalar::random(&mut csprng); 370 | // Compute e_2 = Hash(s_2^i G - e_1^i (C^i - 2m^i H) ) 371 | let Ci_minus_mi2H = &C[i] - &mi2_H; 372 | let P = vartime::k_fold_scalar_mult(&[s_2[i], -&e_1[i]], 373 | &[G.basepoint(), Ci_minus_mi2H]); 374 | e_2[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 375 | 376 | R[i] = &C[i] * &e_2[i]; 377 | } else if v[i] == 2 { 378 | // Commitment to i-th digit is r^i G + 2 * m^i H 379 | r[i] = Scalar::random(&mut csprng); 380 | C[i] = &(G * &r[i]) + &mi2_H; 381 | // Begin at index 2 in the ring, choosing random e_2 382 | let P = G * &k[i]; 383 | e_2[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 384 | 385 | R[i] = &C[i] * &e_2[i]; 386 | } else { 387 | panic!("Invalid digit {}", v[i]); 388 | } 389 | 390 | // Set mi_H <- 3 * mi_H so that mi_H = m^i H in the loop 391 | mi_H = &mi2_H + &mi_H; 392 | } 393 | 394 | // Compute e_0 = Hash( R^0 || ... || R^{n-1} ) 395 | let mut e_0_hash = Sha512::default(); 396 | for i in 0..n { 397 | e_0_hash.input(R[i].compress().as_bytes()); 398 | } 399 | let e_0 = Scalar::from_hash(e_0_hash); 400 | 401 | let mut mi_H = *H; 402 | for i in 0..n { 403 | let mi2_H = &mi_H + &mi_H; 404 | if v[i] == 0 { 405 | let k_1 = Scalar::random(&mut csprng); 406 | let P = vartime::k_fold_scalar_mult(&[k_1, e_0], &[G.basepoint(), mi_H]); 407 | e_1[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 408 | 409 | let k_2 = Scalar::random(&mut csprng); 410 | let P = vartime::k_fold_scalar_mult(&[k_2, e_1[i]], &[G.basepoint(), mi2_H]); 411 | e_2[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 412 | 413 | let e_2_inv = e_2[i].invert(); 414 | r[i] = &e_2_inv * &k[i]; 415 | C[i] = G * &r[i]; 416 | 417 | s_1[i] = &k_1 + &(&e_0 * &(&k[i] * &e_2_inv)); 418 | s_2[i] = &k_2 + &(&e_1[i] * &(&k[i] * &e_2_inv)); 419 | } else if v[i] == 1 { 420 | s_1[i] = Scalar::multiply_add(&e_0, &r[i], &k[i]); 421 | } else if v[i] == 2 { 422 | s_1[i] = Scalar::random(&mut csprng); 423 | // Compute e_1^i = Hash(s_1^i G - e_0^i (C^i - 1 m^i H) ) 424 | let Ci_minus_miH = &C[i] - &mi_H; 425 | let P = vartime::k_fold_scalar_mult(&[s_1[i], -&e_0], 426 | &[G.basepoint(), Ci_minus_miH]); 427 | e_1[i] = Scalar::hash_from_bytes::(P.compress().as_bytes()); 428 | s_2[i] = Scalar::multiply_add(&e_1[i], &r[i], &k[i]); 429 | } 430 | // Set mi_H <-- 3*m_iH, so that mi_H is always 3^i * H in the loop 431 | mi_H = &mi_H + &mi2_H; 432 | } 433 | 434 | let mut blinding = Scalar::zero(); 435 | let mut commitment = DecafPoint::identity(); 436 | for i in 0..n { 437 | blinding += &r[i]; 438 | // XXX implement AddAssign for ExtendedPoint 439 | commitment = &commitment + &C[i]; 440 | } 441 | 442 | Some(( 443 | RangeProof{e_0: e_0, C: C, s_1: s_1, s_2: s_2}, 444 | commitment, 445 | blinding, 446 | )) 447 | } 448 | 449 | /// Construct a rangeproof for `value`, in constant time. 450 | /// 451 | /// This function is roughly three times slower (since `m = 3`) than the 452 | /// variable time version, for all values of `n`. 453 | /// 454 | /// # Inputs 455 | /// 456 | /// * `n`, so that the range is `[0,3^n]` with `n < RANGEPROOF_MAX_N`; 457 | /// * The `value` to prove within range `[0,3^n]`; 458 | /// * `csprng`, an implementation of `rand::Rng`, which should be 459 | /// cryptographically secure. 460 | /// 461 | /// # Returns 462 | /// 463 | /// If `value` is not in the range `[0,3^n]`, return None. 464 | /// 465 | /// Note that this function is designed to execute in constant 466 | /// time for all *valid* inputs. Passing an out-of-range `value` 467 | /// will cause it to return `None` early. 468 | /// 469 | /// Otherwise, returns `Some((proof, commitment, blinding))`, where: 470 | /// `proof` is the rangeproof, and `commitment = blinding*G + value*H`. 471 | /// 472 | /// Only the `RangeProof` should be sent to the verifier. The 473 | /// commitment and blinding are for the use of the prover. 474 | /// 475 | /// # Note 476 | /// 477 | /// Even when passing a deterministic CSPRNG generated with identical seeds, 478 | /// e.g. two instances of `rand::chacha::ChaChaRng::new_unseeded()`, and 479 | /// seeking to prove the same `value`, one cannot expect the `RangeProofs` 480 | /// generated with `RangeProof::create_vartime()` and `RangeProof::create()` 481 | /// to be identical. The values in the eventual proofs will differ, since 482 | /// this constant time version makes additional calls to the `csprng` which 483 | /// are thrown away in some conditions. 484 | pub fn create( 485 | n: usize, 486 | value: u64, 487 | G: &DecafBasepointTable, 488 | H: &DecafPoint, 489 | mut csprng: &mut T, 490 | ) -> Option<(RangeProof, DecafPoint, Scalar)> { 491 | // Calling verify with n out of bounds is a programming error. 492 | if n > RANGEPROOF_MAX_N { 493 | panic!("Error: called create_vartime with too large bound 3^n, n = {}", n); 494 | } 495 | 496 | // Check that value is in range: all digits above N should be 0 497 | let v = base3_digits(value); 498 | for i in n..41 { 499 | if v[i] != 0 { return None; } 500 | } 501 | 502 | let mut R = vec![DecafPoint::identity(); n]; 503 | let mut C = vec![DecafPoint::identity(); n]; 504 | let mut k = vec![Scalar::zero(); n]; 505 | let mut r = vec![Scalar::zero(); n]; 506 | let mut s_1 = vec![Scalar::zero(); n]; 507 | let mut s_2 = vec![Scalar::zero(); n]; 508 | let mut e_1 = vec![Scalar::zero(); n]; 509 | let mut e_2 = vec![Scalar::zero(); n]; 510 | 511 | let mut mi_H = *H; 512 | let mut P: DecafPoint; 513 | 514 | for i in 0..n { 515 | debug_assert!(v[i] == 0 || v[i] == 1 || v[i] == 2); 516 | 517 | let mi2_H: DecafPoint = &mi_H + &mi_H; 518 | 519 | k[i] = Scalar::random(&mut csprng); 520 | 521 | // Commitment to i-th digit is r^i G + (v^1 * m^i H) 522 | let maybe_ri: Scalar = Scalar::random(&mut csprng); 523 | r[i].conditional_assign(&maybe_ri, byte_is_nonzero(v[i])); 524 | 525 | let mut which_mi_H: DecafPoint = mi_H; // is a copy 526 | which_mi_H.conditional_assign(&mi2_H, bytes_equal_ct(v[i], 2u8)); 527 | 528 | let maybe_Ci: DecafPoint = &(G * &r[i]) + &which_mi_H; 529 | C[i].conditional_assign(&maybe_Ci, byte_is_nonzero(v[i])); 530 | 531 | P = &k[i] * G; 532 | 533 | // Begin at index 1 in the ring, choosing random e_{v^i} 534 | let mut maybe_ei = Scalar::hash_from_bytes::(P.compress().as_bytes()); 535 | e_1[i].conditional_assign(&maybe_ei, bytes_equal_ct(v[i], 1u8)); 536 | e_2[i].conditional_assign(&maybe_ei, bytes_equal_ct(v[i], 2u8)); 537 | 538 | // Choose random scalar for s_2 539 | let maybe_s2: Scalar = Scalar::random(&mut csprng); 540 | s_2[i].conditional_assign(&maybe_s2, bytes_equal_ct(v[i], 1u8)); 541 | 542 | // Compute e_2 = Hash(s_2^i G - e_1^i (C^i - 2m^i H) ) 543 | P = &(&s_2[i] * G) - &(&e_1[i] * &(&C[i] - &mi2_H)); 544 | maybe_ei = Scalar::hash_from_bytes::(P.compress().as_bytes()); 545 | e_2[i].conditional_assign(&maybe_ei, bytes_equal_ct(v[i], 1u8)); 546 | 547 | // Compute R^i = k^i G iff v^i == 0, otherwise 548 | // R^i = e_2^i * C^i 549 | R[i] = &k[i] * G; 550 | 551 | let maybe_Ri: DecafPoint = &e_2[i] * &C[i]; 552 | R[i].conditional_assign(&maybe_Ri, byte_is_nonzero(v[i])); 553 | 554 | // Multiply mi_H by m (a.k.a. m == 3) 555 | mi_H = &mi2_H + &mi_H; 556 | } 557 | 558 | // Compute e_0 = Hash( R^0 || ... || R^{n-1} ) 559 | let mut e_0_hash = Sha512::default(); 560 | for i in 0..n { 561 | e_0_hash.input(R[i].compress().as_bytes()); // XXX new digest API for 0.5.x 562 | } 563 | let e_0 = Scalar::from_hash(e_0_hash); 564 | 565 | let mut mi_H = *H; 566 | 567 | for i in 0..n { 568 | debug_assert!(v[i] == 0 || v[i] == 1 || v[i] == 2); 569 | 570 | let mi2_H = &mi_H + &mi_H; 571 | 572 | let mut k_1 = Scalar::zero(); 573 | let maybe_k1: Scalar = Scalar::random(&mut csprng); 574 | k_1.conditional_assign(&maybe_k1, bytes_equal_ct(v[i], 0u8)); 575 | 576 | P = &(&k_1 * G) + &(&e_0 * &mi_H); 577 | let maybe_e_1 = Scalar::hash_from_bytes::(P.compress().as_bytes()); 578 | e_1[i].conditional_assign(&maybe_e_1, bytes_equal_ct(v[i], 0u8)); 579 | 580 | let mut k_2 = Scalar::zero(); 581 | let maybe_k2: Scalar = Scalar::random(&mut csprng); 582 | k_2.conditional_assign(&maybe_k2, bytes_equal_ct(v[i], 0u8)); 583 | 584 | P = &(&k_2 * &G.basepoint()) + &(&e_1[i] * &mi2_H); 585 | let maybe_e_2 = Scalar::hash_from_bytes::(P.compress().as_bytes()); // XXX API 586 | e_2[i].conditional_assign(&maybe_e_2, bytes_equal_ct(v[i], 0u8)); 587 | 588 | let e_2_inv = e_2[i].invert(); // XXX only used in v[i]==0, check what the optimiser is doing 589 | let maybe_r_i = &e_2_inv * &k[i]; 590 | r[i].conditional_assign(&maybe_r_i, bytes_equal_ct(v[i], 0u8)); 591 | 592 | let maybe_C_i = G * &r[i]; 593 | C[i].conditional_assign(&maybe_C_i, bytes_equal_ct(v[i], 0u8)); 594 | 595 | let ki_e_2_inv = &k[i] * &e_2_inv; 596 | let mut maybe_s_1 = &k_1 + &(&e_0 * &ki_e_2_inv); 597 | s_1[i].conditional_assign(&maybe_s_1, bytes_equal_ct(v[i], 0u8)); 598 | maybe_s_1 = Scalar::multiply_add(&e_0, &r[i], &k[i]); 599 | s_1[i].conditional_assign(&maybe_s_1, bytes_equal_ct(v[i], 1u8)); 600 | maybe_s_1 = Scalar::random(&mut csprng); 601 | s_1[i].conditional_assign(&maybe_s_1, bytes_equal_ct(v[i], 2u8)); 602 | 603 | // Compute e_1^i = Hash(s_1^i G - e_0^i (C^i - 1 m^i H) ) 604 | let Ci_minus_miH = &C[i] - &mi_H; // XXX only used in v[i]==2, check optimiser 605 | 606 | P = &(&s_1[i] * &G.basepoint()) - &(&e_0 * &Ci_minus_miH); 607 | let maybe_e_1 = Scalar::hash_from_bytes::(P.compress().as_bytes()); 608 | e_1[i].conditional_assign(&maybe_e_1, bytes_equal_ct(v[i], 2u8)); 609 | 610 | let mut maybe_s_2 = &k_2 + &(&e_1[i] * &ki_e_2_inv); 611 | s_2[i].conditional_assign(&maybe_s_2, bytes_equal_ct(v[i], 0u8)); 612 | maybe_s_2 = Scalar::multiply_add(&e_1[i], &r[i], &k[i]); 613 | s_2[i].conditional_assign(&maybe_s_2, bytes_equal_ct(v[i], 2u8)); 614 | 615 | // Set mi_H <-- 3*m_iH, so that mi_H is always 3^i * H in the loop 616 | mi_H = &mi_H + &mi2_H; 617 | } 618 | 619 | let mut blinding = Scalar::zero(); 620 | let mut commitment = DecafPoint::identity(); 621 | for i in 0..n { 622 | blinding += &r[i]; 623 | // XXX implement AddAssign for ExtendedPoint 624 | commitment = &commitment + &C[i]; 625 | } 626 | 627 | Some(( 628 | RangeProof{ e_0: e_0, C: C, s_1: s_1, s_2: s_2}, 629 | commitment, 630 | blinding, 631 | )) 632 | } 633 | } 634 | 635 | fn base3_digits(mut x: u64) -> [u8; 41] { 636 | let mut digits = [0u8; 41]; 637 | for i in 0..41 { 638 | let rem = x % 3; 639 | digits[i] = rem as u8; 640 | x = x / 3; 641 | } 642 | digits 643 | } 644 | 645 | #[cfg(test)] 646 | mod tests { 647 | use super::*; 648 | 649 | use rand::OsRng; 650 | use sha2::Sha256; 651 | 652 | use curve25519_dalek::constants as dalek_constants; 653 | 654 | #[test] 655 | fn base3_digits_vs_sage() { 656 | let values: [u64; 10] = [10352669767914021650, 7804842618637096123, 657 | 7334633556203117754, 8160423201521470302, 658 | 17232767106382697250, 8845500362072010910, 659 | 9696550650556789001, 769845413554321661, 660 | 3398590720602317514, 14390516357262902374]; 661 | let digits_sage: [[u8;41]; 10] = [ 662 | [2, 2, 0, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 1, 1, 2, 1, 2, 0, 1, 0, 2, 2, 1, 0, 1, 2, 0, 2, 0, 2, 2, 0, 2, 2, 2, 2, 1, 1, 2, 0], 663 | [1, 1, 2, 2, 1, 0, 1, 2, 1, 0, 0, 1, 0, 2, 2, 1, 1, 1, 2, 0, 1, 0, 0, 1, 1, 0, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2, 1, 0], 664 | [0, 1, 1, 0, 2, 2, 1, 2, 0, 0, 0, 2, 0, 2, 1, 1, 0, 2, 1, 0, 0, 0, 2, 0, 0, 1, 2, 1, 1, 2, 1, 0, 1, 2, 1, 2, 0, 1, 2, 1, 0], 665 | [0, 2, 0, 1, 2, 2, 0, 0, 2, 2, 2, 2, 0, 2, 2, 0, 1, 1, 1, 1, 0, 1, 0, 2, 0, 2, 1, 2, 2, 1, 1, 2, 2, 0, 0, 1, 0, 0, 0, 2, 0], 666 | [0, 1, 2, 1, 2, 2, 0, 2, 0, 0, 2, 2, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 2, 1, 2, 2, 2, 0, 1, 1, 2, 2, 0, 1, 2, 0, 2, 0, 1, 1], 667 | [1, 2, 0, 2, 2, 0, 2, 1, 0, 1, 2, 1, 2, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 0, 2, 0, 0, 0, 2, 1, 0, 1, 2, 2, 1, 1, 0, 2, 0], 668 | [2, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 1, 0, 1, 0, 2, 1, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 2, 0], 669 | [2, 2, 2, 1, 2, 2, 1, 2, 0, 2, 1, 0, 2, 1, 1, 2, 2, 2, 2, 0, 2, 1, 0, 1, 2, 0, 1, 2, 0, 0, 1, 1, 1, 0, 1, 0, 2, 1, 0, 0, 0], 670 | [0, 1, 1, 2, 0, 1, 1, 1, 2, 1, 0, 0, 2, 2, 2, 0, 0, 1, 1, 1, 2, 2, 0, 0, 0, 2, 2, 1, 0, 2, 0, 0, 1, 2, 2, 1, 1, 1, 2, 0, 0], 671 | [1, 2, 0, 0, 1, 1, 0, 1, 2, 1, 0, 1, 0, 1, 2, 2, 0, 0, 2, 1, 0, 1, 2, 2, 1, 2, 2, 0, 1, 2, 2, 2, 1, 2, 1, 2, 2, 1, 1, 0, 1], 672 | ]; 673 | 674 | for i in 0..10 { 675 | let digits = base3_digits(values[i]); 676 | for j in 0..41 { 677 | assert_eq!(digits[j], digits_sage[i][j]); 678 | } 679 | } 680 | } 681 | 682 | #[test] 683 | fn prove_and_verify_vartime() { 684 | let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 685 | let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 686 | 687 | let mut csprng = OsRng::new().unwrap(); 688 | 689 | let n = 16; 690 | let value = 13449261; 691 | let (proof, commitment, blinding) = 692 | RangeProof::create_vartime(n, value, G, &H, &mut csprng).unwrap(); 693 | 694 | let C_option = proof.verify(n, G, &H); 695 | assert!(C_option.is_some()); 696 | 697 | assert!(proof.verify(2, G, &H).is_none()); 698 | 699 | let C = C_option.unwrap(); 700 | let C_hat = &(G * &blinding) + &(&H * &Scalar::from_u64(value)); 701 | 702 | assert_eq!(C.compress(), C_hat.compress()); 703 | assert_eq!(commitment.compress(), C_hat.compress()); 704 | } 705 | 706 | #[test] 707 | fn prove_and_verify_ct() { 708 | let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 709 | let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 710 | 711 | let mut csprng = OsRng::new().unwrap(); 712 | 713 | let n = 16; 714 | let value = 13449261; 715 | let (proof, commitment, blinding) = 716 | RangeProof::create(n, value, G, &H, &mut csprng).unwrap(); 717 | 718 | let C_option = proof.verify(n, G, &H); 719 | assert!(C_option.is_some()); 720 | 721 | assert!(proof.verify(2, G, &H).is_none()); 722 | 723 | let C = C_option.unwrap(); 724 | let C_hat = &(G * &blinding) + &(&H * &Scalar::from_u64(value)); 725 | 726 | assert_eq!(C.compress(), C_hat.compress()); 727 | assert_eq!(commitment.compress(), C_hat.compress()); 728 | } 729 | } 730 | 731 | #[cfg(all(test, feature = "bench"))] 732 | mod bench { 733 | use test::Bencher; 734 | use super::*; 735 | 736 | use rand::OsRng; 737 | use sha2::Sha256; 738 | use curve25519_dalek::constants as dalek_constants; 739 | 740 | #[bench] 741 | fn verify(b: &mut Bencher) { 742 | let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 743 | let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 744 | 745 | let mut csprng = OsRng::new().unwrap(); 746 | 747 | let value = 1666; 748 | let (proof, _, _) = RangeProof::create_vartime( 749 | RANGEPROOF_MAX_N, value, G, &H, &mut csprng).unwrap(); 750 | 751 | b.iter(|| proof.verify(RANGEPROOF_MAX_N, G, &H)); 752 | } 753 | 754 | #[bench] 755 | fn prove_vartime(b: &mut Bencher) { 756 | let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 757 | let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 758 | 759 | let mut csprng = OsRng::new().unwrap(); 760 | 761 | let value = 1666; 762 | b.iter(|| RangeProof::create_vartime(RANGEPROOF_MAX_N, value, G, &H, &mut csprng)); 763 | } 764 | 765 | #[bench] 766 | fn prove_ct(b: &mut Bencher) { 767 | let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE; 768 | let H = DecafPoint::hash_from_bytes::(G.basepoint().compress().as_bytes()); 769 | 770 | let mut csprng = OsRng::new().unwrap(); 771 | 772 | let value = 1666; 773 | b.iter(|| RangeProof::create(RANGEPROOF_MAX_N, value, G, &H, &mut csprng)); 774 | } 775 | 776 | #[bench] 777 | fn bench_base3_digits(b: &mut Bencher) { 778 | b.iter(|| base3_digits(10352669767914021650) ); 779 | } 780 | } 781 | --------------------------------------------------------------------------------