From: Tony Arcieri Date: Sat, 22 Jan 2022 22:48:43 +0000 (-0700) Subject: spake2: capitalization and doc fixes (#89) X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=fb570e02d56b47a812e2a27b6b62eba01ce0416f;p=PAKEs.git spake2: capitalization and doc fixes (#89) Renames the following: - `SPAKE2` => `Spake2` - `SPAKEErr` -> `Error` Additionally lints for `missing_docs` and adds preliminary documentation for all types and methods which previously had none. --- diff --git a/spake2/benches/spake2.rs b/spake2/benches/spake2.rs index c213947..7638194 100644 --- a/spake2/benches/spake2.rs +++ b/spake2/benches/spake2.rs @@ -1,10 +1,10 @@ use bencher::Bencher; use bencher::{benchmark_group, benchmark_main}; -use spake2::{Ed25519Group, Identity, Password, SPAKE2}; +use spake2::{Ed25519Group, Identity, Password, Spake2}; fn spake2_start(bench: &mut Bencher) { bench.iter(|| { - let (_, _) = SPAKE2::::start_a( + let (_, _) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -31,14 +31,14 @@ fn spake2_finish(bench: &mut Bencher) { */ fn spake2_start_and_finish(bench: &mut Bencher) { - let (_, msg2) = SPAKE2::::start_b( + let (_, msg2) = Spake2::::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::::start_a( + let (s1, _) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), diff --git a/spake2/src/lib.rs b/spake2/src/lib.rs index 8a8654a..9e31fd4 100644 --- a/spake2/src/lib.rs +++ b/spake2/src/lib.rs @@ -6,7 +6,7 @@ html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" )] #![forbid(unsafe_code)] -#![warn(rust_2018_idioms, unused_qualifications)] +#![warn(missing_docs, rust_2018_idioms, unused_qualifications)] //! # Usage //! @@ -41,15 +41,15 @@ //! Thus a client-side program start with: //! //! ```rust -//! use spake2::{Ed25519Group, Identity, Password, SPAKE2}; +//! use spake2::{Ed25519Group, Identity, Password, Spake2}; //! # fn send(msg: &[u8]) {} -//! let (s1, outbound_msg) = SPAKE2::::start_a( +//! let (s1, outbound_msg) = Spake2::::start_a( //! &Password::new(b"password"), //! &Identity::new(b"client id string"), //! &Identity::new(b"server id string")); //! send(&outbound_msg); //! -//! # fn receive() -> Vec { let (s2, i2) = SPAKE2::::start_b(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 } +//! # fn receive() -> Vec { let (s2, i2) = Spake2::::start_b(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 } //! let inbound_msg = receive(); //! let key1 = s1.finish(&inbound_msg).unwrap(); //! ``` @@ -58,14 +58,14 @@ //! //! ```rust //! # fn send(msg: &[u8]) {} -//! use spake2::{Ed25519Group, Identity, Password, SPAKE2}; -//! let (s1, outbound_msg) = SPAKE2::::start_b( +//! use spake2::{Ed25519Group, Identity, Password, Spake2}; +//! let (s1, outbound_msg) = Spake2::::start_b( //! &Password::new(b"password"), //! &Identity::new(b"client id string"), //! &Identity::new(b"server id string")); //! send(&outbound_msg); //! -//! # fn receive() -> Vec { let (s2, i2) = SPAKE2::::start_a(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 } +//! # fn receive() -> Vec { let (s2, i2) = Spake2::::start_a(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 } //! let inbound_msg = receive(); //! let key2 = s1.finish(&inbound_msg).unwrap(); //! ``` @@ -102,13 +102,13 @@ //! //! ```rust //! # fn send(msg: &[u8]) {} -//! use spake2::{Ed25519Group, Identity, Password, SPAKE2}; -//! let (s1, outbound_msg) = SPAKE2::::start_symmetric( +//! use spake2::{Ed25519Group, Identity, Password, Spake2}; +//! let (s1, outbound_msg) = Spake2::::start_symmetric( //! &Password::new(b"password"), //! &Identity::new(b"shared id string")); //! send(&outbound_msg); //! -//! # fn receive() -> Vec { let (s2, i2) = SPAKE2::::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 } +//! # fn receive() -> Vec { let (s2, i2) = Spake2::::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 } //! let inbound_msg = receive(); //! let key1 = s1.finish(&inbound_msg).unwrap(); //! ``` @@ -117,13 +117,13 @@ //! //! ```rust //! # fn send(msg: &[u8]) {} -//! use spake2::{Ed25519Group, Identity, Password, SPAKE2}; -//! let (s1, outbound_msg) = SPAKE2::::start_symmetric( +//! use spake2::{Ed25519Group, Identity, Password, Spake2}; +//! let (s1, outbound_msg) = Spake2::::start_symmetric( //! &Password::new(b"password"), //! &Identity::new(b"shared id string")); //! send(&outbound_msg); //! -//! # fn receive() -> Vec { let (s2, i2) = SPAKE2::::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 } +//! # fn receive() -> Vec { let (s2, i2) = Spake2::::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 } //! let inbound_msg = receive(); //! let key1 = s1.finish(&inbound_msg).unwrap(); //! ``` @@ -244,84 +244,133 @@ use sha2::{Digest, Sha256}; #[cfg(feature = "getrandom")] use rand_core::OsRng; -/* "newtype pattern": it's a Vec, but only used for a specific argument - * type, to distinguish between ones that are meant as passwords, and ones - * that are meant as identity strings */ +/// SPAKE2 errors. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Error { + /// Bad side + BadSide, + + /// Corrupt message + CorruptMessage, + + /// Wrong length + WrongLength, +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::BadSide => fmt.write_str("bad side"), + Error::CorruptMessage => fmt.write_str("corrupt message"), + Error::WrongLength => fmt.write_str("invalid length"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} +/// Password. +// TODO(tarcieri): avoid allocation? #[derive(PartialEq, Eq, Clone)] pub struct Password(Vec); + impl Password { + /// Create a new password pub fn new(p: &[u8]) -> Password { Password(p.to_vec()) } } + impl Deref for Password { type Target = Vec; + fn deref(&self) -> &Vec { &self.0 } } +/// SPAKE2 identity. +// TODO(tarcieri): avoid allocation? #[derive(PartialEq, Eq, Clone)] pub struct Identity(Vec); + impl Deref for Identity { type Target = Vec; + fn deref(&self) -> &Vec { &self.0 } } + impl Identity { + /// Create a new identity pub fn new(p: &[u8]) -> Identity { Identity(p.to_vec()) } } -#[derive(Debug, PartialEq, Eq)] -pub enum ErrorType { - BadSide, - WrongLength, - CorruptMessage, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct SPAKEErr { - pub kind: ErrorType, -} - +/// Group trait +// TODO(tarcieri): replace with `group` crate? pub trait Group { + /// Scalar element type Scalar; + + /// Base field element type Element; - //type Element: Add - // + Mul; - // const element_length: usize; // in unstable, or u8 - //type ElementBytes : Index+IndexMut; // later + + /// Transcript hash type TranscriptHash; + + /// Name fn name() -> &'static str; + + /// `m` constant fn const_m() -> Self::Element; + + /// `n` constant fn const_n() -> Self::Element; + + /// `s` constant fn const_s() -> Self::Element; + + /// Hash to scalar fn hash_to_scalar(s: &[u8]) -> Self::Scalar; + + /// Generate a random scalar fn random_scalar(cspring: &mut T) -> Self::Scalar where T: RngCore + CryptoRng; + + /// Scalar negation fn scalar_neg(s: &Self::Scalar) -> Self::Scalar; + + /// Convert base field element to bytes fn element_to_bytes(e: &Self::Element) -> Vec; + + /// Convert bytes to base field element fn bytes_to_element(b: &[u8]) -> Option; + + /// Length of a base field element fn element_length() -> usize; + + /// Fixed-base scalar multiplication fn basepoint_mult(s: &Self::Scalar) -> Self::Element; + + /// Variable-base scalar multiplication fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element; + + /// Group operation fn add(a: &Self::Element, b: &Self::Element) -> Self::Element; } +/// Ed25519 elliptic curve group #[derive(Debug, PartialEq, Eq)] pub struct Ed25519Group; impl Group for Ed25519Group { type Scalar = c2_Scalar; type Element = c2_Element; - //type ElementBytes = Vec; - //type ElementBytes = [u8; 32]; - //type ScalarBytes type TranscriptHash = Sha256; fn name() -> &'static str { @@ -367,44 +416,47 @@ impl Group for Ed25519Group { fn hash_to_scalar(s: &[u8]) -> c2_Scalar { ed25519_hash_to_scalar(s) } + fn random_scalar(cspring: &mut T) -> c2_Scalar where T: RngCore + CryptoRng, { c2_Scalar::random(cspring) } + fn scalar_neg(s: &c2_Scalar) -> c2_Scalar { -s } + fn element_to_bytes(s: &c2_Element) -> Vec { s.compress().as_bytes().to_vec() } + fn element_length() -> usize { 32 } + fn bytes_to_element(b: &[u8]) -> Option { if b.len() != 32 { return None; } - //let mut bytes: [u8; 32] = + let mut bytes = [0u8; 32]; bytes.copy_from_slice(b); + let cey = CompressedEdwardsY(bytes); - // CompressedEdwardsY::new(b) cey.decompress() } fn basepoint_mult(s: &c2_Scalar) -> c2_Element { - //c2_Element::basepoint_mult(s) ED25519_BASEPOINT_POINT * s } fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element { e * s - //e.scalar_mult(s) } + fn add(a: &c2_Element, b: &c2_Element) -> c2_Element { a + b - //a.add(b) } } @@ -530,9 +582,9 @@ enum Side { Symmetric, } -// we implement a custom Debug below, to avoid revealing secrets in a dump -#[derive(PartialEq, Eq)] -pub struct SPAKE2 { +/// SPAKE2 algorithm. +#[derive(Eq, PartialEq)] +pub struct Spake2 { //where &G::Scalar: Neg { side: Side, xy_scalar: G::Scalar, @@ -544,7 +596,7 @@ pub struct SPAKE2 { password_scalar: G::Scalar, } -impl SPAKE2 { +impl Spake2 { fn start_internal( side: Side, password: &Password, @@ -552,7 +604,7 @@ impl SPAKE2 { id_b: &Identity, id_s: &Identity, xy_scalar: G::Scalar, - ) -> (SPAKE2, Vec) { + ) -> (Spake2, Vec) { //let password_scalar: G::Scalar = hash_to_scalar::(password); let password_scalar: G::Scalar = G::hash_to_scalar(password); @@ -587,7 +639,7 @@ impl SPAKE2 { msg_and_side.extend_from_slice(&msg1); ( - SPAKE2 { + Spake2 { side, xy_scalar, password_vec, // string @@ -606,7 +658,7 @@ impl SPAKE2 { id_a: &Identity, id_b: &Identity, xy_scalar: G::Scalar, - ) -> (SPAKE2, Vec) { + ) -> (Spake2, Vec) { Self::start_internal( Side::A, password, @@ -622,7 +674,7 @@ impl SPAKE2 { id_a: &Identity, id_b: &Identity, xy_scalar: G::Scalar, - ) -> (SPAKE2, Vec) { + ) -> (Spake2, Vec) { Self::start_internal( Side::B, password, @@ -637,7 +689,7 @@ impl SPAKE2 { password: &Password, id_s: &Identity, xy_scalar: G::Scalar, - ) -> (SPAKE2, Vec) { + ) -> (Spake2, Vec) { Self::start_internal( Side::Symmetric, password, @@ -648,95 +700,90 @@ impl SPAKE2 { ) } + /// Start with identity `a`. + /// + /// Uses the system RNG. #[cfg(feature = "getrandom")] #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))] - pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2, Vec) { + pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2, Vec) { Self::start_a_with_rng(password, id_a, id_b, OsRng) } + /// Start with identity `b`. + /// + /// Uses the system RNG. #[cfg(feature = "getrandom")] #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))] - pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2, Vec) { + pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2, Vec) { Self::start_b_with_rng(password, id_a, id_b, OsRng) } + /// Start with symmetric identity. + /// + /// Uses the system RNG. #[cfg(feature = "getrandom")] #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))] - pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2, Vec) { + pub fn start_symmetric(password: &Password, id_s: &Identity) -> (Spake2, Vec) { Self::start_symmetric_with_rng(password, id_s, OsRng) } + /// Start with identity `a` and the provided cryptographically secure RNG. pub fn start_a_with_rng( password: &Password, id_a: &Identity, id_b: &Identity, - mut csprng: impl CryptoRng + RngCore, - ) -> (SPAKE2, Vec) { - let xy_scalar: G::Scalar = G::random_scalar(&mut csprng); + mut csrng: impl CryptoRng + RngCore, + ) -> (Spake2, Vec) { + let xy_scalar: G::Scalar = G::random_scalar(&mut csrng); Self::start_a_internal(password, id_a, id_b, xy_scalar) } + /// Start with identity `b` and the provided cryptographically secure RNG. pub fn start_b_with_rng( password: &Password, id_a: &Identity, id_b: &Identity, - mut csprng: impl CryptoRng + RngCore, - ) -> (SPAKE2, Vec) { - let xy_scalar: G::Scalar = G::random_scalar(&mut csprng); + mut csrng: impl CryptoRng + RngCore, + ) -> (Spake2, Vec) { + let xy_scalar: G::Scalar = G::random_scalar(&mut csrng); Self::start_b_internal(password, id_a, id_b, xy_scalar) } + /// Start with symmetric identity and the provided cryptographically secure RNG. pub fn start_symmetric_with_rng( password: &Password, id_s: &Identity, - mut csprng: impl CryptoRng + RngCore, - ) -> (SPAKE2, Vec) { - let xy_scalar: G::Scalar = G::random_scalar(&mut csprng); + mut csrng: impl CryptoRng + RngCore, + ) -> (Spake2, Vec) { + let xy_scalar: G::Scalar = G::random_scalar(&mut csrng); Self::start_symmetric_internal(password, id_s, xy_scalar) } - pub fn finish(self, msg2: &[u8]) -> Result, SPAKEErr> { + /// Finish SPAKE2. + pub fn finish(self, msg2: &[u8]) -> Result, Error> { if msg2.len() != 1 + G::element_length() { - return Err(SPAKEErr { - kind: ErrorType::WrongLength, - }); + return Err(Error::WrongLength); } let msg_side = msg2[0]; match self.side { Side::A => match msg_side { 0x42 => (), // 'B' - _ => { - return Err(SPAKEErr { - kind: ErrorType::BadSide, - }) - } + _ => return Err(Error::BadSide), }, Side::B => match msg_side { 0x41 => (), // 'A' - _ => { - return Err(SPAKEErr { - kind: ErrorType::BadSide, - }) - } + _ => return Err(Error::BadSide), }, Side::Symmetric => match msg_side { 0x53 => (), // 'S' - _ => { - return Err(SPAKEErr { - kind: ErrorType::BadSide, - }) - } + _ => return Err(Error::BadSide), }, } let msg2_element = match G::bytes_to_element(&msg2[1..]) { Some(x) => x, - None => { - return Err(SPAKEErr { - kind: ErrorType::CorruptMessage, - }) - } + None => return Err(Error::CorruptMessage), }; // a: K = (Y+N*(-pw))*x @@ -786,7 +833,7 @@ impl SPAKE2 { } } -impl fmt::Debug for SPAKE2 { +impl fmt::Debug for Spake2 { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("SPAKE2") .field("group", &G::name()) diff --git a/spake2/src/tests.rs b/spake2/src/tests.rs index d063b86..49749db 100644 --- a/spake2/src/tests.rs +++ b/spake2/src/tests.rs @@ -61,13 +61,13 @@ fn test_password_to_scalar() { #[test] fn test_sizes() { - let (s1, msg1) = SPAKE2::::start_a( + let (s1, msg1) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), ); assert_eq!(msg1.len(), 1 + 32); - let (s2, msg2) = SPAKE2::::start_b( + let (s2, msg2) = Spake2::::start_b( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -78,12 +78,12 @@ fn test_sizes() { assert_eq!(key1.len(), 32); assert_eq!(key2.len(), 32); - let (s1, msg1) = SPAKE2::::start_symmetric( + let (s1, msg1) = Spake2::::start_symmetric( &Password::new(b"password"), &Identity::new(b"idS"), ); assert_eq!(msg1.len(), 1 + 32); - let (s2, msg2) = SPAKE2::::start_symmetric( + let (s2, msg2) = Spake2::::start_symmetric( &Password::new(b"password"), &Identity::new(b"idS"), ); @@ -135,7 +135,7 @@ fn test_asymmetric() { println!("scalar_a is {}", hex::encode(scalar_a.as_bytes())); - let (s1, msg1) = SPAKE2::::start_a_internal( + let (s1, msg1) = Spake2::::start_a_internal( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -159,7 +159,7 @@ fn test_asymmetric() { ); assert_eq!(hex::encode(&msg1), expected_msg1); - let (s2, msg2) = SPAKE2::::start_b_internal( + let (s2, msg2) = Spake2::::start_b_internal( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -182,7 +182,7 @@ fn test_asymmetric() { #[test] fn test_debug() { - let (s1, _msg1) = SPAKE2::::start_a( + let (s1, _msg1) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -193,7 +193,7 @@ fn test_debug() { "SPAKE2 { group: \"Ed25519\", side: A, idA: (s=idA), idB: (s=idB), idS: (s=) }" ); - let (s2, _msg1) = SPAKE2::::start_symmetric( + let (s2, _msg1) = Spake2::::start_symmetric( &Password::new(b"password"), &Identity::new(b"idS"), ); diff --git a/spake2/tests/spake2.rs b/spake2/tests/spake2.rs index 07ba946..eeced91 100644 --- a/spake2/tests/spake2.rs +++ b/spake2/tests/spake2.rs @@ -1,13 +1,13 @@ -use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKEErr, SPAKE2}; +use spake2::{Ed25519Group, Error, Identity, Password, Spake2}; #[test] fn test_basic() { - let (s1, msg1) = SPAKE2::::start_a( + let (s1, msg1) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), ); - let (s2, msg2) = SPAKE2::::start_b( + let (s2, msg2) = Spake2::::start_b( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -19,12 +19,12 @@ fn test_basic() { #[test] fn test_mismatch() { - let (s1, msg1) = SPAKE2::::start_a( + let (s1, msg1) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), ); - let (s2, msg2) = SPAKE2::::start_b( + let (s2, msg2) = Spake2::::start_b( &Password::new(b"password2"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -36,23 +36,18 @@ fn test_mismatch() { #[test] fn test_reflected_message() { - let (s1, msg1) = SPAKE2::::start_a( + let (s1, msg1) = Spake2::::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(), - SPAKEErr { - kind: ErrorType::BadSide, - } - ); + assert_eq!(r.unwrap_err(), Error::BadSide); } #[test] fn test_bad_length() { - let (s1, msg1) = SPAKE2::::start_a( + let (s1, msg1) = Spake2::::start_a( &Password::new(b"password"), &Identity::new(b"idA"), &Identity::new(b"idB"), @@ -60,21 +55,16 @@ fn test_bad_length() { let mut msg2 = Vec::::with_capacity(msg1.len() + 1); msg2.resize(msg1.len() + 1, 0u8); let r = s1.finish(&msg2); - assert_eq!( - r.unwrap_err(), - SPAKEErr { - kind: ErrorType::WrongLength, - } - ); + assert_eq!(r.unwrap_err(), Error::WrongLength); } #[test] fn test_basic_symmetric() { - let (s1, msg1) = SPAKE2::::start_symmetric( + let (s1, msg1) = Spake2::::start_symmetric( &Password::new(b"password"), &Identity::new(b"idS"), ); - let (s2, msg2) = SPAKE2::::start_symmetric( + let (s2, msg2) = Spake2::::start_symmetric( &Password::new(b"password"), &Identity::new(b"idS"), );