1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//!
//! **This crate is work in progress, not suitable for production.**
//!
//! Nimue helps performing Fiat-Shamir on any public-coin protocol.
//! It enables secure provision of randomness for the prover and secure generation
//! of random coins for the verifier.
//! It is inspired by the [SAFE] API, with minor variations.
//!
//!
//! # Overview
//!
//! The library does two things:
//!
//! - Assist in the construction of a protocol transcript for a public-coin zero-knowledge proof ([`Merlin`]),
//! - Assist in the deserialization and verification of a public-coin protocol ([`Arthur`]).
//!
//! The basic idea behind Nimue is that prover and verifier "commit" to the protocol before running the actual protocol.
//! They a string encoding the sequence of messages sent from the prover and the verifier (the [`IOPattern`]), which is used as an  "IV" to initialize the hash function for the Fiat-Shamir heuristic.
//!
//! There are prover just proceeds with concatenation, without ever worrying
//! about encoding length and special flags to embed in the hash function.
//! This allows for
//! better preprocessing,
//! friendliness with algebraic hashes,
//! static composition of protocol (and prevention of composition during the execution of a protocol),
//! easy an easier inspection of the Fiat-Shamir transform.
//!
//! ```
//! use nimue::{IOPattern, DefaultHash};
//!
//! let io = IOPattern::<DefaultHash>::new("๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿฅท๐Ÿป๐Ÿ‘จโ€๐Ÿ’ป building ๐Ÿ”๐Ÿ”’๐Ÿ—๏ธ")
//!         // this indicates the prover is sending 10 elements (bytes)
//!         .absorb(10, "first")
//!         // this indicates the verifier is sending 10 elements (bytes)
//!         .squeeze(10, "second");
//! assert_eq!(io.as_bytes(), "๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿฅท๐Ÿป๐Ÿ‘จโ€๐Ÿ’ป building ๐Ÿ”๐Ÿ”’๐Ÿ—๏ธ\0A10first\0S10second".as_bytes())
//! ```
//! An [`IOPattern`] is a UTF8-encoded string wrapper. Absorptions are marked by `A` and
//! squeezes by `S`, followed by the respective length
//! (note: length is expressed in terms of [`hash::Unit`], native elements over which the hash function works).
//! A label is added at the end of each absorb/squeeze, to describe the *type* and
//! *the variable* as used in the protocol. Operations are separated by a NULL byte and therefore labels cannot contain
//! NULL bytes themselves, nor they can start with an ASCII digit.
//!
//! # Batteries included
//! The library comes with support for algebraic objects over arkworks and zkcrypto:
//! - with feature flag `--feature=ark`, the module [`plugins::ark`] provides extension traits for arkworks fields and groups;
//! - with feature flag `--feature=group`, the module [`plugins::group`] provides extension traits for zkcrypto's field and group traits.
//! See the [`plugins`] module for more information.
//!
//!
//! # Protocol transcripts
//!
//! Prover and verifier proof transcripts are built respectively with [`Merlin`] and [`Arthur`].
//! Given the IOPattern, it is possible to build a [`Merlin`] instance that can
//! build the protocol transcript, and seed the private randomness for the prover.
//!
//! ```
//! use nimue::*;
//! use rand::Rng;
//!
//! // Create a new protocol that will absorb 1 byte and squeeze 16 bytes.
//! let io = IOPattern::<DefaultHash>::new("example-protocol ๐ŸคŒ").absorb(1, "โ†ช๏ธ").squeeze(16, "โ†ฉ๏ธ");
//! let mut merlin = io.to_merlin();
//! // The prover sends the byte 0x42.
//! merlin.add_bytes(&[0x42]).unwrap();
//! // The prover receive a 128-bit challenge.
//! let mut chal = [0u8; 16];
//! merlin.fill_challenge_bytes(&mut chal).unwrap();
//! // The transcript is recording solely the bytes sent by the prover so far.
//! assert_eq!(merlin.transcript(), [0x42]);
//! // Generate some private randomness bound to the protocol transcript.
//! let private = merlin.rng().gen::<[u8; 2]>();
//!
//! assert_eq!(merlin.transcript(), [0x42]);
//! ```
//!
//! (Note: Nimue provides aliases [`DefaultHash`] and [`DefaultRng`] mapping to secure hash functions and random number generators).
//! An [`Merlin`] instance can generate public coins (via a [`Safe`] instance) and private coins.
//! Private coins are generated with a sponge that absorbs whatever the public sponge absorbs, and is seeded by a cryptographic random number generator throughout the protocol by the prover.
//! This way, it is really hard to produce two different challenges for the same prover message.
//!
//! The verifier can use a [`Arthur`] instance to recover the protocol transcript and public coins:
//! ```
//! use nimue::{IOPattern, Arthur};
//! use nimue::hash::Keccak;
//! use nimue::traits::*;
//! use rand::{Rng, rngs::OsRng};
//!
//! let io = IOPattern::<Keccak>::new("example-protocol ๐Ÿง€").absorb(1, "in ๐Ÿฝ๏ธ").squeeze(16, "out ๐Ÿคฎ");
//! let transcript = [0x42];
//! let mut arthur = io.to_arthur(&transcript);
//!
//! // Read the first message.
//! let [first_message] = arthur.next_bytes().unwrap();
//! assert_eq!(first_message, 0x42);
//!
//! // Squeeze out randomness.
//! let chal = arthur.challenge_bytes::<16>().expect("Squeezing 128 bits");
//! ```
//!
//! # Acknowledgements
//!
//! This work is heavily inspired from:
//! - Libsignal's [shosha256], by Trevor Perrin. It provides an absorb/squeeze interface over legacy hash functions.
//! - the [SAFE] API, by Dmitry Khovratovich, JP Aumasson, Porรงu Quine, Bart Mennink. To my knowledge they are the first to introduce this idea of using an IO Pattern to build a transcript and the SAFE API.
//! - [Arthur], by Henry de Valence. To my knowledge it introduced this idea of a `Transcript` object carrying over the state of the hash function throughout the protocol.
//!
//!
//! [shosha256]: https://github.com/signalapp/libsignal/blob/main/rust/poksho/src/shosha256.rs
//! [SAFE]: https://eprint.iacr.org/2023/522
//! [Arthur]: https://github.com/dalek-cryptography/arthur
//! [`digest::Digest`]: https://docs.rs/digest/latest/digest/trait.Digest.html

#[cfg(target_endian = "big")]
compile_error!(
    r#"
This crate doesn't support big-endian targets.
"#
);

/// Prover's internal state and transcript generation.
mod merlin;
/// Built-in proof results.
mod errors;
/// Hash functions traits and implementations.
pub mod hash;
/// IO Pattern
mod iopattern;
/// Verifier state and transcript deserialization.
mod arthur;
/// APIs for common zkp libraries.
#[cfg(any(feature = "ark", feature = "group"))]
pub mod plugins;
/// SAFE API.
mod safe;
/// Unit-tests.
#[cfg(test)]
mod tests;

/// Traits for byte support.
pub mod traits;

pub use merlin::Merlin;
pub use errors::{IOPatternError, ProofError, ProofResult};
pub use hash::{DuplexHash, Unit, legacy::DigestBridge};
pub use iopattern::IOPattern;
pub use arthur::Arthur;
pub use safe::Safe;
pub use traits::*;

/// Default random number generator used ([`rand::rngs::OsRng`]).
pub type DefaultRng = rand::rngs::OsRng;

/// Default hash function used ([`hash::Keccak`]).
pub type DefaultHash = hash::Keccak;