diff --git a/src/accountant.rs b/src/accountant.rs index b993965697..55cf559a8e 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -3,7 +3,9 @@ //! transfer funds to other users. use log::{Entry, Sha256Hash}; -use event::{get_pubkey, sign_transaction_data, verify_event, Event, PublicKey, Signature}; +use event::Event; +use transaction::{sign_transaction_data, Transaction}; +use signature::{get_pubkey, PublicKey, Signature}; use genesis::Genesis; use historian::{reserve_signature, Historian}; use ring::signature::Ed25519KeyPair; @@ -14,7 +16,8 @@ use std::result; #[derive(Debug, PartialEq, Eq)] pub enum AccountingError { InsufficientFunds, - InvalidEvent, + InvalidTransfer, + InvalidTransferSignature, SendError, } @@ -74,20 +77,44 @@ impl Accountant { allow_deposits && from == to } - pub fn process_event(self: &mut Self, event: Event) -> Result<()> { - if !verify_event(&event) { - return Err(AccountingError::InvalidEvent); + pub fn process_transaction(self: &mut Self, tr: Transaction) -> Result<()> { + if !tr.verify() { + return Err(AccountingError::InvalidTransfer); } - if let Event::Transaction { from, data, .. } = event { - if self.get_balance(&from).unwrap_or(0) < data { - return Err(AccountingError::InsufficientFunds); + if self.get_balance(&tr.from).unwrap_or(0) < tr.data { + return Err(AccountingError::InsufficientFunds); + } + + self.process_verified_transaction(&tr, false)?; + if let Err(SendError(_)) = self.historian.sender.send(Event::Transaction(tr)) { + return Err(AccountingError::SendError); + } + + Ok(()) + } + + fn process_verified_transaction( + self: &mut Self, + tr: &Transaction, + allow_deposits: bool, + ) -> Result<()> { + if !reserve_signature(&mut self.historian.signatures, &tr.sig) { + return Err(AccountingError::InvalidTransferSignature); + } + + if !Self::is_deposit(allow_deposits, &tr.from, &tr.to) { + if let Some(x) = self.balances.get_mut(&tr.from) { + *x -= tr.data; } } - self.process_verified_event(&event, false)?; - if let Err(SendError(_)) = self.historian.sender.send(event) { - return Err(AccountingError::SendError); + if self.balances.contains_key(&tr.to) { + if let Some(x) = self.balances.get_mut(&tr.to) { + *x += tr.data; + } + } else { + self.balances.insert(tr.to, tr.data); } Ok(()) @@ -98,26 +125,10 @@ impl Accountant { event: &Event, allow_deposits: bool, ) -> Result<()> { - if !reserve_signature(&mut self.historian.signatures, event) { - return Err(AccountingError::InvalidEvent); + match *event { + Event::Tick => Ok(()), + Event::Transaction(ref tr) => self.process_verified_transaction(tr, allow_deposits), } - - if let Event::Transaction { from, to, data, .. } = *event { - if !Self::is_deposit(allow_deposits, &from, &to) { - if let Some(x) = self.balances.get_mut(&from) { - *x -= data; - } - } - - if self.balances.contains_key(&to) { - if let Some(x) = self.balances.get_mut(&to) { - *x += data; - } - } else { - self.balances.insert(to, data); - } - } - Ok(()) } pub fn transfer( @@ -129,14 +140,14 @@ impl Accountant { let from = get_pubkey(keypair); let last_id = self.last_id; let sig = sign_transaction_data(&n, keypair, &to, &last_id); - let event = Event::Transaction { + let tr = Transaction { from, to, data: n, last_id, sig, }; - self.process_event(event).map(|_| sig) + self.process_transaction(tr).map(|_| sig) } pub fn get_balance(self: &Self, pubkey: &PublicKey) -> Option { @@ -147,7 +158,7 @@ impl Accountant { #[cfg(test)] mod tests { use super::*; - use event::{generate_keypair, get_pubkey}; + use signature::{generate_keypair, get_pubkey}; use logger::ExitReason; use genesis::Creator; diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index 069b5cd851..4c6c12a981 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -1,6 +1,7 @@ use std::io; use accountant::Accountant; -use event::{Event, PublicKey, Signature}; +use transaction::Transaction; +use signature::PublicKey; use log::{Entry, Sha256Hash}; use std::net::UdpSocket; use bincode::{deserialize, serialize}; @@ -11,22 +12,10 @@ pub struct AccountantSkel { #[derive(Serialize, Deserialize, Debug)] pub enum Request { - Transfer { - from: PublicKey, - to: PublicKey, - val: i64, - last_id: Sha256Hash, - sig: Signature, - }, - GetBalance { - key: PublicKey, - }, - GetEntries { - last_id: Sha256Hash, - }, - GetId { - is_last: bool, - }, + Transaction(Transaction), + GetBalance { key: PublicKey }, + GetEntries { last_id: Sha256Hash }, + GetId { is_last: bool }, } #[derive(Serialize, Deserialize, Debug)] @@ -43,22 +32,9 @@ impl AccountantSkel { pub fn process_request(self: &mut Self, msg: Request) -> Option { match msg { - Request::Transfer { - from, - to, - val, - last_id, - sig, - } => { - let event = Event::Transaction { - from, - to, - data: val, - last_id, - sig, - }; - if let Err(err) = self.acc.process_event(event) { - eprintln!("Transfer error: {:?}", err); + Request::Transaction(tr) => { + if let Err(err) = self.acc.process_transaction(tr) { + eprintln!("Transaction error: {:?}", err); } None } diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index 8e62addd36..4e4b6942c0 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -5,7 +5,8 @@ use std::net::UdpSocket; use std::io; use bincode::{deserialize, serialize}; -use event::{get_pubkey, get_signature, sign_transaction_data, PublicKey, Signature}; +use transaction::{sign_transaction_data, Transaction}; +use signature::{get_pubkey, PublicKey, Signature}; use log::{Entry, Sha256Hash}; use ring::signature::Ed25519KeyPair; use accountant_skel::{Request, Response}; @@ -33,13 +34,13 @@ impl AccountantStub { last_id: Sha256Hash, sig: Signature, ) -> io::Result { - let req = Request::Transfer { + let req = Request::Transaction(Transaction { from, to, - val, + data: val, last_id, sig, - }; + }); let data = serialize(&req).unwrap(); self.socket.send_to(&data, &self.addr) } @@ -108,7 +109,7 @@ impl AccountantStub { if let Response::Entries { entries } = resp { for Entry { id, event, .. } in entries { self.last_id = Some(id); - if let Some(sig) = get_signature(&event) { + if let Some(sig) = event.get_signature() { if sig == *wait_sig { return Ok(()); } diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index b54331a4c4..5a6279eef7 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -2,7 +2,9 @@ extern crate serde_json; extern crate silk; use silk::accountant_stub::AccountantStub; -use silk::event::{generate_keypair, get_pubkey, sign_transaction_data, verify_event, Event}; +use silk::event::Event; +use silk::signature::{generate_keypair, get_pubkey}; +use silk::transaction::{sign_transaction_data, Transaction}; use silk::genesis::Genesis; use std::time::Instant; use std::net::UdpSocket; @@ -47,14 +49,14 @@ fn main() { println!("Verify signatures..."); let now = Instant::now(); for &(k, s) in &sigs { - let e = Event::Transaction { + let e = Event::Transaction(Transaction { from: alice_pubkey, to: k, data: one, last_id, sig: s, - }; - assert!(verify_event(&e)); + }); + assert!(e.verify()); } let duration = now.elapsed(); let ns = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; diff --git a/src/bin/demo.rs b/src/bin/demo.rs index 4f6967e418..aa8424c94f 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -2,7 +2,9 @@ extern crate silk; use silk::historian::Historian; use silk::log::{verify_slice, Entry, Sha256Hash}; -use silk::event::{generate_keypair, get_pubkey, sign_claim_data, Event}; +use silk::signature::{generate_keypair, get_pubkey}; +use silk::transaction::sign_claim_data; +use silk::event::Event; use std::thread::sleep; use std::time::Duration; use std::sync::mpsc::SendError; diff --git a/src/bin/genesis-file-demo.rs b/src/bin/genesis-file-demo.rs index 26a47c5e8b..8a8adf5d66 100644 --- a/src/bin/genesis-file-demo.rs +++ b/src/bin/genesis-file-demo.rs @@ -2,7 +2,7 @@ extern crate serde_json; extern crate silk; use silk::genesis::{Creator, Genesis}; -use silk::event::{generate_keypair, get_pubkey}; +use silk::signature::{generate_keypair, get_pubkey}; fn main() { let alice = Creator { diff --git a/src/event.rs b/src/event.rs index 1b1fd2f386..c75b3b20d0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,30 +1,10 @@ -//! The `log` crate provides the foundational data structures for Proof-of-History, -//! an ordered log of events in time. +//! The `event` crate provides the data structures for log events. -/// Each log entry contains three pieces of data. The 'num_hashes' field is the number -/// of hashes performed since the previous entry. The 'id' field is the result -/// of hashing 'id' from the previous entry 'num_hashes' times. The 'event' -/// field points to an Event that took place shortly after 'id' was generated. -/// -/// If you divide 'num_hashes' by the amount of time it takes to generate a new hash, you -/// get a duration estimate since the last event. Since processing power increases -/// over time, one should expect the duration 'num_hashes' represents to decrease proportionally. -/// Though processing power varies across nodes, the network gives priority to the -/// fastest processor. Duration should therefore be estimated by assuming that the hash -/// was generated by the fastest processor at the time the entry was logged. - -use generic_array::GenericArray; -use generic_array::typenum::{U32, U64}; -use ring::signature::Ed25519KeyPair; -use ring::{rand, signature}; -use untrusted; +use signature::{PublicKey, Signature}; +use transaction::Transaction; use serde::Serialize; -use bincode::serialize; use log::Sha256Hash; -pub type PublicKey = GenericArray; -pub type Signature = GenericArray; - /// When 'event' is Tick, the event represents a simple clock tick, and exists for the /// sole purpose of improving the performance of event log verification. A tick can /// be generated in 'num_hashes' hashes and verified in 'num_hashes' hashes. By logging @@ -33,112 +13,25 @@ pub type Signature = GenericArray; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Event { Tick, - Transaction { - from: PublicKey, - to: PublicKey, - data: T, - last_id: Sha256Hash, - sig: Signature, - }, + Transaction(Transaction), } -impl Event { +impl Event { pub fn new_claim(to: PublicKey, data: T, last_id: Sha256Hash, sig: Signature) -> Self { - Event::Transaction { - from: to, - to, - data, - last_id, - sig, + Event::Transaction(Transaction::new_claim(to, data, last_id, sig)) + } + + pub fn get_signature(&self) -> Option { + match *self { + Event::Tick => None, + Event::Transaction(ref tr) => Some(tr.sig), + } + } + + pub fn verify(&self) -> bool { + match *self { + Event::Tick => true, + Event::Transaction(ref tr) => tr.verify(), } } } - -/// Return a new ED25519 keypair -pub fn generate_keypair() -> Ed25519KeyPair { - 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 the public key for the given keypair -pub fn get_pubkey(keypair: &Ed25519KeyPair) -> PublicKey { - GenericArray::clone_from_slice(keypair.public_key_bytes()) -} - -/// Return a signature for the given data using the private key from the given keypair. -fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { - let serialized = serialize(data).unwrap(); - GenericArray::clone_from_slice(keypair.sign(&serialized).as_ref()) -} - -/// Return a signature for the given transaction data using the private key from the given keypair. -pub fn sign_transaction_data( - data: &T, - keypair: &Ed25519KeyPair, - to: &PublicKey, - last_id: &Sha256Hash, -) -> Signature { - let from = &get_pubkey(keypair); - sign_serialized(&(from, to, data, last_id), keypair) -} - -/// Return a signature for the given data using the private key from the given keypair. -pub fn sign_claim_data( - data: &T, - keypair: &Ed25519KeyPair, - last_id: &Sha256Hash, -) -> Signature { - sign_transaction_data(data, keypair, &get_pubkey(keypair), last_id) -} - -/// 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 { - 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() -} - -pub fn get_signature(event: &Event) -> Option { - match *event { - Event::Tick => None, - Event::Transaction { sig, .. } => Some(sig), - } -} - -pub fn verify_event(event: &Event) -> bool { - if let Event::Transaction { - from, - to, - ref data, - last_id, - sig, - } = *event - { - let sign_data = serialize(&(&from, &to, &data, &last_id)).unwrap(); - if !verify_signature(&from, &sign_data, &sig) { - return false; - } - } - true -} - -#[cfg(test)] -mod tests { - use super::*; - use bincode::{deserialize, serialize}; - - #[test] - fn test_serialize_claim() { - let claim0 = Event::new_claim( - Default::default(), - 0u8, - Default::default(), - Default::default(), - ); - let buf = serialize(&claim0).unwrap(); - let claim1: Event = deserialize(&buf).unwrap(); - assert_eq!(claim1, claim0); - } -} diff --git a/src/genesis.rs b/src/genesis.rs index a30f35883a..ead4f053b1 100644 --- a/src/genesis.rs +++ b/src/genesis.rs @@ -1,6 +1,8 @@ //! A library for generating the chain's genesis block. -use event::{generate_keypair, get_pubkey, sign_transaction_data, Event, PublicKey}; +use event::Event; +use transaction::{sign_transaction_data, Transaction}; +use signature::{generate_keypair, get_pubkey, PublicKey}; use log::{create_entries, hash, Entry, Sha256Hash}; use ring::rand::SystemRandom; use ring::signature::Ed25519KeyPair; @@ -56,13 +58,13 @@ impl Genesis { let last_id = self.get_seed(); let from = self.get_pubkey(); let sig = sign_transaction_data(&data, &self.get_keypair(), to, &last_id); - Event::Transaction { + Event::Transaction(Transaction { from, to: *to, data, last_id, sig, - } + }) } pub fn create_events(&self) -> Vec> { @@ -93,7 +95,7 @@ mod tests { fn test_create_events() { let mut events = Genesis::new(100, vec![]).create_events().into_iter(); assert_eq!(events.next().unwrap(), Event::Tick); - if let Event::Transaction { from, to, .. } = events.next().unwrap() { + if let Event::Transaction(Transaction { from, to, .. }) = events.next().unwrap() { assert_eq!(from, to); } else { assert!(false); diff --git a/src/historian.rs b/src/historian.rs index 6db553b476..8af1d0424d 100644 --- a/src/historian.rs +++ b/src/historian.rs @@ -7,7 +7,8 @@ use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use std::time::Instant; use log::{hash, Entry, Sha256Hash}; use logger::{ExitReason, Logger}; -use event::{get_signature, Event, Signature}; +use signature::Signature; +use event::Event; use serde::Serialize; use std::fmt::Debug; @@ -55,13 +56,11 @@ impl Historian { } } -pub fn reserve_signature(sigs: &mut HashSet, event: &Event) -> bool { - if let Some(sig) = get_signature(&event) { - if sigs.contains(&sig) { - return false; - } - sigs.insert(sig); +pub fn reserve_signature(sigs: &mut HashSet, sig: &Signature) -> bool { + if sigs.contains(sig) { + return false; } + sigs.insert(*sig); true } @@ -70,6 +69,8 @@ mod tests { use super::*; use log::*; use event::*; + use transaction::*; + use signature::*; use std::thread::sleep; use std::time::Duration; @@ -116,10 +117,10 @@ mod tests { let data = b"hello, world"; let zero = Sha256Hash::default(); let sig = sign_claim_data(&data, &keypair, &zero); - let event0 = Event::new_claim(to, &data, zero, sig); + let tr0 = Transaction::new_claim(to, &data, zero, sig); let mut sigs = HashSet::new(); - assert!(reserve_signature(&mut sigs, &event0)); - assert!(!reserve_signature(&mut sigs, &event0)); + assert!(reserve_signature(&mut sigs, &tr0.sig)); + assert!(!reserve_signature(&mut sigs, &tr0.sig)); } #[test] diff --git a/src/lib.rs b/src/lib.rs index bf65fb2d54..91cbf8b703 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ pub mod log; pub mod logger; pub mod event; +pub mod transaction; +pub mod signature; pub mod genesis; pub mod historian; pub mod accountant; diff --git a/src/log.rs b/src/log.rs index 8d133b5b3c..7aea7cc1c6 100644 --- a/src/log.rs +++ b/src/log.rs @@ -16,7 +16,7 @@ use generic_array::GenericArray; use generic_array::typenum::U32; use serde::Serialize; -use event::{get_signature, verify_event, Event}; +use event::Event; use sha2::{Digest, Sha256}; use rayon::prelude::*; @@ -29,7 +29,7 @@ pub struct Entry { pub event: Event, } -impl Entry { +impl Entry { /// Creates a Entry from the number of hashes 'num_hashes' since the previous event /// and that resulting 'id'. pub fn new_tick(num_hashes: u64, id: &Sha256Hash) -> Self { @@ -39,6 +39,15 @@ impl Entry { event: Event::Tick, } } + + /// Verifies self.id is the result of hashing a 'start_hash' 'self.num_hashes' times. + /// If the event is not a Tick, then hash that as well. + pub fn verify(&self, start_hash: &Sha256Hash) -> bool { + if !self.event.verify() { + return false; + } + self.id == next_hash(start_hash, self.num_hashes, &self.event) + } } /// Return a Sha256 hash for the given data. @@ -64,7 +73,7 @@ pub fn next_hash( event: &Event, ) -> Sha256Hash { let mut id = *start_hash; - let sig = get_signature(event); + let sig = event.get_signature(); let start_index = if sig.is_some() { 1 } else { 0 }; for _ in start_index..num_hashes { id = hash(&id); @@ -81,7 +90,7 @@ pub fn create_entry( cur_hashes: u64, event: Event, ) -> Entry { - let sig = get_signature(&event); + let sig = event.get_signature(); let num_hashes = cur_hashes + if sig.is_some() { 1 } else { 0 }; let id = next_hash(start_hash, 0, &event); Entry { @@ -113,34 +122,25 @@ pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entr } } -/// Verifies self.id is the result of hashing a 'start_hash' 'self.num_hashes' times. -/// If the event is not a Tick, then hash that as well. -pub fn verify_entry(entry: &Entry, start_hash: &Sha256Hash) -> bool { - if !verify_event(&entry.event) { - return false; - } - entry.id == next_hash(start_hash, entry.num_hashes, &entry.event) -} - /// Verifies the hashes and counts of a slice of events are all consistent. pub fn verify_slice(events: &[Entry], start_hash: &Sha256Hash) -> bool { let genesis = [Entry::new_tick(Default::default(), start_hash)]; let event_pairs = genesis.par_iter().chain(events).zip(events); - event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.id)) + event_pairs.all(|(x0, x1)| x1.verify(&x0.id)) } /// Verifies the hashes and counts of a slice of events are all consistent. pub fn verify_slice_i64(events: &[Entry], start_hash: &Sha256Hash) -> bool { let genesis = [Entry::new_tick(Default::default(), start_hash)]; let event_pairs = genesis.par_iter().chain(events).zip(events); - event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.id)) + event_pairs.all(|(x0, x1)| x1.verify(&x0.id)) } /// Verifies the hashes and events serially. Exists only for reference. pub fn verify_slice_seq(events: &[Entry], start_hash: &Sha256Hash) -> bool { let genesis = [Entry::new_tick(0, start_hash)]; let mut event_pairs = genesis.iter().chain(events).zip(events); - event_pairs.all(|(x0, x1)| verify_entry(&x1, &x0.id)) + event_pairs.all(|(x0, x1)| x1.verify(&x0.id)) } pub fn create_entries( @@ -169,16 +169,17 @@ pub fn next_ticks(start_hash: &Sha256Hash, num_hashes: u64, len: usize) -> Vec(&Entry::new_tick(0, &zero), &zero)); // base case - assert!(!verify_entry::(&Entry::new_tick(0, &zero), &one)); // base case, bad - assert!(verify_entry::(&next_tick(&zero, 1), &zero)); // inductive step - assert!(!verify_entry::(&next_tick(&zero, 1), &one)); // inductive step, bad + assert!(Entry::::new_tick(0, &zero).verify(&zero)); // base case + assert!(!Entry::::new_tick(0, &zero).verify(&one)); // base case, bad + assert!(next_tick::(&zero, 1).verify(&zero)); // inductive step + assert!(!next_tick::(&zero, 1).verify(&one)); // inductive step, bad } #[test] @@ -277,13 +278,13 @@ mod tests { let keypair1 = generate_keypair(); let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); - let event0 = Event::Transaction { + let event0 = Event::Transaction(Transaction { from: get_pubkey(&keypair0), to: pubkey1, data, last_id: zero, sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), - }; + }); let entries = create_entries(&zero, vec![event0]); assert!(verify_slice(&entries, &zero)); } @@ -295,13 +296,13 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let zero = Sha256Hash::default(); - let event0 = Event::Transaction { + let event0 = Event::Transaction(Transaction { from: get_pubkey(&keypair0), to: pubkey1, data: hash(b"goodbye cruel world"), // <-- attack! last_id: zero, sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), - }; + }); let entries = create_entries(&zero, vec![event0]); assert!(!verify_slice(&entries, &zero)); } @@ -314,13 +315,13 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let zero = Sha256Hash::default(); - let event0 = Event::Transaction { + let event0 = Event::Transaction(Transaction { from: get_pubkey(&keypair0), to: get_pubkey(&thief_keypair), // <-- attack! data: hash(b"goodbye cruel world"), last_id: zero, sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero), - }; + }); let entries = create_entries(&zero, vec![event0]); assert!(!verify_slice(&entries, &zero)); } diff --git a/src/logger.rs b/src/logger.rs index 130f746602..0b132a6a0f 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -80,6 +80,8 @@ mod tests { use super::*; use log::*; use event::*; + use signature::*; + use transaction::*; #[test] fn test_bad_event_signature() { @@ -92,6 +94,6 @@ mod tests { zero, sig, ); - assert!(!verify_event(&event0)); + assert!(!event0.verify()); } } diff --git a/src/signature.rs b/src/signature.rs new file mode 100644 index 0000000000..c363252625 --- /dev/null +++ b/src/signature.rs @@ -0,0 +1,30 @@ +//! The `signature` crate provides functionality for public and private keys + +use generic_array::GenericArray; +use generic_array::typenum::{U32, U64}; +use ring::signature::Ed25519KeyPair; +use ring::{rand, signature}; +use untrusted; + +pub type PublicKey = GenericArray; +pub type Signature = GenericArray; + +/// Return a new ED25519 keypair +pub fn generate_keypair() -> Ed25519KeyPair { + 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 the public key for the given keypair +pub fn get_pubkey(keypair: &Ed25519KeyPair) -> PublicKey { + GenericArray::clone_from_slice(keypair.public_key_bytes()) +} + +/// 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 { + 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() +} diff --git a/src/transaction.rs b/src/transaction.rs new file mode 100644 index 0000000000..ee67a58a8b --- /dev/null +++ b/src/transaction.rs @@ -0,0 +1,77 @@ +//! The `transaction` crate provides functionality for creating log transactions. + +use signature::{get_pubkey, verify_signature, PublicKey, Signature}; +use ring::signature::Ed25519KeyPair; +use serde::Serialize; +use bincode::serialize; +use log::Sha256Hash; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Transaction { + pub from: PublicKey, + pub to: PublicKey, + pub data: T, + pub last_id: Sha256Hash, + pub sig: Signature, +} + +impl Transaction { + pub fn new_claim(to: PublicKey, data: T, last_id: Sha256Hash, sig: Signature) -> Self { + Transaction { + from: to, + to, + data, + last_id, + sig, + } + } + + pub fn verify(&self) -> bool { + let sign_data = serialize(&(&self.from, &self.to, &self.data, &self.last_id)).unwrap(); + verify_signature(&self.from, &sign_data, &self.sig) + } +} + +fn sign_serialized(data: &T, keypair: &Ed25519KeyPair) -> Signature { + let serialized = serialize(data).unwrap(); + Signature::clone_from_slice(keypair.sign(&serialized).as_ref()) +} + +/// Return a signature for the given transaction data using the private key from the given keypair. +pub fn sign_transaction_data( + data: &T, + keypair: &Ed25519KeyPair, + to: &PublicKey, + last_id: &Sha256Hash, +) -> Signature { + let from = &get_pubkey(keypair); + sign_serialized(&(from, to, data, last_id), keypair) +} + +/// Return a signature for the given data using the private key from the given keypair. +pub fn sign_claim_data( + data: &T, + keypair: &Ed25519KeyPair, + last_id: &Sha256Hash, +) -> Signature { + sign_transaction_data(data, keypair, &get_pubkey(keypair), last_id) +} + +#[cfg(test)] +mod tests { + use super::*; + use bincode::{deserialize, serialize}; + + #[test] + fn test_serialize_claim() { + let claim0 = Transaction::new_claim( + Default::default(), + 0u8, + Default::default(), + Default::default(), + ); + let buf = serialize(&claim0).unwrap(); + let claim1: Transaction = deserialize(&buf).unwrap(); + assert_eq!(claim1, claim0); + } +}