Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ sha2 = "^0.7"
rand = "^0.4"
tiny-keccak = "^1.4"

[dev-dependencies]
hex = "^0.3"

[features]
yolocrypto = ["curve25519-dalek/yolocrypto"]
std = ["curve25519-dalek/std"]
91 changes: 0 additions & 91 deletions src/fiatshamir.rs

This file was deleted.

3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![feature(test)]
#![feature(non_ascii_idents)]
#![allow(non_snake_case)]

extern crate core;
Expand All @@ -9,7 +8,7 @@ extern crate test;
extern crate rand;
extern crate tiny_keccak;

mod fiatshamir;
mod random_oracle;

use std::iter;
use sha2::{Digest, Sha256, Sha512};
Expand Down
166 changes: 166 additions & 0 deletions src/random_oracle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use curve25519_dalek::scalar::Scalar;
use tiny_keccak::Keccak;

#[derive(Clone)]
pub struct RandomOracle {
hash: Keccak,
state: SpongeState,
}

impl RandomOracle {
/// Instantiates a new random oracle with a given label that
/// will be committed as a first message with a padding.
pub fn new(label: &[u8]) -> Self {
let mut ro = RandomOracle {
hash: Keccak::new_shake128(),
state: SpongeState::Absorbing,
};
ro.commit(label);
// makes sure the label is disambiguated from the rest of the messages.
ro.pad();
ro
}

/// Sends a message to a random oracle.
/// Each message must be less than 256 bytes long.
pub fn commit(&mut self, message: &[u8]) {
self.set_state(SpongeState::Absorbing);
let len = message.len();
if len > 255 {
panic!("Committed message must be less than 256 bytes!");
}
// we use 1-byte length prefix, hence the limitation on the message size.
let lenprefix = [len as u8; 1];
self.hash.absorb(&lenprefix);
self.hash.absorb(message);
}

/// Extracts an arbitrary-sized number of bytes as a challenge.
pub fn challenge_bytes(&mut self, mut output: &mut [u8]) {
self.set_state(SpongeState::Squeezing);
self.hash.squeeze(&mut output);
}

/// Gets a challenge in a form of a scalar by squeezing
/// 64 bytes and reducing them to a scalar.
pub fn challenge_scalar(&mut self) -> Scalar {
let mut buf = [0u8; 64];
self.challenge_bytes(&mut buf);
Scalar::from_bytes_mod_order_wide(&buf)
}

/// Ensures that the state is correct.
/// Does necessary padding+permutation if needed to transition from one state to another.
fn set_state(&mut self, newstate: SpongeState) {
if self.state != newstate {
self.pad();
self.state = newstate;
}
}

/// Pad separates the prior operations by a full permutation.
/// Each incoming message is length-prefixed anyway, but padding
/// enables pre-computing and re-using the oracle state.
fn pad(&mut self) {
// tiny_keccak's API is not very clear,
// so we'd probably need to fork and either document it, or tweak to make it more sensible.
// 1. pad() only adds keccak padding, but does not advance internal offset and
// does not perform a permutation round.
// 2. fill_block() does not pad, but resets the internal offset and does a permutation round.
match self.state {
SpongeState::Absorbing => {
self.hash.pad();
self.hash.fill_block();
}
SpongeState::Squeezing => {
// in the squeezing state we are not feeding messages,
// only reading portions of a state, so padding does not make sense.
// what we need is to perform computation and reset the internal offset to zero.
self.hash.fill_block();
}
}
}
}

#[derive(Clone,PartialEq)]
enum SpongeState {
Absorbing,
Squeezing,
}

#[cfg(test)]
mod tests {
extern crate hex;
use super::*;

#[test]
fn usage_example() {
let mut ro = RandomOracle::new(b"TestProtocol");
ro.commit(b"msg1");
ro.commit(b"msg2");
{
let mut challenge1 = [0u8; 8];
ro.challenge_bytes(&mut challenge1);
assert_eq!(hex::encode(challenge1), "7f04fadac332ce45");
}
{
let mut challenge2 = [0u8; 200];
ro.challenge_bytes(&mut challenge2);
}
{
let mut challenge3 = [0u8; 8];
ro.challenge_bytes(&mut challenge3);
assert_eq!(hex::encode(challenge3), "2cd86eb9913c0dc7");
}
ro.commit(b"msg3");
{
let mut challenge4 = [0u8; 8];
ro.challenge_bytes(&mut challenge4);
assert_eq!(hex::encode(challenge4), "383c7fc8d7bf8ad3");
}
}

#[test]
fn disambiguation() {
{
let mut ro = RandomOracle::new(b"TestProtocol");
ro.commit(b"msg1msg2");
{
let mut ch = [0u8; 8];
ro.challenge_bytes(&mut ch);
assert_eq!(hex::encode(ch), "42023e04ad4f232c");
}
}
{
let mut ro = RandomOracle::new(b"TestProtocol");
ro.commit(b"msg1");
ro.commit(b"msg2");
{
let mut ch = [0u8; 8];
ro.challenge_bytes(&mut ch);
assert_eq!(hex::encode(ch), "7f04fadac332ce45");
}
}
{
let mut ro = RandomOracle::new(b"TestProtocol");
ro.commit(b"msg");
ro.commit(b"1msg2");
{
let mut ch = [0u8; 8];
ro.challenge_bytes(&mut ch);
assert_eq!(hex::encode(ch), "dbbd832ca1fd3c2f");
}
}
{
let mut ro = RandomOracle::new(b"TestProtocol");
ro.commit(b"ms");
ro.commit(b"g1ms");
ro.commit(b"g2");
{
let mut ch = [0u8; 8];
ro.challenge_bytes(&mut ch);
assert_eq!(hex::encode(ch), "18860c017b1d28ec");
}
}
}
}