Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support permissions & more #128

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ eyre = "0.6.5"

futures = "0.3.12"
async-std = { version = "1.9.0", features = ["attributes", "unstable"] }
async-tungstenite = { version = "0.13.1", features = ["async-std-runtime"] }
async-tungstenite = { version = "0.13.1", features = ["async-std-runtime", "async-tls"] }

# for "bin" feature
clap = { version = "2.33.3", optional = true }
Expand All @@ -47,6 +47,7 @@ console = { version = "0.14.1", optional = true }
indicatif = { version = "0.16.0", optional = true }
dialoguer = { version = "0.8.0", optional = true }
color-eyre = { version = "0.5.7", optional = true }
hashcash = "0.1.1"

# for some tests
[dev-dependencies]
Expand Down
4 changes: 3 additions & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,9 @@ fn enter_code() -> eyre::Result<String> {
}

fn print_welcome(term: &mut Term, welcome: &magic_wormhole::WormholeWelcome) -> eyre::Result<()> {
writeln!(term, "Got welcome from server: {}", &welcome.welcome)?;
if let Some(welcome) = &welcome.welcome {
writeln!(term, "Got welcome from server: {}", welcome)?;
}
Ok(())
}

Expand Down
158 changes: 104 additions & 54 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ pub enum WormholeCoreError {
/// the server sent some bullshit message order
#[error("Protocol error: {}", _0)]
Protocol(Box<str>),
/// The server sent us an error message
#[error("Received error message from server: {}", _0)]
Server(Box<str>),
#[error(
"Server wants one of {:?} for permissions, but we don't suppport any of these",
_0
)]
Login(Vec<String>),
#[error(
"Key confirmation failed. If you didn't mistype the code, \
this is a sign of an attacker guessing passwords. Please try \
Expand Down Expand Up @@ -78,11 +86,16 @@ impl From<std::convert::Infallible> for WormholeCoreError {
// TODO manually implement Debug again to display some Vec<u8> as string and others as hex
#[derive(Debug, derive_more::Display)]
pub enum APIEvent {
#[display(fmt = "ConnectedToServer {{ welcome: {}, code: {} }}", welcome, code)]
#[display(
fmt = "ConnectedToServer {{ motd: {} }}",
r#"motd.as_deref().unwrap_or("<none>")"#
)]
ConnectedToServer {
/// A little welcome message from the server (message of the day and such)
// TODO we can actually provide more structure than a "value", see the protocol
welcome: serde_json::Value,
motd: Option<String>,
},
#[display(fmt = "GotCode {{ code: {} }}", code)]
GotCode {
/// Share this with your peer so they can connect
code: Code,
},
Expand Down Expand Up @@ -123,6 +136,12 @@ pub enum Mood {

#[derive(Debug, derive_more::Display)]
enum State {
#[display(fmt = "")] // TODO
WaitForWelcome {
versions: serde_json::Value,
code_provider: CodeProvider,
},

#[display(
fmt = "AllocatingNameplate {{ wordlist: <{} words>, side: {}, versions: {} }}",
"wordlist.num_words",
Expand Down Expand Up @@ -197,39 +216,12 @@ pub async fn run(

let mut actions: VecDeque<Event> = VecDeque::new();

/* Bootstrapping code */
let mut state;
actions.push_back(OutboundMessage::bind(appid.clone(), side.clone()).into());
/* A mini state machine to track that messaage. It's okay for now, but modularize if it starts growing. */
let mut welcome_message = None;

match code_provider {
CodeProvider::AllocateCode(num_words) => {
// TODO: provide choice of wordlists
let wordlist = Arc::new(wordlist::default_wordlist(num_words));
actions.push_back(OutboundMessage::Allocate.into());

state = State::AllocatingNameplate {
wordlist,
side: side.clone(),
versions,
};
},
CodeProvider::SetCode(code) => {
let code_string = code.to_string();
let nc: Vec<&str> = code_string.splitn(2, '-').collect();
let nameplate = Nameplate::new(nc[0]);
actions.push_back(OutboundMessage::claim(nameplate.clone()).into());

state = State::ClaimingNameplate {
nameplate,
code: Code(code),
side: side.clone(),
versions,
};
},
}
let mut state = State::WaitForWelcome {
versions,
code_provider,
};

/* The usual main loop */
loop {
let e = match actions.pop_front() {
Some(event) => Ok(event),
Expand Down Expand Up @@ -266,7 +258,72 @@ pub async fn run(
use self::{events::Event::*, server_messages::InboundMessage};
match e {
FromIO(InboundMessage::Welcome { welcome }) => {
welcome_message = Some(welcome);
match state {
State::WaitForWelcome {
versions,
code_provider,
} => {
use server_messages::{PermissionRequired, SubmitPermission};

actions
.push_back(APIEvent::ConnectedToServer { motd: welcome.motd }.into());

match welcome.permission_required {
Some(PermissionRequired {
hashcash: Some(hashcash),
..
}) => {
let token = hashcash::Token::new(hashcash.resource, hashcash.bits);
actions.push_back(
OutboundMessage::SubmitPermission(SubmitPermission::Hashcash {
stamp: token.to_string(),
})
.into(),
)
},
Some(PermissionRequired { none: true, .. }) => (),
Some(PermissionRequired { other, .. }) => {
/* We can't actually log in :/ */
actions.push_back(Event::ShutDown(Err(WormholeCoreError::Login(
// TODO use `into_keys` once stable and remove the `cloned`
other.keys().cloned().collect(),
))));
},
None => (),
}

actions
.push_back(OutboundMessage::bind(appid.clone(), side.clone()).into());

match code_provider {
CodeProvider::AllocateCode(num_words) => {
// TODO: provide choice of wordlists
let wordlist = Arc::new(wordlist::default_wordlist(num_words));
actions.push_back(OutboundMessage::Allocate.into());

state = State::AllocatingNameplate {
wordlist,
side: side.clone(),
versions,
};
},
CodeProvider::SetCode(code) => {
let code_string = code.to_string();
let nc: Vec<&str> = code_string.splitn(2, '-').collect();
let nameplate = Nameplate::new(nc[0]);
actions.push_back(OutboundMessage::claim(nameplate.clone()).into());

state = State::ClaimingNameplate {
nameplate,
code: Code(code),
side: side.clone(),
versions,
};
},
}
},
_ => unreachable!(),
}
},
FromIO(InboundMessage::Claimed { mailbox }) => {
match state {
Expand All @@ -288,19 +345,7 @@ pub async fn run(
&code,
)));

actions.push_back(
APIEvent::ConnectedToServer {
/* TODO Is the welcome message mandatory or optional? */
welcome: welcome_message
.take()
.ok_or_else(|| {
anyhow::format_err!("Didn't get a welcome message")
})
.unwrap(),
code,
}
.into(),
);
actions.push_back(APIEvent::GotCode { code }.into());
},
State::Closing { .. } => { /* This may happen. Ignore it. */ },
_ => {
Expand Down Expand Up @@ -397,11 +442,9 @@ pub async fn run(
orig: _,
}) => {
// TODO maybe hanlde orig field for better messages
// Also, make this a proper error type, maybe ServerError or RemoteError
actions.push_back(Event::ShutDown(Err(WormholeCoreError::protocol(format!(
"Received error message from server: {}",
message
)))));
actions.push_back(Event::ShutDown(Err(WormholeCoreError::Server(
message.into(),
))));
},
FromIO(InboundMessage::Pong { .. }) | FromIO(InboundMessage::Ack { .. }) => (), /* we ignore this, it's only for the timing log */
FromIO(InboundMessage::Unknown) => {
Expand All @@ -419,6 +462,13 @@ pub async fn run(
}
},
ShutDown(result) => match state {
State::WaitForWelcome { .. } => {
state = State::Closing {
await_nameplate_release: false,
await_mailbox_close: false,
result,
};
},
State::AllocatingNameplate { .. } => {
state = State::Closing {
await_nameplate_release: false,
Expand Down
Loading