merlin-3.0.0/.cargo_vcs_info.json0000644000000001120000000000000123130ustar { "git": { "sha1": "a94892f457202399661573480bb7250b4fe7c0af" } } merlin-3.0.0/.github/workflows/main.yml000064400000000000000000000033330000000000000161350ustar 00000000000000name: Test merlin on: push: pull_request: types: [opened, repoened, synchronize] jobs: native-test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: rust_toolchain: [nightly, stable] os: [ubuntu-latest] timeout-minutes: 10 steps: - uses: actions/checkout@v2 - name: Install Rust ${{ matrix.rust_toolchain }} uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust_toolchain }} profile: minimal default: true - name: Build uses: actions-rs/cargo@v1 with: command: build - name: Test uses: actions-rs/cargo@v1 with: command: test # Uncomment this when adding support for big-endian targets. # cross-linux-test: # runs-on: ubuntu-latest # strategy: # fail-fast: false # matrix: # rust_target: # - powerpc-unknown-linux-gnu # - powerpc64-unknown-linux-gnu # timeout-minutes: 10 # steps: # - uses: actions/checkout@v2 # - name: Install Rust nightly # uses: actions-rs/toolchain@v1 # with: # toolchain: nightly # profile: minimal # target: ${{ matrix.rust_target }} # default: true # - name: Build # uses: actions-rs/cargo@v1 # with: # # see https://github.com/rust-embedded/cross # use-cross: true # command: build # args: --target ${{ matrix.rust_target }} # - name: Test # uses: actions-rs/cargo@v1 # with: # use-cross: true # command: test # args: --target ${{ matrix.rust_target }} -- --test-threads=1 merlin-3.0.0/CHANGELOG.md000064400000000000000000000031550000000000000127040ustar 00000000000000# Changelog ## 3.0.0 * Update `rand_core` to `0.6`. Because traits from `rand_core` are part of the public API, this is technically a breaking change, but there are no other changes to Merlin's API. ## 2.0.1 * Update repository, add `html_root_url`, update dev-dependencies. ## 2.0.0 * Update `rand_core` to `0.5`. Because traits from `rand_core` are part of the public API, this is technically a breaking change, but there are no other changes to Merlin's API. ## 1.3.0 * Replace `clear_on_drop` with `zeroize`, and implement `Zeroize` for `Transcript`. ## 1.2.1 * Switch to Rust 2018. * Update the `strobe-rs` dev-dependency used for conformance testing to `0.5.0`. ## 1.2.0 * Add `no_std` support. The `std` feature is enabled by default. ## 1.1.0 * Rename transcript functions to avoid any possible confusion between protocol-level "commitments" and transcript messages. * Move design docs to [merlin.cool](https://merlin.cool) and reorient the Rust docs around the API. ## 1.0.3 * Remove `rand` dependency in favor of `rand_core`. * Update README with example of transcript logs. ## 1.0.2 * Update doc comment on Merlin domain separator version string. * Add an experimental `debug-transcript` feature which does pretty-printing. ## 1.0.1 * Update the `curve25519-dalek` dev-dependency used for tests to version `1.0`. ## 1.0.0 * Initial stable version. ## 0.3 * Forces labels to be `&'static` * Merlin-specific domain separator * Use a pointer cast instead of a transmute * Clarify example documentation ## 0.2 * Adds a TranscriptRng for use by the prover. ## 0.1 * Initial prototype version. merlin-3.0.0/Cargo.toml0000644000000033210000000000000103160ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "merlin" version = "3.0.0" authors = ["Henry de Valence "] exclude = [".travis.yml", ".gitignore"] description = "Composable proof transcripts for public-coin arguments of knowledge" homepage = "https://docs.rs/merlin" documentation = "https://docs.rs/merlin" readme = "README.md" keywords = ["cryptography", "zero-knowledge", "fiat-shamir", "transcript"] categories = ["cryptography"] license = "MIT" repository = "https://github.com/zkcrypto/merlin" [package.metadata.docs.rs] features = ["nightly"] [dependencies.byteorder] version = "1.2.4" default-features = false [dependencies.hex] version = "0.3" optional = true default-features = false [dependencies.keccak] version = "0.1.0" default-features = false [dependencies.rand_core] version = "0.6" default-features = false [dependencies.zeroize] version = "1" features = ["zeroize_derive"] default-features = false [dev-dependencies.curve25519-dalek] version = "4" package = "curve25519-dalek-ng" [dev-dependencies.rand_chacha] version = "0.3" [dev-dependencies.strobe-rs] version = "0.5" [features] debug-transcript = ["hex"] default = ["std"] nightly = [] std = ["rand_core/std", "byteorder/std"] merlin-3.0.0/Cargo.toml.orig000064400000000000000000000024060000000000000137600ustar 00000000000000[package] name = "merlin" # Before incrementing: # - update CHANGELOG # - update html_root_url # - update README if required by semver version = "3.0.0" authors = ["Henry de Valence "] edition = "2018" readme = "README.md" license = "MIT" repository = "https://github.com/zkcrypto/merlin" homepage = "https://docs.rs/merlin" documentation = "https://docs.rs/merlin" categories = ["cryptography"] keywords = ["cryptography", "zero-knowledge", "fiat-shamir", "transcript"] description = "Composable proof transcripts for public-coin arguments of knowledge" exclude = [".travis.yml", ".gitignore"] [package.metadata.docs.rs] # We need nightly to build docs features = ["nightly"] [dependencies] keccak = { version = "0.1.0", default-features = false } byteorder = { version = "1.2.4", default-features = false } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } rand_core = { version = "0.6", default-features = false } hex = {version = "0.3", default-features = false, optional = true} [dev-dependencies] strobe-rs = "0.5" curve25519-dalek = { version = "4", package = "curve25519-dalek-ng" } rand_chacha = "0.3" [features] default = ["std"] nightly = [] debug-transcript = ["hex"] std = ["rand_core/std", "byteorder/std"] merlin-3.0.0/LICENSE.txt000064400000000000000000000020620000000000000127120ustar 00000000000000MIT License Copyright (c) 2018 Henry de Valence. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. merlin-3.0.0/README.md000064400000000000000000000054600000000000000123530ustar 00000000000000 ## Merlin: composable proof transcripts for public-coin arguments of knowledge [Merlin][merlin_cool] is a [STROBE][strobe]-based transcript construction for zero-knowledge proofs. It automates the Fiat-Shamir transform, so that by using Merlin, non-interactive protocols can be implemented as if they were interactive. This is significantly easier and less error-prone than performing the transformation by hand, and in addition, it also provides natural support for: * multi-round protocols with alternating commit and challenge phases; * natural domain separation, ensuring challenges are bound to the statements to be proved; * automatic message framing, preventing ambiguous encoding of commitment data; * and protocol composition, by using a common transcript for multiple protocols. Finally, Merlin also provides a transcript-based random number generator as defense-in-depth against bad-entropy attacks (such as nonce reuse, or bias over many proofs). This RNG provides synthetic randomness derived from the entire public transcript, as well as the prover's witness data, and an auxiliary input from an external RNG. More details on the design of Merlin and how to use it for proof systems can be found on the [Merlin website][merlin_cool]. ## Features The `nightly` feature is passed to `clear_on_drop`; it may be replaced with a no-op in the future (since `clear_on_drop` is an implementation detail). The `debug-transcript` feature prints an annotated proof transcript to `stdout`; it is only suitable for development and testing purposes, should not be used in released crates, and should not be considered stable. An example of an annotated transcript for a Bulletproof rangeproof can be [found here][bp_transcript]. ## About Merlin is authored by Henry de Valence, with design input from Isis Lovecruft and Oleg Andreev. The construction grew out of work with Oleg Andreev and Cathie Yun on a [Bulletproofs implementation][bp]. Thanks also to Trevor Perrin and Mike Hamburg for helpful discussions. Merlin is named in reference to [Arthur-Merlin protocols][am_wiki] which introduced the notion of public coin arguments. The header image was created by Oleg Andreev as a composite of Arthur Pyle's [The Enchanter Merlin][merlin_pyle] and the Keccak Team's [θ-step diagram][keccak_theta]. This project is licensed under the MIT license. [merlin_cool]: https://merlin.cool [bp]: https://doc.dalek.rs/bulletproofs/ [strobe]: https://strobe.sourceforge.io/ [am_wiki]: https://en.wikipedia.org/wiki/Arthur%E2%80%93Merlin_protocol [merlin_pyle]: https://commons.wikimedia.org/wiki/File:Arthur-Pyle_The_Enchanter_Merlin.JPG [keccak_theta]: https://keccak.team/figures.html [bp_transcript]: https://gist.github.com/hdevalence/9db3997cc275597eeae1ec2461b8e2a1 merlin-3.0.0/src/constants.rs000064400000000000000000000005630000000000000142440ustar 00000000000000/// Domain separation label to initialize the STROBE context. /// /// This is not to be confused with the crate's semver string: /// the latter applies to the API, while this label defines the protocol. /// E.g. it is possible that crate 2.0 will have an incompatible API, /// but implement the same 1.0 protocol. pub const MERLIN_PROTOCOL_LABEL: &[u8] = b"Merlin v1.0"; merlin-3.0.0/src/lib.rs000064400000000000000000000014370000000000000127770ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![doc(html_root_url = "https://docs.rs/merlin/3.0.0")] // put this after the #![doc(..)] so it appears as a footer: //! Note that docs will only build on nightly Rust until //! [RFC 1990 stabilizes](https://github.com/rust-lang/rust/issues/44732). #[cfg(target_endian = "big")] compile_error!( r#" This crate doesn't support big-endian targets, since I didn't have one to test correctness on. If you're seeing this message, please file an issue! "# ); mod constants; mod strobe; mod transcript; pub use crate::transcript::Transcript; pub use crate::transcript::TranscriptRng; pub use crate::transcript::TranscriptRngBuilder; merlin-3.0.0/src/strobe.rs000064400000000000000000000135010000000000000135220ustar 00000000000000//! Minimal implementation of (parts of) Strobe. use core::ops::{Deref, DerefMut}; use keccak; use zeroize::Zeroize; /// Strobe R value; security level 128 is hardcoded const STROBE_R: u8 = 166; const FLAG_I: u8 = 1; const FLAG_A: u8 = 1 << 1; const FLAG_C: u8 = 1 << 2; const FLAG_T: u8 = 1 << 3; const FLAG_M: u8 = 1 << 4; const FLAG_K: u8 = 1 << 5; fn transmute_state(st: &mut AlignedKeccakState) -> &mut [u64; 25] { unsafe { &mut *(st as *mut AlignedKeccakState as *mut [u64; 25]) } } /// This is a wrapper around 200-byte buffer that's always 8-byte aligned /// to make pointers to it safely convertible to pointers to [u64; 25] /// (since u64 words must be 8-byte aligned) #[derive(Clone, Zeroize)] #[zeroize(drop)] #[repr(align(8))] struct AlignedKeccakState([u8; 200]); /// A Strobe context for the 128-bit security level. /// /// Only `meta-AD`, `AD`, `KEY`, and `PRF` operations are supported. #[derive(Clone, Zeroize)] pub struct Strobe128 { state: AlignedKeccakState, pos: u8, pos_begin: u8, cur_flags: u8, } impl ::core::fmt::Debug for Strobe128 { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { // Ensure that the Strobe state isn't accidentally logged write!(f, "Strobe128: STATE OMITTED") } } impl Strobe128 { pub fn new(protocol_label: &[u8]) -> Strobe128 { let initial_state = { let mut st = AlignedKeccakState([0u8; 200]); st[0..6].copy_from_slice(&[1, STROBE_R + 2, 1, 0, 1, 96]); st[6..18].copy_from_slice(b"STROBEv1.0.2"); keccak::f1600(transmute_state(&mut st)); st }; let mut strobe = Strobe128 { state: initial_state, pos: 0, pos_begin: 0, cur_flags: 0, }; strobe.meta_ad(protocol_label, false); strobe } pub fn meta_ad(&mut self, data: &[u8], more: bool) { self.begin_op(FLAG_M | FLAG_A, more); self.absorb(data); } pub fn ad(&mut self, data: &[u8], more: bool) { self.begin_op(FLAG_A, more); self.absorb(data); } pub fn prf(&mut self, data: &mut [u8], more: bool) { self.begin_op(FLAG_I | FLAG_A | FLAG_C, more); self.squeeze(data); } pub fn key(&mut self, data: &[u8], more: bool) { self.begin_op(FLAG_A | FLAG_C, more); self.overwrite(data); } } impl Strobe128 { fn run_f(&mut self) { self.state[self.pos as usize] ^= self.pos_begin; self.state[(self.pos + 1) as usize] ^= 0x04; self.state[(STROBE_R + 1) as usize] ^= 0x80; keccak::f1600(transmute_state(&mut self.state)); self.pos = 0; self.pos_begin = 0; } fn absorb(&mut self, data: &[u8]) { for byte in data { self.state[self.pos as usize] ^= byte; self.pos += 1; if self.pos == STROBE_R { self.run_f(); } } } fn overwrite(&mut self, data: &[u8]) { for byte in data { self.state[self.pos as usize] = *byte; self.pos += 1; if self.pos == STROBE_R { self.run_f(); } } } fn squeeze(&mut self, data: &mut [u8]) { for byte in data { *byte = self.state[self.pos as usize]; self.state[self.pos as usize] = 0; self.pos += 1; if self.pos == STROBE_R { self.run_f(); } } } fn begin_op(&mut self, flags: u8, more: bool) { // Check if we're continuing an operation if more { assert_eq!( self.cur_flags, flags, "You tried to continue op {:#b} but changed flags to {:#b}", self.cur_flags, flags, ); return; } // Skip adjusting direction information (we just use AD, PRF) assert_eq!( flags & FLAG_T, 0u8, "You used the T flag, which this implementation doesn't support" ); let old_begin = self.pos_begin; self.pos_begin = self.pos + 1; self.cur_flags = flags; self.absorb(&[old_begin, flags]); // Force running F if C or K is set let force_f = 0 != (flags & (FLAG_C | FLAG_K)); if force_f && self.pos != 0 { self.run_f(); } } } impl Deref for AlignedKeccakState { type Target = [u8; 200]; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for AlignedKeccakState { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } #[cfg(test)] mod tests { use strobe_rs::{self, SecParam}; #[test] fn test_conformance() { let mut s1 = super::Strobe128::new(b"Conformance Test Protocol"); let mut s2 = strobe_rs::Strobe::new(b"Conformance Test Protocol", SecParam::B128); // meta-AD(b"msg"); AD(msg) let msg = [99u8; 1024]; s1.meta_ad(b"ms", false); s1.meta_ad(b"g", true); s1.ad(&msg, false); s2.meta_ad(b"ms", false); s2.meta_ad(b"g", true); s2.ad(&msg, false); // meta-AD(b"prf"); PRF() let mut prf1 = [0u8; 32]; s1.meta_ad(b"prf", false); s1.prf(&mut prf1, false); let mut prf2 = [0u8; 32]; s2.meta_ad(b"prf", false); s2.prf(&mut prf2, false); assert_eq!(prf1, prf2); // meta-AD(b"key"); KEY(prf output) s1.meta_ad(b"key", false); s1.key(&prf1, false); s2.meta_ad(b"key", false); s2.key(&prf2, false); // meta-AD(b"prf"); PRF() let mut prf1 = [0u8; 32]; s1.meta_ad(b"prf", false); s1.prf(&mut prf1, false); let mut prf2 = [0u8; 32]; s2.meta_ad(b"prf", false); s2.prf(&mut prf2, false); assert_eq!(prf1, prf2); } } merlin-3.0.0/src/transcript.rs000064400000000000000000000464140000000000000144260ustar 00000000000000use rand_core; use zeroize::Zeroize; use crate::strobe::Strobe128; fn encode_u64(x: u64) -> [u8; 8] { use byteorder::{ByteOrder, LittleEndian}; let mut buf = [0; 8]; LittleEndian::write_u64(&mut buf, x); buf } fn encode_usize_as_u32(x: usize) -> [u8; 4] { use byteorder::{ByteOrder, LittleEndian}; assert!(x <= (u32::max_value() as usize)); let mut buf = [0; 4]; LittleEndian::write_u32(&mut buf, x as u32); buf } /// A transcript of a public-coin argument. /// /// The prover's messages are added to the transcript using /// [`append_message`](Transcript::append_message), and the verifier's /// challenges can be computed using /// [`challenge_bytes`](Transcript::challenge_bytes). /// /// # Creating and using a Merlin transcript /// /// To create a Merlin transcript, use [`Transcript::new()`]. This /// function takes a domain separation label which should be unique to /// the application. /// /// To use the transcript with a Merlin-based proof implementation, /// the prover's side creates a Merlin transcript with an /// application-specific domain separation label, and passes a `&mut` /// reference to the transcript to the proving function(s). /// /// To verify the resulting proof, the verifier creates their own /// Merlin transcript using the same domain separation label, then /// passes a `&mut` reference to the verifier's transcript to the /// verification function. /// /// # Implementing proofs using Merlin /// /// For information on the design of Merlin and how to use it to /// implement a proof system, see the documentation at /// [merlin.cool](https://merlin.cool), particularly the [Using /// Merlin](https://merlin.cool/use/index.html) section. #[derive(Clone, Zeroize)] pub struct Transcript { strobe: Strobe128, } impl Transcript { /// Initialize a new transcript with the supplied `label`, which /// is used as a domain separator. /// /// # Note /// /// This function should be called by a proof library's API /// consumer (i.e., the application using the proof library), and /// **not by the proof implementation**. See the [Passing /// Transcripts](https://merlin.cool/use/passing.html) section of /// the Merlin website for more details on why. pub fn new(label: &'static [u8]) -> Transcript { use crate::constants::MERLIN_PROTOCOL_LABEL; #[cfg(feature = "debug-transcript")] { use std::str::from_utf8; println!( "Initialize STROBE-128({})\t# b\"{}\"", hex::encode(MERLIN_PROTOCOL_LABEL), from_utf8(MERLIN_PROTOCOL_LABEL).unwrap(), ); } let mut transcript = Transcript { strobe: Strobe128::new(MERLIN_PROTOCOL_LABEL), }; transcript.append_message(b"dom-sep", label); transcript } /// Append a prover's `message` to the transcript. /// /// The `label` parameter is metadata about the message, and is /// also appended to the transcript. See the [Transcript /// Protocols](https://merlin.cool/use/protocol.html) section of /// the Merlin website for details on labels. pub fn append_message(&mut self, label: &'static [u8], message: &[u8]) { let data_len = encode_usize_as_u32(message.len()); self.strobe.meta_ad(label, false); self.strobe.meta_ad(&data_len, true); self.strobe.ad(message, false); #[cfg(feature = "debug-transcript")] { use std::str::from_utf8; match from_utf8(label) { Ok(label_str) => { println!( "meta-AD : {} || LE32({})\t# b\"{}\"", hex::encode(label), message.len(), label_str ); } Err(_) => { println!( "meta-AD : {} || LE32({})", hex::encode(label), message.len() ); } } match from_utf8(message) { Ok(message_str) => { println!(" AD : {}\t# b\"{}\"", hex::encode(message), message_str); } Err(_) => { println!(" AD : {}", hex::encode(message)); } } } } /// Deprecated. This function was renamed to /// [`append_message`](Transcript::append_message). /// /// This is intended to avoid any possible confusion between the /// transcript-level messages and protocol-level commitments. #[deprecated(since = "1.1.0", note = "renamed to append_message for clarity.")] pub fn commit_bytes(&mut self, label: &'static [u8], message: &[u8]) { self.append_message(label, message); } /// Convenience method for appending a `u64` to the transcript. /// /// The `label` parameter is metadata about the message, and is /// also appended to the transcript. See the [Transcript /// Protocols](https://merlin.cool/use/protocol.html) section of /// the Merlin website for details on labels. /// /// # Implementation /// /// Calls `append_message` with the 8-byte little-endian encoding /// of `x`. pub fn append_u64(&mut self, label: &'static [u8], x: u64) { self.append_message(label, &encode_u64(x)); } /// Deprecated. This function was renamed to /// [`append_u64`](Transcript::append_u64). /// /// This is intended to avoid any possible confusion between the /// transcript-level messages and protocol-level commitments. #[deprecated(since = "1.1.0", note = "renamed to append_u64 for clarity.")] pub fn commit_u64(&mut self, label: &'static [u8], x: u64) { self.append_u64(label, x); } /// Fill the supplied buffer with the verifier's challenge bytes. /// /// The `label` parameter is metadata about the challenge, and is /// also appended to the transcript. See the [Transcript /// Protocols](https://merlin.cool/use/protocol.html) section of /// the Merlin website for details on labels. pub fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { let data_len = encode_usize_as_u32(dest.len()); self.strobe.meta_ad(label, false); self.strobe.meta_ad(&data_len, true); self.strobe.prf(dest, false); #[cfg(feature = "debug-transcript")] { use std::str::from_utf8; match from_utf8(label) { Ok(label_str) => { println!( "meta-AD : {} || LE32({})\t# b\"{}\"", hex::encode(label), dest.len(), label_str ); } Err(_) => { println!("meta-AD : {} || LE32({})", hex::encode(label), dest.len()); } } println!(" PRF: {}", hex::encode(dest)); } } /// Fork the current [`Transcript`] to construct an RNG whose output is bound /// to the current transcript state as well as prover's secrets. /// /// See the [`TranscriptRngBuilder`] documentation for more details. pub fn build_rng(&self) -> TranscriptRngBuilder { TranscriptRngBuilder { strobe: self.strobe.clone(), } } } /// Constructs a [`TranscriptRng`] by rekeying the [`Transcript`] with /// prover secrets and an external RNG. /// /// The prover uses a [`TranscriptRngBuilder`] to rekey with its /// witness data, before using an external RNG to finalize to a /// [`TranscriptRng`]. The resulting [`TranscriptRng`] will be a PRF /// of all of the entire public transcript, the prover's secret /// witness data, and randomness from the external RNG. /// /// # Usage /// /// To construct a [`TranscriptRng`], a prover calls /// [`Transcript::build_rng()`] to clone the transcript state, then /// uses [`rekey_with_witness_bytes()`][rekey_with_witness_bytes] to rekey the /// transcript with the prover's secrets, before finally calling /// [`finalize()`][finalize]. This rekeys the transcript with the /// output of an external [`rand_core::RngCore`] instance and returns /// a finalized [`TranscriptRng`]. /// /// These methods are intended to be chained, passing from a borrowed /// [`Transcript`] to an owned [`TranscriptRng`] as follows: /// ``` /// # extern crate merlin; /// # extern crate rand_core; /// # use merlin::Transcript; /// # fn main() { /// # let mut transcript = Transcript::new(b"TranscriptRng doctest"); /// # let public_data = b"public data"; /// # let witness_data = b"witness data"; /// # let more_witness_data = b"witness data"; /// transcript.append_message(b"public", public_data); /// /// let mut rng = transcript /// .build_rng() /// .rekey_with_witness_bytes(b"witness1", witness_data) /// .rekey_with_witness_bytes(b"witness2", more_witness_data) /// .finalize(&mut rand_core::OsRng); /// # } /// ``` /// In this example, the final `rng` is a PRF of `public_data` /// (as well as all previous `transcript` state), and of the prover's /// secret `witness_data` and `more_witness_data`, and finally, of the /// output of the thread-local RNG. /// Note that because the [`TranscriptRng`] is produced from /// [`finalize()`][finalize], it's impossible to forget /// to rekey the transcript with external randomness. /// /// # Note /// /// Protocols that require randomness in multiple places (e.g., to /// choose blinding factors for a multi-round protocol) should create /// a fresh [`TranscriptRng`] **each time they need randomness**, /// rather than reusing a single instance. This ensures that the /// randomness in each round is bound to the latest transcript state, /// rather than just the state of the transcript when randomness was /// first required. /// /// # Typed Witness Data /// /// Like the [`Transcript`], the [`TranscriptRngBuilder`] provides a /// minimal, byte-oriented API, and like the [`Transcript`], this API /// can be extended to allow rekeying with protocol-specific types /// using an extension trait. See the [Transcript /// Protocols](https://merlin.cool/use/protocol.html) section of the /// Merlin website for more details. /// /// [rekey_with_witness_bytes]: TranscriptRngBuilder::rekey_with_witness_bytes /// [finalize]: TranscriptRngBuilder::finalize pub struct TranscriptRngBuilder { strobe: Strobe128, } impl TranscriptRngBuilder { /// Rekey the transcript using the provided witness data. /// /// The `label` parameter is metadata about `witness`. pub fn rekey_with_witness_bytes( mut self, label: &'static [u8], witness: &[u8], ) -> TranscriptRngBuilder { let witness_len = encode_usize_as_u32(witness.len()); self.strobe.meta_ad(label, false); self.strobe.meta_ad(&witness_len, true); self.strobe.key(witness, false); self } /// Deprecated. This function was renamed to /// [`rekey_with_witness_bytes`](Transcript::rekey_with_witness_bytes). /// /// This is intended to avoid any possible confusion between the /// transcript-level messages and protocol-level commitments. #[deprecated( since = "1.1.0", note = "renamed to rekey_with_witness_bytes for clarity." )] pub fn commit_witness_bytes( self, label: &'static [u8], witness: &[u8], ) -> TranscriptRngBuilder { self.rekey_with_witness_bytes(label, witness) } /// Use the supplied external `rng` to rekey the transcript, so /// that the finalized [`TranscriptRng`] is a PRF bound to /// randomness from the external RNG, as well as all other /// transcript data. pub fn finalize(mut self, rng: &mut R) -> TranscriptRng where R: rand_core::RngCore + rand_core::CryptoRng, { let random_bytes = { let mut bytes = [0u8; 32]; rng.fill_bytes(&mut bytes); bytes }; self.strobe.meta_ad(b"rng", false); self.strobe.key(&random_bytes, false); TranscriptRng { strobe: self.strobe, } } } /// An RNG providing synthetic randomness to the prover. /// /// A [`TranscriptRng`] is constructed from a [`Transcript`] using a /// [`TranscriptRngBuilder`]; see its documentation for details on /// how to construct one. /// /// The transcript RNG construction is described in the [Generating /// Randomness](https://merlin.cool/transcript/rng.html) section of /// the Merlin website. pub struct TranscriptRng { strobe: Strobe128, } impl rand_core::RngCore for TranscriptRng { fn next_u32(&mut self) -> u32 { rand_core::impls::next_u32_via_fill(self) } fn next_u64(&mut self) -> u64 { rand_core::impls::next_u64_via_fill(self) } fn fill_bytes(&mut self, dest: &mut [u8]) { let dest_len = encode_usize_as_u32(dest.len()); self.strobe.meta_ad(&dest_len, false); self.strobe.prf(dest, false); } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); Ok(()) } } impl rand_core::CryptoRng for TranscriptRng {} #[cfg(test)] mod tests { use strobe_rs::SecParam; use strobe_rs::Strobe; use super::*; /// Test against a full strobe implementation to ensure we match the few /// operations we're interested in. struct TestTranscript { state: Strobe, } impl TestTranscript { /// Strobe init; meta-AD(label) pub fn new(label: &[u8]) -> TestTranscript { use crate::constants::MERLIN_PROTOCOL_LABEL; let mut tt = TestTranscript { state: Strobe::new(MERLIN_PROTOCOL_LABEL, SecParam::B128), }; tt.append_message(b"dom-sep", label); tt } /// Strobe op: meta-AD(label || len(message)); AD(message) pub fn append_message(&mut self, label: &[u8], message: &[u8]) { // metadata = label || len(message); let mut metadata: Vec = Vec::with_capacity(label.len() + 4); metadata.extend_from_slice(label); metadata.extend_from_slice(&encode_usize_as_u32(message.len())); self.state.meta_ad(&metadata, false); self.state.ad(&message, false); } /// Strobe op: meta-AD(label || len(dest)); PRF into challenge_bytes pub fn challenge_bytes(&mut self, label: &[u8], dest: &mut [u8]) { let prf_len = dest.len(); // metadata = label || len(challenge_bytes); let mut metadata: Vec = Vec::with_capacity(label.len() + 4); metadata.extend_from_slice(label); metadata.extend_from_slice(&encode_usize_as_u32(prf_len)); self.state.meta_ad(&metadata, false); self.state.prf(dest, false); } } /// Test a simple protocol with one message and one challenge #[test] fn equivalence_simple() { let mut real_transcript = Transcript::new(b"test protocol"); let mut test_transcript = TestTranscript::new(b"test protocol"); real_transcript.append_message(b"some label", b"some data"); test_transcript.append_message(b"some label", b"some data"); let mut real_challenge = [0u8; 32]; let mut test_challenge = [0u8; 32]; real_transcript.challenge_bytes(b"challenge", &mut real_challenge); test_transcript.challenge_bytes(b"challenge", &mut test_challenge); assert_eq!(real_challenge, test_challenge); } /// Test a complex protocol with multiple messages and challenges, /// with messages long enough to wrap around the sponge state, and /// with multiple rounds of messages and challenges. #[test] fn equivalence_complex() { let mut real_transcript = Transcript::new(b"test protocol"); let mut test_transcript = TestTranscript::new(b"test protocol"); let data = vec![99; 1024]; real_transcript.append_message(b"step1", b"some data"); test_transcript.append_message(b"step1", b"some data"); let mut real_challenge = [0u8; 32]; let mut test_challenge = [0u8; 32]; for _ in 0..32 { real_transcript.challenge_bytes(b"challenge", &mut real_challenge); test_transcript.challenge_bytes(b"challenge", &mut test_challenge); assert_eq!(real_challenge, test_challenge); real_transcript.append_message(b"bigdata", &data); test_transcript.append_message(b"bigdata", &data); real_transcript.append_message(b"challengedata", &real_challenge); test_transcript.append_message(b"challengedata", &test_challenge); } } #[test] fn transcript_rng_is_bound_to_transcript_and_witnesses() { use curve25519_dalek::scalar::Scalar; use rand_chacha::ChaChaRng; use rand_core::SeedableRng; // Check that the TranscriptRng is bound to the transcript and // the witnesses. This is done by producing a sequence of // transcripts that diverge at different points and checking // that they produce different challenges. let protocol_label = b"test TranscriptRng collisions"; let commitment1 = b"commitment data 1"; let commitment2 = b"commitment data 2"; let witness1 = b"witness data 1"; let witness2 = b"witness data 2"; let mut t1 = Transcript::new(protocol_label); let mut t2 = Transcript::new(protocol_label); let mut t3 = Transcript::new(protocol_label); let mut t4 = Transcript::new(protocol_label); t1.append_message(b"com", commitment1); t2.append_message(b"com", commitment2); t3.append_message(b"com", commitment2); t4.append_message(b"com", commitment2); let mut r1 = t1 .build_rng() .rekey_with_witness_bytes(b"witness", witness1) .finalize(&mut ChaChaRng::from_seed([0; 32])); let mut r2 = t2 .build_rng() .rekey_with_witness_bytes(b"witness", witness1) .finalize(&mut ChaChaRng::from_seed([0; 32])); let mut r3 = t3 .build_rng() .rekey_with_witness_bytes(b"witness", witness2) .finalize(&mut ChaChaRng::from_seed([0; 32])); let mut r4 = t4 .build_rng() .rekey_with_witness_bytes(b"witness", witness2) .finalize(&mut ChaChaRng::from_seed([0; 32])); let s1 = Scalar::random(&mut r1); let s2 = Scalar::random(&mut r2); let s3 = Scalar::random(&mut r3); let s4 = Scalar::random(&mut r4); // Transcript t1 has different commitments than t2, t3, t4, so // it should produce distinct challenges from all of them. assert_ne!(s1, s2); assert_ne!(s1, s3); assert_ne!(s1, s4); // Transcript t2 has different witness variables from t3, t4, // so it should produce distinct challenges from all of them. assert_ne!(s2, s3); assert_ne!(s2, s4); // Transcripts t3 and t4 have the same commitments and // witnesses, so they should give different challenges only // based on the RNG. Checking that they're equal in the // presence of a bad RNG checks that the different challenges // above aren't because the RNG is accidentally different. assert_eq!(s3, s4); } }