use 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
}
#[derive(Clone, Zeroize)]
pub struct Transcript {
strobe: Strobe128,
}
impl Transcript {
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
}
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(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);
}
pub fn append_u64(&mut self, label: &'static [u8], x: u64) {
self.append_message(label, &encode_u64(x));
}
#[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);
}
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));
}
}
pub fn build_rng(&self) -> TranscriptRngBuilder {
TranscriptRngBuilder {
strobe: self.strobe.clone(),
}
}
}
pub struct TranscriptRngBuilder {
strobe: Strobe128,
}
impl TranscriptRngBuilder {
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(
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)
}
pub fn finalize<R>(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,
}
}
}
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::*;
struct TestTranscript {
state: Strobe,
}
impl TestTranscript {
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
}
pub fn append_message(&mut self, label: &[u8], message: &[u8]) {
let mut metadata: Vec<u8> = 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);
}
pub fn challenge_bytes(&mut self, label: &[u8], dest: &mut [u8]) {
let prf_len = dest.len();
let mut metadata: Vec<u8> = 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]
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]
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;
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);
assert_ne!(s1, s2);
assert_ne!(s1, s3);
assert_ne!(s1, s4);
assert_ne!(s2, s3);
assert_ne!(s2, s4);
assert_eq!(s3, s4);
}
}