From b02eab57d26832e442723cb8536496518bd277f1 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Mon, 26 Feb 2018 11:01:19 -0700 Subject: [PATCH] Extend the event log with a Transaction event to transfer possession This implementation assumes 'from' is the current owner of 'data'. Once that's verified, the signature ensures that nobody modified 'data' (the asset being transferred) or 'to' the entity taking ownership. Fixes #14 --- src/log.rs | 193 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 55 deletions(-) diff --git a/src/log.rs b/src/log.rs index 2fc8c0e7a1b57b..752f305d99845c 100644 --- a/src/log.rs +++ b/src/log.rs @@ -43,6 +43,12 @@ pub enum Event { data: Sha256Hash, sig: Signature, }, + Transaction { + from: PublicKey, + to: PublicKey, + data: Sha256Hash, + sig: Signature, + }, } impl Entry { @@ -64,14 +70,36 @@ impl Entry { return false; } } + if let Event::Transaction { + from, + to, + data, + sig, + } = self.event + { + let mut sign_data = data.to_vec(); + sign_data.extend_from_slice(&to); + if !verify_signature(&from, &sign_data, &sig) { + return false; + } + } self.end_hash == next_hash(start_hash, self.num_hashes, &self.event) } } +// Return a new ED25519 keypair +pub fn generate_keypair() -> Ed25519KeyPair { + use ring::{rand, signature}; + use untrusted; + let rng = rand::SystemRandom::new(); + let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); + signature::Ed25519KeyPair::from_pkcs8(untrusted::Input::from(&pkcs8_bytes)).unwrap() +} + /// 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(); +pub fn sign_hash(data: &Sha256Hash, keypair: &Ed25519KeyPair) -> Event { + let sig = keypair.sign(data); + let peer_public_key_bytes = keypair.public_key_bytes(); let sig_bytes = sig.as_ref(); Event::Claim { key: GenericArray::clone_from_slice(peer_public_key_bytes), @@ -80,6 +108,21 @@ pub fn sign_hash(data: &Sha256Hash, key_pair: &Ed25519KeyPair) -> Event { } } +/// Return a Transaction Event that indicates a transfer in ownership of the given hash. +pub fn transfer_hash(data: &Sha256Hash, keypair: &Ed25519KeyPair, to: PublicKey) -> Event { + let from_public_key_bytes = keypair.public_key_bytes(); + let mut sign_data = data.to_vec(); + sign_data.extend_from_slice(&to); + let sig = keypair.sign(&sign_data); + let sig_bytes = sig.as_ref(); + Event::Transaction { + from: GenericArray::clone_from_slice(from_public_key_bytes), + to, + 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}; @@ -106,6 +149,18 @@ pub fn hash_event(end_hash: &Sha256Hash, event: &Event) -> Sha256Hash { event_data.extend_from_slice(&key); extend_and_hash(end_hash, 2, &event_data) } + Event::Transaction { + from, + to, + data, + sig, + } => { + let mut event_data = data.to_vec(); + event_data.extend_from_slice(&sig); + event_data.extend_from_slice(&from); + event_data.extend_from_slice(&to); + extend_and_hash(end_hash, 2, &event_data) + } } } @@ -126,6 +181,12 @@ pub fn next_entry(start_hash: &Sha256Hash, num_hashes: u64, event: Event) -> Ent } } +pub fn next_entry_mut(start_hash: &mut Sha256Hash, num_hashes: u64, event: Event) -> Entry { + let entry = next_entry(start_hash, num_hashes, event); + *start_hash = entry.end_hash; + entry +} + /// Creates the next Tick Entry 'num_hashes' after 'start_hash'. pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entry { next_entry(start_hash, num_hashes, Event::Tick) @@ -156,17 +217,21 @@ pub fn verify_signature(peer_public_key_bytes: &[u8], msg_bytes: &[u8], sig_byte signature::verify(&signature::ED25519, peer_public_key, msg, sig).is_ok() } +pub fn create_entries(start_hash: &Sha256Hash, num_hashes: u64, events: &[Event]) -> Vec { + let mut end_hash = *start_hash; + events + .iter() + .map(|event| next_entry_mut(&mut end_hash, num_hashes, event.clone())) + .collect() +} + /// 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 { use std::iter; let mut end_hash = *start_hash; iter::repeat(Event::Tick) .take(len) - .map(|event| { - let entry = next_entry(&end_hash, num_hashes, event); - end_hash = entry.end_hash; - entry - }) + .map(|event| next_entry_mut(&mut end_hash, num_hashes, event)) .collect() } @@ -219,19 +284,11 @@ mod tests { let one = hash(&zero); // First, verify Discovery events - let mut end_hash = zero; let events = [ Event::Discovery { data: zero }, Event::Discovery { data: one }, ]; - let mut entries: Vec = events - .iter() - .map(|event| { - let entry = next_entry(&end_hash, 0, event.clone()); - end_hash = entry.end_hash; - entry - }) - .collect(); + let mut entries = create_entries(&zero, 0, &events); assert!(verify_slice(&entries, &zero)); // Next, swap two Discovery events and ensure verification fails. @@ -243,53 +300,79 @@ mod tests { } #[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); + fn test_claim() { + let keypair = generate_keypair(); + let event0 = sign_hash(&hash(b"hello, world"), &keypair); let zero = Sha256Hash::default(); - let mut end_hash = zero; - let entries: Vec = [event0] - .iter() - .map(|event| { - let entry = next_entry(&end_hash, 0, event.clone()); - end_hash = entry.end_hash; - entry - }) - .collect(); + let entries = create_entries(&zero, 0, &[event0]); 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); + fn test_wrong_data_claim_attack() { + let keypair = generate_keypair(); + let mut event0 = sign_hash(&hash(b"hello, world"), &keypair); if let Event::Claim { key, sig, .. } = event0 { - const GOODBYE: &'static [u8] = b"goodbye cruel world"; - let data = hash(GOODBYE); + let data = hash(b"goodbye cruel world"); event0 = Event::Claim { key, data, sig }; } let zero = Sha256Hash::default(); - let mut end_hash = zero; - let entries: Vec = [event0] - .iter() - .map(|event| { - let entry = next_entry(&end_hash, 0, event.clone()); - end_hash = entry.end_hash; - entry - }) - .collect(); + let entries = create_entries(&zero, 0, &[event0]); + assert!(!verify_slice(&entries, &zero)); + } + + #[test] + fn test_transfer() { + let keypair0 = generate_keypair(); + let keypair1 = generate_keypair(); + let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); + let event0 = transfer_hash(&hash(b"hello, world"), &keypair0, pubkey1); + let zero = Sha256Hash::default(); + let entries = create_entries(&zero, 0, &[event0]); + assert!(verify_slice(&entries, &zero)); + } + + #[test] + fn test_wrong_data_transfer_attack() { + let keypair0 = generate_keypair(); + let keypair1 = generate_keypair(); + let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); + let mut event0 = transfer_hash(&hash(b"hello, world"), &keypair0, pubkey1); + if let Event::Transaction { from, to, sig, .. } = event0 { + let data = hash(b"goodbye cruel world"); + event0 = Event::Transaction { + from, + to, + data, + sig, + }; + } + let zero = Sha256Hash::default(); + let entries = create_entries(&zero, 0, &[event0]); + assert!(!verify_slice(&entries, &zero)); + } + + #[test] + fn test_transfer_hijack_attack() { + let keypair0 = generate_keypair(); + let keypair1 = generate_keypair(); + let pubkey1 = GenericArray::clone_from_slice(keypair1.public_key_bytes()); + let mut event0 = transfer_hash(&hash(b"hello, world"), &keypair0, pubkey1); + if let Event::Transaction { + from, data, sig, .. + } = event0 + { + let theif_keypair = generate_keypair(); + let to = GenericArray::clone_from_slice(theif_keypair.public_key_bytes()); + event0 = Event::Transaction { + from, + to, + data, + sig, + }; + } + let zero = Sha256Hash::default(); + let entries = create_entries(&zero, 0, &[event0]); assert!(!verify_slice(&entries, &zero)); } }