Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ sha2-asm = {version="0.3", optional=true}
generic-array = { version = "0.9.0", default-features = false, features = ["serde"] }
serde = "1.0.27"
serde_derive = "1.0.27"
ring = "0.12.1"
untrusted = "0.5.1"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use std::sync::mpsc::SendError;

fn create_log(hist: &Historian) -> Result<(), SendError<Event>> {
sleep(Duration::from_millis(15));
hist.sender.send(Event::UserDataKey(Sha256Hash::default()))?;
hist.sender.send(Event::Discovery(Sha256Hash::default()))?;
sleep(Duration::from_millis(10));
Ok(())
}
Expand All @@ -62,7 +62,7 @@ Running the program should produce a log similar to:

```rust
Entry { num_hashes: 0, end_hash: [0, ...], event: Tick }
Entry { num_hashes: 2, end_hash: [67, ...], event: UserDataKey(3735928559) }
Entry { num_hashes: 2, end_hash: [67, ...], event: Discovery(3735928559) }
Entry { num_hashes: 3, end_hash: [123, ...], event: Tick }
```

Expand Down
6 changes: 3 additions & 3 deletions diagrams/historian.msc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ msc {
logger=>historian [ label = "e0 = Entry{hash: h0, n: 0, event: Tick}" ] ;
logger=>logger [ label = "h1 = hash(h0)" ] ;
logger=>logger [ label = "h2 = hash(h1)" ] ;
client=>historian [ label = "UserData(d0)" ] ;
historian=>logger [ label = "UserData(d0)" ] ;
client=>historian [ label = "Discovery(d0)" ] ;
historian=>logger [ label = "Discovery(d0)" ] ;
logger=>logger [ label = "h3 = hash(h2 + d0)" ] ;
logger=>historian [ label = "e1 = Entry{hash: hash(h3), n: 2, event: UserData(d0)}" ] ;
logger=>historian [ label = "e1 = Entry{hash: hash(h3), n: 2, event: Discovery(d0)}" ] ;
logger=>logger [ label = "h4 = hash(h3)" ] ;
logger=>logger [ label = "h5 = hash(h4)" ] ;
logger=>logger [ label = "h6 = hash(h5)" ] ;
Expand Down
2 changes: 1 addition & 1 deletion src/bin/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::sync::mpsc::SendError;

fn create_log(hist: &Historian) -> Result<(), SendError<Event>> {
sleep(Duration::from_millis(15));
hist.sender.send(Event::UserDataKey(Sha256Hash::default()))?;
hist.sender.send(Event::Discovery(Sha256Hash::default()))?;
sleep(Duration::from_millis(10));
Ok(())
}
Expand Down
10 changes: 4 additions & 6 deletions src/historian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use std::thread::JoinHandle;
use std::sync::mpsc::{Receiver, Sender};
use std::time::{Duration, SystemTime};
use log::{extend_and_hash, hash, Entry, Event, Sha256Hash};
use log::{hash, hash_event, Entry, Event, Sha256Hash};

pub struct Historian {
pub sender: Sender<Event>,
Expand All @@ -27,9 +27,7 @@ fn log_event(
end_hash: &mut Sha256Hash,
event: Event,
) -> Result<(), (Entry, ExitReason)> {
if let Event::UserDataKey(key) = event {
*end_hash = extend_and_hash(end_hash, &key);
}
*end_hash = hash_event(end_hash, &event);
let entry = Entry {
end_hash: *end_hash,
num_hashes: *num_hashes,
Expand Down Expand Up @@ -139,7 +137,7 @@ mod tests {

hist.sender.send(Event::Tick).unwrap();
sleep(Duration::new(0, 1_000_000));
hist.sender.send(Event::UserDataKey(zero)).unwrap();
hist.sender.send(Event::Discovery(zero)).unwrap();
sleep(Duration::new(0, 1_000_000));
hist.sender.send(Event::Tick).unwrap();

Expand Down Expand Up @@ -173,7 +171,7 @@ mod tests {
let zero = Sha256Hash::default();
let hist = Historian::new(&zero, Some(20));
sleep(Duration::from_millis(30));
hist.sender.send(Event::UserDataKey(zero)).unwrap();
hist.sender.send(Event::Discovery(zero)).unwrap();
sleep(Duration::from_millis(15));
drop(hist.sender);
assert_eq!(
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ pub mod log;
pub mod historian;
extern crate generic_array;
extern crate rayon;
extern crate ring;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate sha2;
extern crate untrusted;
123 changes: 110 additions & 13 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
/// was generated by the fastest processor at the time the entry was logged.

use generic_array::GenericArray;
use generic_array::typenum::U32;
use generic_array::typenum::{U32, U64};
use ring::signature::Ed25519KeyPair;
pub type Sha256Hash = GenericArray<u8, U32>;
pub type PublicKey = GenericArray<u8, U32>;
pub type Signature = GenericArray<u8, U64>;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Entry {
Expand All @@ -32,7 +35,12 @@ pub struct Entry {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Event {
Tick,
UserDataKey(Sha256Hash),
Discovery(Sha256Hash),
Claim {
key: PublicKey,
data: Sha256Hash,
sig: Signature,
},
}

impl Entry {
Expand All @@ -47,12 +55,30 @@ impl Entry {
}

/// Verifies self.end_hash is the result of hashing a 'start_hash' 'self.num_hashes' times.
/// If the event is a UserDataKey, then hash that as well.
/// If the event is not a Tick, then hash that as well.
pub fn verify(self: &Self, start_hash: &Sha256Hash) -> bool {
if let Event::Claim { key, data, sig } = self.event {
if !verify_signature(&key, &data, &sig) {
return false;
}
}
self.end_hash == next_hash(start_hash, self.num_hashes, &self.event)
}
}

/// Return a Claim Event for the given hash and key-pair.
pub fn sign_hash(data: &Sha256Hash, key_pair: &Ed25519KeyPair) -> Event {
let sig = key_pair.sign(data);
let peer_public_key_bytes = key_pair.public_key_bytes();
let sig_bytes = sig.as_ref();
Event::Claim {
key: GenericArray::clone_from_slice(peer_public_key_bytes),
data: GenericArray::clone_from_slice(data),
sig: GenericArray::clone_from_slice(sig_bytes),
}
}

/// Return a Sha256 hash for the given data.
pub fn hash(val: &[u8]) -> Sha256Hash {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::default();
Expand All @@ -61,21 +87,32 @@ pub fn hash(val: &[u8]) -> Sha256Hash {
}

/// Return the hash of the given hash extended with the given value.
pub fn extend_and_hash(end_hash: &Sha256Hash, val: &[u8]) -> Sha256Hash {
pub fn extend_and_hash(end_hash: &Sha256Hash, ty: u8, val: &[u8]) -> Sha256Hash {
let mut hash_data = end_hash.to_vec();
hash_data.push(ty);
hash_data.extend_from_slice(val);
hash(&hash_data)
}

pub fn hash_event(end_hash: &Sha256Hash, event: &Event) -> Sha256Hash {
match *event {
Event::Tick => *end_hash,
Event::Discovery(data) => extend_and_hash(end_hash, 1, &data),
Event::Claim { key, data, sig } => {
let mut event_data = data.to_vec();
event_data.extend_from_slice(&sig);
event_data.extend_from_slice(&key);
extend_and_hash(end_hash, 2, &event_data)
}
}
}

pub fn next_hash(start_hash: &Sha256Hash, num_hashes: u64, event: &Event) -> Sha256Hash {
let mut end_hash = *start_hash;
for _ in 0..num_hashes {
end_hash = hash(&end_hash);
}
if let Event::UserDataKey(key) = *event {
return extend_and_hash(&end_hash, &key);
}
end_hash
hash_event(&end_hash, event)
}

/// Creates the next Tick Entry 'num_hashes' after 'start_hash'.
Expand Down Expand Up @@ -107,6 +144,16 @@ pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Hash) -> bool {
event_pairs.all(|(x0, x1)| x1.verify(&x0.end_hash))
}

/// Verify a signed message with the given public key.
pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_bytes: &[u8]) -> bool {
use untrusted;
use ring::signature;
let peer_public_key = untrusted::Input::from(peer_public_key_bytes);
let msg = untrusted::Input::from(msg_bytes);
let sig = untrusted::Input::from(sig_bytes);
signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok()
}

/// Create a vector of Ticks of length 'len' from 'start_hash' hash and 'num_hashes'.
pub fn create_ticks(start_hash: &Sha256Hash, num_hashes: u64, len: usize) -> Vec<Entry> {
use std::iter;
Expand Down Expand Up @@ -169,9 +216,9 @@ mod tests {
let zero = Sha256Hash::default();
let one = hash(&zero);

// First, verify UserData events
// First, verify Discovery events
let mut end_hash = zero;
let events = [Event::UserDataKey(zero), Event::UserDataKey(one)];
let events = [Event::Discovery(zero), Event::Discovery(one)];
let mut entries: Vec<Entry> = events
.iter()
.map(|event| {
Expand All @@ -180,16 +227,66 @@ mod tests {
entry
})
.collect();
assert!(verify_slice(&entries, &zero)); // inductive step
assert!(verify_slice(&entries, &zero));

// Next, swap only two UserData events and ensure verification fails.
// Next, swap two Discovery events and ensure verification fails.
let event0 = entries[0].event.clone();
let event1 = entries[1].event.clone();
entries[0].event = event1;
entries[1].event = event0;
assert!(!verify_slice(&entries, &zero)); // inductive step
assert!(!verify_slice(&entries, &zero));
}

#[test]
fn test_signature() {
use untrusted;
use ring::{rand, signature};
let rng = rand::SystemRandom::new();
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
let key_pair =
signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap();
const MESSAGE: &'static [u8] = b"hello, world";
let event0 = sign_hash(&hash(MESSAGE), &key_pair);
let zero = Sha256Hash::default();
let mut end_hash = zero;
let entries: Vec<Entry> = [event0]
.iter()
.map(|event| {
let entry = next_entry(&end_hash, 0, event.clone());
end_hash = entry.end_hash;
entry
})
.collect();
assert!(verify_slice(&entries, &zero));
}

#[test]
fn test_bad_signature() {
use untrusted;
use ring::{rand, signature};
let rng = rand::SystemRandom::new();
let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
let key_pair =
signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap();
const MESSAGE: &'static [u8] = b"hello, world";
let mut event0 = sign_hash(&hash(MESSAGE), &key_pair);
if let Event::Claim { key, sig, .. } = event0 {
const GOODBYE: &'static [u8] = b"goodbye cruel world";
let data = hash(GOODBYE);
event0 = Event::Claim { key, data, sig };
}
let zero = Sha256Hash::default();
let mut end_hash = zero;
let entries: Vec<Entry> = [event0]
.iter()
.map(|event| {
let entry = next_entry(&end_hash, 0, event.clone());
end_hash = entry.end_hash;
entry
})
.collect();
assert!(!verify_slice(&entries, &zero));
}
}

#[cfg(all(feature = "unstable", test))]
Expand Down