Skip to content

Commit

Permalink
Merge branch 'newtype-key'
Browse files Browse the repository at this point in the history
Use a new "Key" type to wrap the master key (derived from the SPAKE2
exchange), for better typechecking.

refs #32
  • Loading branch information
warner committed May 26, 2018
2 parents 504f78a + f18861d commit 94be84e
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 51 deletions.
3 changes: 2 additions & 1 deletion core/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use events::Key;
use hex;
use std::collections::HashMap;
use std::error::Error;
Expand Down Expand Up @@ -119,7 +120,7 @@ pub enum APIAction {
// from WormholeCore out through IO glue to application
GotWelcome(HashMap<String, String>), // actually anything JSON-able: Value
GotCode(String), // must be easy to canonically encode into UTF-8 bytes
GotUnverifiedKey(Vec<u8>),
GotUnverifiedKey(Key),
GotVerifier(Vec<u8>),
GotVersions(HashMap<String, String>), // actually anything JSON-able
GotMessage(Vec<u8>),
Expand Down
2 changes: 1 addition & 1 deletion core/src/boss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl BossMachine {
use events::BossEvent::*;
match event {
GotCode(code) => self.got_code(&code),
GotKey(key) => events![APIAction::GotUnverifiedKey(key)],
GotKey(key) => events![APIAction::GotUnverifiedKey(key.clone())],
Happy => self.happy(),
GotVerifier(verifier) => events![APIAction::GotVerifier(verifier)],
GotMessage(phase, plaintext) => self.got_message(&phase, plaintext),
Expand Down
21 changes: 18 additions & 3 deletions core/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
use hex;
use std::fmt;
use std::iter::FromIterator;
use std::ops::Deref;
use std::sync::Arc;
// Events come into the core, Actions go out of it (to the IO glue layer)
use api::{APIAction, IOAction, Mood};
use util::maybe_utf8;

pub use wordlist::Wordlist;

#[derive(PartialEq, Eq, Clone)]
pub struct Key(pub Vec<u8>);
impl Deref for Key {
type Target = Vec<u8>;
fn deref(&self) -> &Vec<u8> {
&self.0
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Key(REDACTED)")
}
}

// machines (or IO, or the API) emit these events, and each is routed to a
// specific machine (or IO or the API)
#[allow(dead_code)] // TODO: Drop dead code directive once core is complete
Expand All @@ -26,7 +41,7 @@ pub enum BossEvent {
Error,
Closed,
GotCode(String),
GotKey(Vec<u8>), // TODO: fixed length?
GotKey(Key), // TODO: fixed length?
Scared,
Happy,
GotVerifier(Vec<u8>), // TODO: fixed length (sha256)
Expand Down Expand Up @@ -174,7 +189,7 @@ impl fmt::Debug for OrderEvent {
#[derive(PartialEq)]
pub enum ReceiveEvent {
GotMessage(String, String, Vec<u8>), // side, phase, body
GotKey(Vec<u8>), // key
GotKey(Key),
}

impl fmt::Debug for ReceiveEvent {
Expand Down Expand Up @@ -235,7 +250,7 @@ impl fmt::Debug for RendezvousEvent {
#[derive(PartialEq)]
pub enum SendEvent {
Send(String, Vec<u8>), // phase, plaintext
GotVerifiedKey(Vec<u8>),
GotVerifiedKey(Key),
}

impl fmt::Debug for SendEvent {
Expand Down
31 changes: 15 additions & 16 deletions core/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use sodiumoxide::crypto::secretbox;
use spake2::{Ed25519Group, SPAKE2};
use std::mem;

use events::Events;
use events::{Events, Key};
use util;
// we process these
use events::KeyEvent;
Expand All @@ -22,7 +22,7 @@ enum State {
S0KnowNothing,
S1KnowCode(SPAKE2<Ed25519Group>), // pake_state
S2KnowPake(Vec<u8>), // their_pake
S3KnowBoth(Vec<u8>), // key
S3KnowBoth(Key), // key
#[allow(dead_code)] // TODO: if PAKE is somehow bad, land here
S4Scared,
}
Expand Down Expand Up @@ -72,7 +72,7 @@ impl KeyMachine {
S1KnowCode(_) => panic!("already got code"),
S2KnowPake(ref their_pake_msg) => {
let (pake_state, pake_msg_ser) = start_pake(&code, &self.appid);
let key = finish_pake(pake_state, their_pake_msg.clone());
let key: Key = finish_pake(pake_state, their_pake_msg.clone());
let versions = json!({"app_versions": {}}); // TODO: self.versions
let (version_phase, version_msg) =
build_version_msg(&self.side, &key, &versions);
Expand All @@ -99,7 +99,7 @@ impl KeyMachine {
events![]
}
S1KnowCode(pake_state) => {
let key = finish_pake(pake_state, pake);
let key: Key = finish_pake(pake_state, pake);
let versions = json!({"app_versions": {}}); // TODO: self.versions
let (version_phase, version_msg) =
build_version_msg(&self.side, &key, &versions);
Expand Down Expand Up @@ -131,21 +131,20 @@ fn start_pake(appid: &str, code: &str) -> (SPAKE2<Ed25519Group>, Vec<u8>) {
(pake_state, pake_msg_ser)
}

fn finish_pake(pake_state: SPAKE2<Ed25519Group>, peer_msg: Vec<u8>) -> Vec<u8> {
fn finish_pake(pake_state: SPAKE2<Ed25519Group>, peer_msg: Vec<u8>) -> Key {
let msg2 = extract_pake_msg(peer_msg).unwrap();
let key = pake_state
Key(pake_state
.finish(&hex::decode(msg2).unwrap())
.unwrap();
key
.unwrap())
}

fn build_version_msg(
side: &str,
key: &Vec<u8>,
key: &Key,
versions: &Value,
) -> (String, Vec<u8>) {
let phase = "version";
let data_key = derive_phase_key(side, key, &phase);
let data_key = derive_phase_key(side, &key, &phase);
let plaintext = versions.to_string();
let (_nonce, encrypted) = encrypt_data(data_key, &plaintext.as_bytes());
(phase.to_string(), encrypted)
Expand Down Expand Up @@ -196,7 +195,7 @@ pub fn derive_key(key: &[u8], purpose: &[u8], length: usize) -> Vec<u8> {
hk.expand(purpose, length)
}

pub fn derive_phase_key(side: &str, key: &[u8], phase: &str) -> Vec<u8> {
pub fn derive_phase_key(side: &str, key: &Key, phase: &str) -> Vec<u8> {
let side_bytes = side.as_bytes();
let phase_bytes = phase.as_bytes();
let side_digest: Vec<u8> = sha256_digest(side_bytes)
Expand All @@ -216,7 +215,7 @@ pub fn derive_phase_key(side: &str, key: &[u8], phase: &str) -> Vec<u8> {
purpose_vec.extend(phase_digest);

let length = sodiumoxide::crypto::secretbox::KEYBYTES;
derive_key(key, &purpose_vec, length)
derive_key(&key.to_vec(), &purpose_vec, length)
}

#[cfg(test)]
Expand Down Expand Up @@ -246,10 +245,10 @@ mod test {
// hexlified output: fe9315729668a6278a97449dc99a5f4c2102a668c6853338152906bb75526a96
let _k = KeyMachine::new("appid1", "side");

let key = "key".as_bytes();
let key = Key("key".as_bytes().to_vec());
let side = "side";
let phase = "phase1";
let phase1_key = derive_phase_key(side, key, phase);
let phase1_key = derive_phase_key(side, &key, phase);

assert_eq!(
hex::encode(phase1_key),
Expand All @@ -261,10 +260,10 @@ mod test {
fn test_encrypt_data_decrypt_data_roundtrip() {
use super::*;

let key = "key".as_bytes();
let key = Key("key".as_bytes().to_vec());
let side = "side";
let phase = "phase";
let data_key = derive_phase_key(side, key, phase);
let data_key = derive_phase_key(side, &key, phase);
let plaintext = "hello world";

let (_nonce, encrypted) =
Expand Down
20 changes: 10 additions & 10 deletions core/src/receive.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use events::Events;
use events::{Events, Key};
use key;
use std::str;
// we process these
Expand All @@ -12,8 +12,8 @@ use events::SendEvent::GotVerifiedKey as S_GotVerifiedKey;
#[derive(Debug, PartialEq)]
enum State {
S0UnknownKey,
S1UnverifiedKey(Vec<u8>),
S2VerifiedKey(Vec<u8>),
S1UnverifiedKey(Key),
S2VerifiedKey(Key),
S3Scared,
}

Expand Down Expand Up @@ -51,13 +51,13 @@ impl ReceiveMachine {
use events::ReceiveEvent::*;
match event {
GotMessage(..) => panic!(),
GotKey(key) => (State::S1UnverifiedKey(key), events![]),
GotKey(key) => (State::S1UnverifiedKey(key.clone()), events![]),
}
}

fn derive_key_and_decrypt(
side: &str,
key: &[u8],
key: &Key,
phase: &str,
body: Vec<u8>,
) -> Option<Vec<u8>> {
Expand All @@ -68,7 +68,7 @@ impl ReceiveMachine {

fn in_unverified_key(
&self,
key: &[u8],
key: &Key,
event: ReceiveEvent,
) -> (State, Events) {
use events::ReceiveEvent::*;
Expand All @@ -81,9 +81,9 @@ impl ReceiveMachine {
let msg =
key::derive_key(&key, b"wormhole:verifier", 32); // TODO: replace 32 with KEY_SIZE const
(
State::S2VerifiedKey(key.to_vec()),
State::S2VerifiedKey(key.clone()),
events![
S_GotVerifiedKey(key.to_vec()),
S_GotVerifiedKey(key.clone()),
B_Happy,
B_GotVerifier(msg),
B_GotMessage(phase, plaintext)
Expand All @@ -101,7 +101,7 @@ impl ReceiveMachine {

fn in_verified_key(
&self,
key: &[u8],
key: &Key,
event: ReceiveEvent,
) -> (State, Events) {
use events::ReceiveEvent::*;
Expand All @@ -112,7 +112,7 @@ impl ReceiveMachine {
Some(plaintext) => {
// got_message_good
(
State::S2VerifiedKey(key.to_vec()),
State::S2VerifiedKey(key.clone()),
events![B_GotMessage(phase, plaintext)],
)
}
Expand Down
33 changes: 13 additions & 20 deletions core/src/send.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use events::Events;
use events::{Events, Key};
use key;
// we process these
use events::SendEvent;
Expand All @@ -9,14 +9,13 @@ use events::MailboxEvent::AddMessage as M_AddMessage;
pub struct SendMachine {
state: State,
side: String,
key: Vec<u8>,
queue: Vec<(String, Vec<u8>)>,
}

#[derive(Debug, PartialEq)]
enum State {
S0,
S1(Vec<u8>),
S0NoKey,
S1HaveVerifiedKey(Key),
}

enum QueueStatus {
Expand All @@ -28,9 +27,8 @@ enum QueueStatus {
impl SendMachine {
pub fn new(side: &str) -> SendMachine {
SendMachine {
state: State::S0,
state: State::S0NoKey,
side: side.to_string(),
key: Vec::new(),
queue: Vec::new(),
}
}
Expand All @@ -41,8 +39,8 @@ impl SendMachine {
self.state, event
);
let (newstate, actions, queue_status) = match self.state {
State::S0 => self.do_s0(event),
State::S1(ref key) => self.do_s1(key.to_vec(), event),
State::S0NoKey => self.do_s0(event),
State::S1HaveVerifiedKey(ref key) => self.do_s1(&key, event),
};

// process the queue
Expand All @@ -58,7 +56,7 @@ impl SendMachine {
actions
}

fn drain(&self, key: Vec<u8>) -> Events {
fn drain(&self, key: Key) -> Events {
let mut es = Events::new();

for &(ref phase, ref plaintext) in &self.queue {
Expand All @@ -70,12 +68,7 @@ impl SendMachine {
es
}

fn deliver(
&self,
key: Vec<u8>,
phase: String,
plaintext: Vec<u8>,
) -> Events {
fn deliver(&self, key: Key, phase: String, plaintext: Vec<u8>) -> Events {
let data_key = key::derive_phase_key(&self.side, &key, &phase);
let (_nonce, encrypted) = key::encrypt_data(data_key, &plaintext);
events![M_AddMessage(phase, encrypted)]
Expand All @@ -85,13 +78,13 @@ impl SendMachine {
use events::SendEvent::*;
match event {
GotVerifiedKey(ref key) => (
State::S1(key.to_vec()),
self.drain(key.to_vec()),
State::S1HaveVerifiedKey(key.clone()),
self.drain(key.clone()),
QueueStatus::Drain,
),
// we don't have a verified key, yet we got messages to send, so queue it up.
Send(phase, plaintext) => (
State::S0,
State::S0NoKey,
events![],
QueueStatus::Enqueue((phase, plaintext)),
),
Expand All @@ -100,7 +93,7 @@ impl SendMachine {

fn do_s1(
&self,
key: Vec<u8>,
key: &Key,
event: SendEvent,
) -> (State, Events, QueueStatus) {
use events::SendEvent::*;
Expand All @@ -110,7 +103,7 @@ impl SendMachine {
let deliver_events =
self.deliver(key.clone(), phase, plaintext);
(
State::S1(key),
State::S1HaveVerifiedKey(key.clone()),
deliver_events,
QueueStatus::NoAction,
)
Expand Down

0 comments on commit 94be84e

Please sign in to comment.