Skip to content
This repository has been archived by the owner on Feb 15, 2019. It is now read-only.

Commit

Permalink
use newtypes for Password and Identity to avoid usage errors
Browse files Browse the repository at this point in the history
This a breaking API change. The next release should bump the minor version
number.

As discussed in #3 and
https://github.com/warner/magic-wormhole.rs/issues/32 , if an application
were to accidentally swap the "password" and "identity" arguments (mainly for
start_symmetric which only takes two args), the app would appear to work, but
would contain a devastating security vulnerability (online brute-force
password attack, with precomputation enabled).

You might think of newtypes as giving the API named parameters. Instead of:

`s = start_symmetric(b"pw", b"appid")`

you get:

`s = start_symmetric(&Password::new(b"pw"), &Identity::new(b"appid"))`

but it protects you (with a compile-time error) against mistakes like:

`s = start_symmetric(&Identity::new(b"appid"), &Password::new(b"pw"))`

I'd like to find a way to remove requirement to pass a reference (and enable
`start_symmetric(Password::new(..)..)`).
  • Loading branch information
warner committed Jun 3, 2018
1 parent 9bd2021 commit 0869881
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 59 deletions.
41 changes: 29 additions & 12 deletions benches/spake2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,49 @@ extern crate bencher;
extern crate spake2;

use bencher::Bencher;
use spake2::{Ed25519Group, SPAKE2};
use spake2::{Ed25519Group, Identity, Password, SPAKE2};

fn spake2_start(bench: &mut Bencher) {
bench.iter(|| {
let (_, _) = SPAKE2::<Ed25519Group>::start_a(b"password", b"A", b"B");
let (_, _) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
})
}

/*
fn spake2_finish(bench: &mut Bencher) {
// this doesn't work, because s1 is consumed by doing finish()
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password",
b"idA", b"idB");
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password",
b"idA", b"idB");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let msg2_slice = msg2.as_slice();
bench.iter(|| {
s1.finish(msg2_slice)
})
}*/
bench.iter(|| s1.finish(msg2_slice))
}
*/

fn spake2_start_and_finish(bench: &mut Bencher) {
let (_, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password", b"idA", b"idB");
let (_, msg2) = SPAKE2::<Ed25519Group>::start_b(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let msg2_slice = msg2.as_slice();
bench.iter(|| {
let (s1, _) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
let (s1, _) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
s1.finish(msg2_slice)
})
}
Expand Down
48 changes: 39 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,49 @@ pub use spake2::*;

#[cfg(test)]
mod tests {
use spake2::{Ed25519Group, ErrorType, SPAKE2, SPAKEErr};
use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKE2, SPAKEErr};

#[test]
fn test_basic() {
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password", b"idA", b"idB");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let key1 = s1.finish(msg2.as_slice()).unwrap();
let key2 = s2.finish(msg1.as_slice()).unwrap();
assert_eq!(key1, key2);
}

#[test]
fn test_mismatch() {
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password2", b"idA", b"idB");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
&Password::new(b"password2"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let key1 = s1.finish(msg2.as_slice()).unwrap();
let key2 = s2.finish(msg1.as_slice()).unwrap();
assert_ne!(key1, key2);
}

#[test]
fn test_reflected_message() {
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let r = s1.finish(msg1.as_slice());
assert_eq!(
r.unwrap_err(),
Expand All @@ -44,7 +64,11 @@ mod tests {

#[test]
fn test_bad_length() {
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
&Password::new(b"password"),
&Identity::new(b"idA"),
&Identity::new(b"idB"),
);
let mut msg2 = Vec::<u8>::with_capacity(msg1.len() + 1);
msg2.resize(msg1.len() + 1, 0u8);
let r = s1.finish(&msg2);
Expand All @@ -58,8 +82,14 @@ mod tests {

#[test]
fn test_basic_symmetric() {
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", b"idS");
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", b"idS");
let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(
&Password::new(b"password"),
&Identity::new(b"idS"),
);
let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(
&Password::new(b"password"),
&Identity::new(b"idS"),
);
let key1 = s1.finish(msg2.as_slice()).unwrap();
let key2 = s2.finish(msg1.as_slice()).unwrap();
assert_eq!(key1, key2);
Expand Down
Loading

0 comments on commit 0869881

Please sign in to comment.