diff --git a/Cargo.toml b/Cargo.toml index 917f816..2352d7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ proptest = "1" rand = "0.8" regex = "1" rustyline = "9" +scrypt = "0.10" serde_json = "1" [[bench]] @@ -77,3 +78,7 @@ name = "opaque" [package.metadata.docs.rs] features = ["argon2", "std", "x25519-u64"] targets = [] + +[[example]] +name = "simple_login" +required-features = ["argon2"] diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 5d0aed4..6bb5d73 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -45,10 +45,10 @@ use rustyline::Editor; // The ciphersuite trait allows to specify the underlying primitives that will // be used in the OPAQUE protocol #[allow(dead_code)] -struct Default; +struct DefaultCipherSuite; #[cfg(feature = "ristretto255")] -impl CipherSuite for Default { +impl CipherSuite for DefaultCipherSuite { type OprfCs = opaque_ke::Ristretto255; type KeGroup = opaque_ke::Ristretto255; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; @@ -56,7 +56,7 @@ impl CipherSuite for Default { } #[cfg(not(feature = "ristretto255"))] -impl CipherSuite for Default { +impl CipherSuite for DefaultCipherSuite { type OprfCs = p256::NistP256; type KeGroup = p256::NistP256; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; @@ -65,7 +65,7 @@ impl CipherSuite for Default { struct Locker { contents: Vec, - password_file: GenericArray>, + password_file: GenericArray>, } // Given a key and plaintext, produce an AEAD ciphertext along with a nonce @@ -96,18 +96,19 @@ fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec { // Password-based registration and encryption of client secret message between a // client and server fn register_locker( - server_setup: &ServerSetup, + server_setup: &ServerSetup, locker_id: usize, password: String, secret_message: String, ) -> Locker { let mut client_rng = OsRng; let client_registration_start_result = - ClientRegistration::::start(&mut client_rng, password.as_bytes()).unwrap(); + ClientRegistration::::start(&mut client_rng, password.as_bytes()) + .unwrap(); let registration_request_bytes = client_registration_start_result.message.serialize(); // Client sends registration_request_bytes to server - let server_registration_start_result = ServerRegistration::::start( + let server_registration_start_result = ServerRegistration::::start( server_setup, RegistrationRequest::deserialize(®istration_request_bytes).unwrap(), &locker_id.to_be_bytes(), @@ -137,7 +138,7 @@ fn register_locker( // Client sends message_bytes to server let password_file = ServerRegistration::finish( - RegistrationUpload::::deserialize(&message_bytes).unwrap(), + RegistrationUpload::::deserialize(&message_bytes).unwrap(), ); Locker { @@ -148,19 +149,20 @@ fn register_locker( // Open the contents of a locker with a password between a client and server fn open_locker( - server_setup: &ServerSetup, + server_setup: &ServerSetup, locker_id: usize, password: String, locker: &Locker, ) -> Result { let mut client_rng = OsRng; let client_login_start_result = - ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); + ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); let credential_request_bytes = client_login_start_result.message.serialize(); // Client sends credential_request_bytes to server - let password_file = ServerRegistration::::deserialize(&locker.password_file).unwrap(); + let password_file = + ServerRegistration::::deserialize(&locker.password_file).unwrap(); let mut server_rng = OsRng; let server_login_start_result = ServerLogin::start( &mut server_rng, @@ -212,7 +214,7 @@ fn open_locker( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng); + let server_setup = ServerSetup::::new(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_lockers: Vec = vec![]; diff --git a/examples/simple_login.rs b/examples/simple_login.rs index 4897430..39296be 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -24,6 +24,7 @@ use std::collections::HashMap; use std::process::exit; +use argon2::Argon2; use generic_array::GenericArray; use opaque_ke::ciphersuite::CipherSuite; use opaque_ke::rand::rngs::OsRng; @@ -39,38 +40,41 @@ use rustyline::Editor; // The ciphersuite trait allows to specify the underlying primitives that will // be used in the OPAQUE protocol #[allow(dead_code)] -struct Default; +struct DefaultCipherSuite; #[cfg(feature = "ristretto255")] -impl CipherSuite for Default { +impl CipherSuite for DefaultCipherSuite { type OprfCs = opaque_ke::Ristretto255; type KeGroup = opaque_ke::Ristretto255; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; - type Ksf = opaque_ke::ksf::Identity; + + type Ksf = Argon2<'static>; } #[cfg(not(feature = "ristretto255"))] -impl CipherSuite for Default { +impl CipherSuite for DefaultCipherSuite { type OprfCs = p256::NistP256; type KeGroup = p256::NistP256; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; - type Ksf = opaque_ke::ksf::Identity; + + type Ksf = Argon2<'static>; } // Password-based registration between a client and server fn account_registration( - server_setup: &ServerSetup, + server_setup: &ServerSetup, username: String, password: String, -) -> GenericArray> { +) -> GenericArray> { let mut client_rng = OsRng; let client_registration_start_result = - ClientRegistration::::start(&mut client_rng, password.as_bytes()).unwrap(); + ClientRegistration::::start(&mut client_rng, password.as_bytes()) + .unwrap(); let registration_request_bytes = client_registration_start_result.message.serialize(); // Client sends registration_request_bytes to server - let server_registration_start_result = ServerRegistration::::start( + let server_registration_start_result = ServerRegistration::::start( server_setup, RegistrationRequest::deserialize(®istration_request_bytes).unwrap(), username.as_bytes(), @@ -94,26 +98,27 @@ fn account_registration( // Client sends message_bytes to server let password_file = ServerRegistration::finish( - RegistrationUpload::::deserialize(&message_bytes).unwrap(), + RegistrationUpload::::deserialize(&message_bytes).unwrap(), ); password_file.serialize() } // Password-based login between a client and server fn account_login( - server_setup: &ServerSetup, + server_setup: &ServerSetup, username: String, password: String, password_file_bytes: &[u8], ) -> bool { let mut client_rng = OsRng; let client_login_start_result = - ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); + ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); let credential_request_bytes = client_login_start_result.message.serialize(); // Client sends credential_request_bytes to server - let password_file = ServerRegistration::::deserialize(password_file_bytes).unwrap(); + let password_file = + ServerRegistration::::deserialize(password_file_bytes).unwrap(); let mut server_rng = OsRng; let server_login_start_result = ServerLogin::start( &mut server_rng, @@ -153,11 +158,11 @@ fn account_login( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng); + let server_setup = ServerSetup::::new(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_users = - HashMap::>>::new(); + HashMap::>>::new(); loop { println!( "\nCurrently registered usernames: {:?}\n", diff --git a/src/lib.rs b/src/lib.rs index 004977d..f929dc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -986,6 +986,103 @@ //! let server_setup = ServerSetup::::new_with_key(&mut OsRng, keypair); //! ``` //! +//! ## Custom KSF and Parameters +//! +//! An application might want to use a custom KSF (Key Stretching Function) +//! that's not supported directly by this crate. The maintainer of the said KSF +//! or of the application itself can implement the [`Ksf`](ksf::Ksf) trait to +//! use it with `opaque-ke`. `scrypt` is used for this example, but any KSF +//! can be used. +//! ``` +//! # use generic_array::GenericArray; +//! #[derive(Default)] +//! struct CustomKsf(scrypt::Params); +//! +//! // The Ksf trait must be implemented to be used in the ciphersuite. +//! impl opaque_ke::ksf::Ksf for CustomKsf { +//! fn hash>( +//! &self, +//! input: GenericArray, +//! ) -> Result, opaque_ke::errors::InternalError> { +//! let mut output = GenericArray::::default(); +//! scrypt::scrypt(&input, &[], &self.0, &mut output) +//! .map_err(|_| opaque_ke::errors::InternalError::KsfError)?; +//! +//! Ok(output) +//! } +//! } +//! ``` +//! +//! It is also possible to override the default derivation parameters that are +//! used by the KSF during registration and login. This can be especially +//! helpful if the `Ksf` trait is already implemented. +//! ``` +//! # use opaque_ke::CipherSuite; +//! # use opaque_ke::ClientRegistration; +//! # use opaque_ke::ClientRegistrationFinishParameters; +//! # use opaque_ke::ServerSetup; +//! # use opaque_ke::errors::ProtocolError; +//! # use rand::rngs::OsRng; +//! # use rand::RngCore; +//! # use std::default::Default; +//! # #[cfg(feature = "argon2")] +//! # { +//! # struct DefaultCipherSuite; +//! # #[cfg(feature = "ristretto255")] +//! # impl CipherSuite for DefaultCipherSuite { +//! # type OprfCs = opaque_ke::Ristretto255; +//! # type KeGroup = opaque_ke::Ristretto255; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; +//! # type Ksf = argon2::Argon2<'static>; +//! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for DefaultCipherSuite { +//! # type OprfCs = p256::NistP256; +//! # type KeGroup = p256::NistP256; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; +//! # type Ksf = argon2::Argon2<'static>; +//! # } +//! # +//! # let password = b"password"; +//! # let mut rng = OsRng; +//! # let server_setup = ServerSetup::::new(&mut rng); +//! # let mut client_rng = OsRng; +//! # let client_registration_start_result = +//! # ClientRegistration::::start(&mut client_rng, password)?; +//! # use opaque_ke::ServerRegistration; +//! # let server_registration_start_result = ServerRegistration::::start( +//! # &server_setup, +//! # client_registration_start_result.message, +//! # b"alice@example.com", +//! # )?; +//! # +//! // Create an Argon2 instance with the specified parameters +//! let argon2_params = argon2::Params::new(131072, 2, 4, None).unwrap(); +//! let argon2_params = argon2::Argon2::new( +//! argon2::Algorithm::Argon2id, +//! argon2::Version::V0x13, +//! argon2_params, +//! ); +//! +//! // Override the default parameters with the custom ones +//! let hash_params = ClientRegistrationFinishParameters { +//! ksf: Some(&argon2_params), +//! ..Default::default() +//! }; +//! +//! let client_registration_finish_result = client_registration_start_result +//! .state +//! .finish( +//! &mut rng, +//! password, +//! server_registration_start_result.message, +//! hash_params, +//! ) +//! .unwrap(); +//! # } +//! # Ok::<(), ProtocolError>(()) +//! ``` +//! //! # Features //! //! - The `argon2` feature, when enabled, introduces a dependency on `argon2`