Merge pull request #22 from garious/add-transaction
Extend the event log with a Transaction event to transfer possession
This commit is contained in:
commit
05a27cb34d
193
src/log.rs
193
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<Entry> {
|
||||
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<Entry> {
|
||||
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<Entry> = 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<Entry> = [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<Entry> = [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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue