// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. //! An implementation of the OPAQUE asymmetric password authentication key exchange protocol //! //! Note: This implementation is in sync with [draft-krawczyk-cfrg-opaque-06](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06), //! but this specification is subject to change, until the final version published by the IETF. //! //! # Overview //! //! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives //! to be kept consistent throughout protocol execution. These include: //! * a finite cyclic group along with a point representation, //! * a keypair type, //! * a key exchange protocol, and //! * a slow hashing function. //! //! We will use the following choices in this example: //! ``` //! use opaque_ke::ciphersuite::CipherSuite; //! struct Default; //! impl CipherSuite for Default { //! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; //! } //! ``` //! //! Note that our choice of slow hashing function in this example, `NoOpHash`, is selected only to ensure //! that the tests execute quickly. A real application should use an actual slow hashing function, such as `Scrypt`. //! //! We have included a concrete instantiation of the authenticated key exchange protocol using 3DH. In the future, we plan to //! add support for other KE protocols as well. //! //! ## Setup //! To setup the protocol, the server begins by generating a static keypair: //! ``` //! # use opaque_ke::keypair::{KeyPair, X25519KeyPair, SizedBytes}; //! # use opaque_ke::errors::ProtocolError; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use rand_core::{OsRng, RngCore}; //! let mut rng = OsRng; //! let server_kp = Default::generate_random_keypair(&mut rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! The server must persist this keypair for the registration and login steps, where the public component will be //! used by the client during both registration and login, and the private component will be used by the server during login. //! //! ## Registration //! The registration protocol between the client and server consists of four steps along with three messages, denoted //! as `r1`, `r2`, and `r3`. Before registration begins, it is expected that the server's static public key, `server_kp.public()`, //! has been transmitted to the client in an offline step. A successful execution of the registration protocol results in the //! server producing a password file corresponding to the tuple combination of (password, pepper, server public key) provided by //! the client. This password file is typically stored server-side, and retrieved upon future login attempts made by the client. //! //! In the first step (client registration start), the client chooses a registration password and an optional "pepper", and //! runs `ClientRegistration::start` to produce a message `r1`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use rand_core::{OsRng, RngCore}; //! let mut client_rng = OsRng; //! let (r1, client_state) = ClientRegistration::::start( //! b"password", //! Some(b"pepper"), //! &mut client_rng, //! )?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `r1` is sent to the server, and `client_state` must be persisted on the client for the final step of client //! registration. //! //! In the second step (server registration start), the server takes as input the `r1` message from the client and runs //! `ServerRegistration::start` to produce `r2`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `r2` is returned to the client, and `server_state` must be persisted on the server for the final step of server //! registration. //! //! In the third step (client registration finish), the client takes as input the `r2` message from the server, along //! with the server's static public key `server_kp.public()`, and uses `client_state` from the first step to run //! `finish` and produce a message `r3` along with the export key `export_key_registration`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; //! let (r3, export_key_registration) = //! client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `r3` is sent to the server, and the client can optionally use `export_key_registration` for applications that choose to //! process user information beyond the OPAQUE functionality (e.g., additional secrets or credentials). //! //! In the fourth step of registration, the server takes as input the `r3` message from the client and uses //! `server_state` from the second step to run `finish` and produce `password_file`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; //! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! let password_file = server_state.finish(r3)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! At this point, the client can be considered as successfully registered, and the server can store //! `password_file.to_bytes()` for use during the login protocol. //! //! //! ## Login //! The login protocol between a client and server also consists of four steps along with three messages, denoted as //! `l1`, `l2`, and `l3`. The server is expected to have access to the a password file corresponding to an output //! of the registration phase. The login protocol will execute successfully only if the same tuple combination of //! (password, pepper, server public key) is presented as was used in the registration phase that produced the //! password file that the server is testing against. //! //! In the first step (client login start), the client chooses a registration password and an optional "pepper", and runs //! `ClientLogin::start` to produce a message `l1`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! let mut client_rng = OsRng; //! let (l1, client_state) = ClientLogin::::start( //! b"password", //! Some(b"pepper"), //! &mut client_rng, //! )?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `l1` is sent to the server, and `client_state` must be persisted on the client for the final step of client login. //! //! In the second step (server login start), the server takes as input the `l1` message from the client, the server's //! private key `server_kp.private()`, along with a serialized version of the password file, `password_file_bytes`, and //! runs `ServerLogin::start` to produce `l2`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; //! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! use std::convert::TryFrom; //! let password_file = ServerRegistration::::try_from(&password_file_bytes[..])?; //! let mut server_rng = OsRng; //! let (l2, server_state) = //! ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `l2` is returned to the client, and `server_state` must be persisted on the server for the final step of server login. //! //! In the third step (client login finish), the client takes as input the `l2` message from the server, along with the //! server's static public key `server_kp.public()`, and uses `client_state` from the first step to run `finish` and produce //! a message `l3`, the shared secret `client_shared_secret`, and the export key `export_key_login`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; //! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # use std::convert::TryFrom; //! # let password_file = //! # ServerRegistration::::try_from( //! # &password_file_bytes[..], //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; //! let (l3, client_shared_secret, export_key_login) = client_state.finish( //! l2, //! &server_kp.public(), //! &mut client_rng, //! )?; //! assert_eq!(export_key_registration, export_key_login); //! # Ok::<(), ProtocolError>(()) //! ``` //! Note that if the client supplies a tuple (password, pepper, server public key) that does not match the tuple //! used to create the password file, then at this point the `finish` algorithm outputs the error `InvalidLoginError`. //! //! If `finish` completes successfully, then `l3` is sent to the server, and (similarly to registration) the client //! can use `export_key_login` for applications that can take advantage of the fact that this key is identical to //! `export_key_registration`. //! //! In the fourth step of login, the server takes as input the `l3` message from the client and uses `server_state` from //! the second step to run `finish`: //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, //! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, //! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let (r1, client_state) = ClientRegistration::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # let mut server_rng = OsRng; //! let (r2, server_state) = ServerRegistration::::start(r1, &mut server_rng)?; //! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; //! # let (r3, export_key) = client_state.finish(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", //! # Some(b"pepper"), //! # &mut client_rng, //! # )?; //! # use std::convert::TryFrom; //! # let password_file = //! # ServerRegistration::::try_from( //! # &password_file_bytes[..], //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; //! # let (l3, client_shared_secret, export_key) = client_state.finish( //! # l2, //! # &server_kp.public(), //! # &mut client_rng, //! # )?; //! let server_shared_secret = server_state.finish(l3)?; //! assert_eq!(client_shared_secret, server_shared_secret); //! # Ok::<(), ProtocolError>(()) //! ``` //! If the protocol completes successfully, then the server obtains a `server_shared_secret` which is guaranteed to //! match `client_shared_secret`. Otherwise, on failure, the `finish` algorithm outputs the error `InvalidLoginError`. //! #![cfg_attr(not(feature = "bench"), deny(missing_docs))] #![deny(unsafe_code)] #[cfg(not(any(feature = "u64_backend", feature = "u32_backend",)))] compile_error!( "no dalek arithmetic backend cargo feature enabled! \ please enable one of: u64_backend, u32_backend" ); // Error types pub mod errors; // High-level API pub mod opaque; pub mod ciphersuite; mod envelope; pub mod group; pub mod map_to_curve; pub mod key_exchange; pub mod keypair; #[cfg(feature = "bench")] pub mod oprf; #[cfg(not(feature = "bench"))] mod oprf; pub mod slow_hash; #[cfg(test)] mod tests;