Skip to content

Commit 111a62b

Browse files
committed
Support permissions
The actual log-in process is mostly untested Also add an experimental implementation for <magic-wormhole/magic-wormhole-protocols#6>.
1 parent 424c0f9 commit 111a62b

File tree

7 files changed

+279
-82
lines changed

7 files changed

+279
-82
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ log = "0.4.13"
3030
# zeroize = { version = "1.2.0", features = ["zeroize_derive"] }
3131
get_if_addrs = "0.5.3"
3232
byteorder = "1.4.2"
33+
hashcash = "0.1.1"
3334

3435
anyhow = "1.0.38"
3536
derive_more = { version = "0.99.0", default-features = false, features = ["display"] }

src/bin/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,9 @@ fn enter_code() -> eyre::Result<String> {
365365
}
366366

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

src/core.rs

Lines changed: 98 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ pub enum WormholeCoreError {
4444
/// The server sent us an error message
4545
#[error("Received error message from server: {}", _0)]
4646
Server(Box<str>),
47+
#[error(
48+
"Server wants one of {:?} for permissions, but we don't suppport any of these",
49+
_0
50+
)]
51+
Login(Vec<String>),
4752
#[error(
4853
"Key confirmation failed. If you didn't mistype the code, \
4954
this is a sign of an attacker guessing passwords. Please try \
@@ -81,11 +86,16 @@ impl From<std::convert::Infallible> for WormholeCoreError {
8186
// TODO manually implement Debug again to display some Vec<u8> as string and others as hex
8287
#[derive(Debug, derive_more::Display)]
8388
pub enum APIEvent {
84-
#[display(fmt = "ConnectedToServer {{ welcome: {}, code: {} }}", welcome, code)]
89+
#[display(
90+
fmt = "ConnectedToServer {{ motd: {} }}",
91+
r#"motd.as_deref().unwrap_or("<none>")"#
92+
)]
8593
ConnectedToServer {
8694
/// A little welcome message from the server (message of the day and such)
87-
// TODO we can actually provide more structure than a "value", see the protocol
88-
welcome: serde_json::Value,
95+
motd: Option<String>,
96+
},
97+
#[display(fmt = "GotCode {{ code: {} }}", code)]
98+
GotCode {
8999
/// Share this with your peer so they can connect
90100
code: Code,
91101
},
@@ -126,6 +136,12 @@ pub enum Mood {
126136

127137
#[derive(Debug, derive_more::Display)]
128138
enum State {
139+
#[display(fmt = "")] // TODO
140+
WaitForWelcome {
141+
versions: serde_json::Value,
142+
code_provider: CodeProvider,
143+
},
144+
129145
#[display(
130146
fmt = "AllocatingNameplate {{ wordlist: <{} words>, side: {}, versions: {} }}",
131147
"wordlist.num_words",
@@ -200,39 +216,12 @@ pub async fn run(
200216

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

203-
/* Bootstrapping code */
204-
let mut state;
205-
actions.push_back(OutboundMessage::bind(appid.clone(), side.clone()).into());
206-
/* A mini state machine to track that messaage. It's okay for now, but modularize if it starts growing. */
207-
let mut welcome_message = None;
208-
209-
match code_provider {
210-
CodeProvider::AllocateCode(num_words) => {
211-
// TODO: provide choice of wordlists
212-
let wordlist = Arc::new(wordlist::default_wordlist(num_words));
213-
actions.push_back(OutboundMessage::Allocate.into());
214-
215-
state = State::AllocatingNameplate {
216-
wordlist,
217-
side: side.clone(),
218-
versions,
219-
};
220-
},
221-
CodeProvider::SetCode(code) => {
222-
let code_string = code.to_string();
223-
let nc: Vec<&str> = code_string.splitn(2, '-').collect();
224-
let nameplate = Nameplate::new(nc[0]);
225-
actions.push_back(OutboundMessage::claim(nameplate.clone()).into());
226-
227-
state = State::ClaimingNameplate {
228-
nameplate,
229-
code: Code(code),
230-
side: side.clone(),
231-
versions,
232-
};
233-
},
234-
}
219+
let mut state = State::WaitForWelcome {
220+
versions,
221+
code_provider,
222+
};
235223

224+
/* The usual main loop */
236225
loop {
237226
let e = match actions.pop_front() {
238227
Some(event) => Ok(event),
@@ -269,7 +258,72 @@ pub async fn run(
269258
use self::{events::Event::*, server_messages::InboundMessage};
270259
match e {
271260
FromIO(InboundMessage::Welcome { welcome }) => {
272-
welcome_message = Some(welcome);
261+
match state {
262+
State::WaitForWelcome {
263+
versions,
264+
code_provider,
265+
} => {
266+
use server_messages::{PermissionRequired, SubmitPermission};
267+
268+
actions
269+
.push_back(APIEvent::ConnectedToServer { motd: welcome.motd }.into());
270+
271+
match welcome.permission_required {
272+
Some(PermissionRequired {
273+
hashcash: Some(hashcash),
274+
..
275+
}) => {
276+
let token = hashcash::Token::new(hashcash.resource, hashcash.bits);
277+
actions.push_back(
278+
OutboundMessage::SubmitPermission(SubmitPermission::Hashcash {
279+
stamp: token.to_string(),
280+
})
281+
.into(),
282+
)
283+
},
284+
Some(PermissionRequired { none: true, .. }) => (),
285+
Some(PermissionRequired { other, .. }) => {
286+
/* We can't actually log in :/ */
287+
actions.push_back(Event::ShutDown(Err(WormholeCoreError::Login(
288+
// TODO use `into_keys` once stable and remove the `cloned`
289+
other.keys().cloned().collect(),
290+
))));
291+
},
292+
None => (),
293+
}
294+
295+
actions
296+
.push_back(OutboundMessage::bind(appid.clone(), side.clone()).into());
297+
298+
match code_provider {
299+
CodeProvider::AllocateCode(num_words) => {
300+
// TODO: provide choice of wordlists
301+
let wordlist = Arc::new(wordlist::default_wordlist(num_words));
302+
actions.push_back(OutboundMessage::Allocate.into());
303+
304+
state = State::AllocatingNameplate {
305+
wordlist,
306+
side: side.clone(),
307+
versions,
308+
};
309+
},
310+
CodeProvider::SetCode(code) => {
311+
let code_string = code.to_string();
312+
let nc: Vec<&str> = code_string.splitn(2, '-').collect();
313+
let nameplate = Nameplate::new(nc[0]);
314+
actions.push_back(OutboundMessage::claim(nameplate.clone()).into());
315+
316+
state = State::ClaimingNameplate {
317+
nameplate,
318+
code: Code(code),
319+
side: side.clone(),
320+
versions,
321+
};
322+
},
323+
}
324+
},
325+
_ => unreachable!(),
326+
}
273327
},
274328
FromIO(InboundMessage::Claimed { mailbox }) => {
275329
match state {
@@ -291,19 +345,7 @@ pub async fn run(
291345
&code,
292346
)));
293347

294-
actions.push_back(
295-
APIEvent::ConnectedToServer {
296-
/* TODO Is the welcome message mandatory or optional? */
297-
welcome: welcome_message
298-
.take()
299-
.ok_or_else(|| {
300-
anyhow::format_err!("Didn't get a welcome message")
301-
})
302-
.unwrap(),
303-
code,
304-
}
305-
.into(),
306-
);
348+
actions.push_back(APIEvent::GotCode { code }.into());
307349
},
308350
State::Closing { .. } => { /* This may happen. Ignore it. */ },
309351
_ => {
@@ -420,6 +462,13 @@ pub async fn run(
420462
}
421463
},
422464
ShutDown(result) => match state {
465+
State::WaitForWelcome { .. } => {
466+
state = State::Closing {
467+
await_nameplate_release: false,
468+
await_mailbox_close: false,
469+
result,
470+
};
471+
},
423472
State::AllocatingNameplate { .. } => {
424473
state = State::Closing {
425474
await_nameplate_release: false,

0 commit comments

Comments
 (0)