Repository: novifinancial/opaque-ke (rust)

Issue:
Generics for public objects should all rely on Ciphersuite
RegistrationRequest, RegistrationResponse, RegistrationUpload currently have type parameters that are not CipherSuite, and they should be fixed to be CipherSuite for consistency.

Pull request:
Making message type parameters set to CipherSuite
Closes #136 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c91614..49014dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 0.3.1 (February 11, 2020)
+
+* Re-exporting the rand library (and including it as a dependency instead of
+  just rand_core)
+* Exposing a convenience function for converting from byte array to Key type
+
 ## 0.3.0 (February 8, 2020)
 
 * General API and documentation improvements, including the support of custom
diff --git a/Cargo.lock b/Cargo.lock
index 0a7dbba..a88bf56 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "aead"
 version = "0.3.2"
@@ -571,7 +573,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 [[package]]
 name = "opaque-ke"
-version = "0.3.0"
+version = "0.3.1"
 dependencies = [
  "anyhow",
  "base64",
@@ -589,7 +591,6 @@ dependencies = [
  "lazy_static",
  "proptest",
  "rand 0.8.3",
- "rand_core 0.6.1",
  "rustyline",
  "scrypt",
  "serde_json",
diff --git a/Cargo.toml b/Cargo.toml
index 0ad34ce..9b05912 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "opaque-ke"
-version = "0.3.0"
+version = "0.3.1"
 repository = "https://github.com/novifinancial/opaque-ke"
 keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
 description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
@@ -25,7 +25,7 @@ generic-bytes = { version = "0.1.0" }
 generic-bytes-derive = { version = "0.1.0" }
 hkdf = "0.10.0"
 hmac = "0.10.1"
-rand_core = { version = "0.6.0", features = ["getrandom"] }
+rand = "0.8.3"
 scrypt = { version = "0.5.0", optional = true }
 subtle = { version = "2.3.0", default-features = false }
 thiserror = "1.0.22"
@@ -41,7 +41,6 @@ lazy_static = "1.4.0"
 serde_json = "1.0.60"
 sha2 = "0.9.2"
 proptest = "0.10.1"
-rand = "0.8"
 rustyline = "7.0.0"
 
 [[bench]]
diff --git a/README.md b/README.md
index 7c9b136..23e16d7 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Installation
 Add the following line to the dependencies of your `Cargo.toml`:
 
 ```
-opaque-ke = "0.3.0"
+opaque-ke = "0.3.1"
 ```
 
 Resources
diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs
index 8ef5256..7ad85c8 100644
--- a/examples/digital_locker.rs
+++ b/examples/digital_locker.rs
@@ -26,17 +26,18 @@
 
 use chacha20poly1305::aead::{Aead, NewAead};
 use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
-use rand_core::{OsRng, RngCore};
 use rustyline::error::ReadlineError;
 use rustyline::Editor;
 use std::convert::TryFrom;
 use std::process::exit;
 
 use opaque_ke::{
-    ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
-    ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
-    CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
-    RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
+    ciphersuite::CipherSuite,
+    rand::{rngs::OsRng, RngCore},
+    ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistration,
+    ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
+    CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
+    ServerLoginStartParameters, ServerRegistration,
 };
 
 // The ciphersuite trait allows to specify the underlying primitives
diff --git a/examples/simple_login.rs b/examples/simple_login.rs
index b5e3c2e..598701f 100644
--- a/examples/simple_login.rs
+++ b/examples/simple_login.rs
@@ -20,7 +20,6 @@
 //! messages over "the wire" to the server. These bytes are serialized
 //! and explicitly annotated in the below functions.
 
-use rand_core::OsRng;
 use rustyline::error::ReadlineError;
 use rustyline::Editor;
 use std::collections::HashMap;
@@ -28,10 +27,11 @@ use std::convert::TryFrom;
 use std::process::exit;
 
 use opaque_ke::{
-    ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
-    ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
-    CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
-    RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
+    ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters,
+    ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters,
+    CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
+    RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters,
+    ServerRegistration,
 };
 
 // The ciphersuite trait allows to specify the underlying primitives
diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs
index adb44d4..f1b9b24 100644
--- a/src/ciphersuite.rs
+++ b/src/ciphersuite.rs
@@ -11,7 +11,7 @@ use crate::{
 };
 use digest::Digest;
 
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 
 /// Configures the underlying primitives used in OPAQUE
 /// * `Group`: a finite cyclic group along with a point representation, along
diff --git a/src/envelope.rs b/src/envelope.rs
index e42dbc2..380faf9 100644
--- a/src/envelope.rs
+++ b/src/envelope.rs
@@ -12,7 +12,7 @@ use digest::Digest;
 use generic_array::{typenum::Unsigned, GenericArray};
 use hkdf::Hkdf;
 use hmac::{Hmac, Mac, NewMac};
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 use std::convert::TryFrom;
 
 // Constant string used as salt for HKDF computation
@@ -318,7 +318,7 @@ pub(crate) fn mode_from_ids(optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> InnerE
 #[cfg(test)]
 mod tests {
     use super::*;
-    use rand_core::OsRng;
+    use rand::rngs::OsRng;
 
     #[test]
     fn seal_and_open() {
diff --git a/src/group.rs b/src/group.rs
index 90d803a..3f077d1 100644
--- a/src/group.rs
+++ b/src/group.rs
@@ -19,7 +19,7 @@ use generic_array::{
 };
 use std::convert::TryInto;
 
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 use std::ops::Mul;
 use zeroize::Zeroize;
 
diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs
index cfe7b00..c88ad01 100644
--- a/src/key_exchange/traits.rs
+++ b/src/key_exchange/traits.rs
@@ -9,7 +9,7 @@ use crate::{
     hash::Hash,
     keypair::Key,
 };
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 
 use std::convert::TryFrom;
 
diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs
index 8520723..30a7b32 100644
--- a/src/key_exchange/tripledh.rs
+++ b/src/key_exchange/tripledh.rs
@@ -23,7 +23,7 @@ use generic_array::{
 use generic_bytes::SizedBytes;
 use hkdf::Hkdf;
 use hmac::{Hmac, Mac, NewMac};
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 
 use std::convert::TryFrom;
 
diff --git a/src/keypair.rs b/src/keypair.rs
index 25b7b2a..c26a189 100644
--- a/src/keypair.rs
+++ b/src/keypair.rs
@@ -14,13 +14,14 @@ use generic_bytes_derive::TryFromForSizedBytes;
 use proptest::prelude::*;
 #[cfg(test)]
 use rand::{rngs::StdRng, SeedableRng};
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 use std::fmt::Debug;
 use std::marker::PhantomData;
 use std::ops::Deref;
 
-// Pub(crate) convenience extension trait of SizedBytes for our purposes
-pub(crate) trait SizedBytesExt: SizedBytes {
+/// Convenience extension trait of SizedBytes
+pub trait SizedBytesExt: SizedBytes {
+    /// Convert from bytes
     fn from_bytes(bytes: &[u8]) -> Result<Self, TryFromSizedBytesError> {
         <Self as SizedBytes>::from_arr(GenericArray::from_slice(bytes))
     }
@@ -118,7 +119,7 @@ impl<G: Group + Debug> KeyPair<G> {
     }
 }
 
-/// A minimalist key type built around [u8;32]
+/// A minimalist key type built around a \[u8; 32\]
 #[derive(Debug, PartialEq, Eq, Clone, TryFromForSizedBytes)]
 #[ErrorType = "::generic_bytes::TryFromSizedBytesError"]
 #[repr(transparent)]
diff --git a/src/lib.rs b/src/lib.rs
index 2833f8f..c53240c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -47,7 +47,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! use rand_core::{OsRng, RngCore};
+//! use rand::{rngs::OsRng, RngCore};
 //! let mut rng = OsRng;
 //! let server_kp = Default::generate_random_keypair(&mut rng);
 //! # Ok::<(), ProtocolError>(())
@@ -80,7 +80,7 @@
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
 //! use opaque_ke::ClientRegistration;
-//! use rand_core::{OsRng, RngCore};
+//! use rand::{rngs::OsRng, RngCore};
 //! let mut client_rng = OsRng;
 //! let client_registration_start_result = ClientRegistration::<Default>::start(
 //!     &mut client_rng,
@@ -109,7 +109,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -146,7 +146,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -184,7 +184,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -225,7 +225,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! use opaque_ke::{ClientLogin, ClientLoginStartParameters};
 //! let mut client_rng = OsRng;
 //! let client_login_start_result = ClientLogin::<Default>::start(
@@ -258,7 +258,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -307,7 +307,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -354,7 +354,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -430,7 +430,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -507,7 +507,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -572,7 +572,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -607,7 +607,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -655,7 +655,7 @@
 //! #     type Hash = sha2::Sha512;
 //! #     type SlowHash = opaque_ke::slow_hash::NoOpHash;
 //! # }
-//! # use rand_core::{OsRng, RngCore};
+//! # use rand::{rngs::OsRng, RngCore};
 //! # let mut client_rng = OsRng;
 //! # let client_registration_start_result = ClientRegistration::<Default>::start(
 //! #     &mut client_rng,
@@ -749,6 +749,8 @@ mod tests;
 
 // Exports
 
+pub use rand;
+
 pub use crate::messages::{
     CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
     RegistrationResponse, RegistrationUpload,
diff --git a/src/messages.rs b/src/messages.rs
index fe97e93..090a3b3 100644
--- a/src/messages.rs
+++ b/src/messages.rs
@@ -13,7 +13,6 @@ use crate::{
         PakeError, ProtocolError,
     },
     group::Group,
-    hash::Hash,
     key_exchange::traits::{KeyExchange, ToBytes},
     keypair::{Key, KeyPair, SizedBytesExt},
     serialization::{serialize, tokenize},
@@ -21,32 +20,31 @@ use crate::{
 use generic_array::{typenum::Unsigned, GenericArray};
 use generic_bytes::SizedBytes;
 use std::convert::TryFrom;
-use std::marker::PhantomData;
 
 // Messages
 // =========
 
 /// The message sent by the client to the server, to initiate registration
-pub struct RegistrationRequest<Grp> {
+pub struct RegistrationRequest<CS: CipherSuite> {
     /// blinded password information
-    pub(crate) alpha: Grp,
+    pub(crate) alpha: CS::Group,
 }
 
-impl<Grp: Group> TryFrom<&[u8]> for RegistrationRequest<Grp> {
+impl<CS: CipherSuite> TryFrom<&[u8]> for RegistrationRequest<CS> {
     type Error = ProtocolError;
     fn try_from(first_message_bytes: &[u8]) -> Result<Self, Self::Error> {
-        let elem_len = Grp::ElemLen::to_usize();
+        let elem_len = <CS::Group as Group>::ElemLen::to_usize();
         let checked_slice = check_slice_size(first_message_bytes, elem_len, "first_message_bytes")?;
 
         // Check that the message is actually containing an element of the
         // correct subgroup
         let arr = GenericArray::from_slice(&checked_slice[checked_slice.len() - elem_len..]);
-        let alpha = Grp::from_element_slice(arr)?;
+        let alpha = CS::Group::from_element_slice(arr)?;
         Ok(Self { alpha })
     }
 }
 
-impl<Grp: Group> RegistrationRequest<Grp> {
+impl<CS: CipherSuite> RegistrationRequest<CS> {
     /// Byte representation for the registration request
     pub fn to_bytes(&self) -> Vec<u8> {
         self.alpha.to_arr().to_vec()
@@ -59,39 +57,36 @@ impl<Grp: Group> RegistrationRequest<Grp> {
 
     /// Deserialization from bytes
     pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
-        let checked_slice =
-            check_slice_size(&input, Grp::ElemLen::to_usize(), "first_message_bytes")?;
+        let elem_len = <CS::Group as Group>::ElemLen::to_usize();
+        let checked_slice = check_slice_size(&input, elem_len, "first_message_bytes")?;
         // Check that the message is actually containing an element of the
         // correct subgroup
         let arr = GenericArray::from_slice(checked_slice);
-        let alpha = Grp::from_element_slice(arr)?;
+        let alpha = CS::Group::from_element_slice(arr)?;
         Ok(Self { alpha })
     }
 }
 
 /// The answer sent by the server to the user, upon reception of the
 /// registration attempt
-pub struct RegistrationResponse<Grp> {
+pub struct RegistrationResponse<CS: CipherSuite> {
     /// The server's oprf output
-    pub(crate) beta: Grp,
+    pub(crate) beta: CS::Group,
     /// Server's static public key
     pub(crate) server_s_pk: Vec<u8>,
 }
 
-impl<Grp> TryFrom<&[u8]> for RegistrationResponse<Grp>
-where
-    Grp: Group,
-{
+impl<CS: CipherSuite> TryFrom<&[u8]> for RegistrationResponse<CS> {
     type Error = ProtocolError;
 
     fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
-        let elem_len = Grp::ElemLen::to_usize();
+        let elem_len = <CS::Group as Group>::ElemLen::to_usize();
         let checked_slice = check_slice_size_atleast(bytes, elem_len, "second_message_bytes")?;
 
         // Check that the message is actually containing an element of the
         // correct subgroup
         let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
-        let beta = Grp::from_element_slice(arr)?;
+        let beta = CS::Group::from_element_slice(arr)?;
 
         // FIXME check public key bytes
         let server_s_pk = checked_slice[elem_len..].to_vec();
@@ -100,10 +95,7 @@ where
     }
 }
 
-impl<Grp> RegistrationResponse<Grp>
-where
-    Grp: Group,
-{
+impl<CS: CipherSuite> RegistrationResponse<CS> {
     /// Byte representation for the registration response message. This does not
     /// include the envelope credentials format
     pub fn to_bytes(&self) -> Vec<u8> {
@@ -120,15 +112,15 @@ where
 
     /// Deserialization from bytes
     pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
-        let checked_slice =
-            check_slice_size_atleast(&input, Grp::ElemLen::to_usize(), "second_message_bytes")?;
+        let elem_len = <CS::Group as Group>::ElemLen::to_usize();
+        let checked_slice = check_slice_size_atleast(&input, elem_len, "second_message_bytes")?;
 
         // Check that the message is actually containing an element of the
         // correct subgroup
-        let arr = GenericArray::from_slice(&checked_slice[..Grp::ElemLen::to_usize()]);
-        let beta = Grp::from_element_slice(arr)?;
+        let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
+        let beta = CS::Group::from_element_slice(arr)?;
 
-        let (server_s_pk, remainder) = tokenize(&checked_slice[Grp::ElemLen::to_usize()..], 2)?;
+        let (server_s_pk, remainder) = tokenize(&checked_slice[elem_len..], 2)?;
         if !remainder.is_empty() {
             return Err(PakeError::SerializationError.into());
         }
@@ -139,38 +131,36 @@ where
 
 /// The final message from the client, containing sealed cryptographic
 /// identifiers
-pub struct RegistrationUpload<D: Hash, G: Group> {
+pub struct RegistrationUpload<CS: CipherSuite> {
     /// The "envelope" generated by the user, containing sealed
     /// cryptographic identifiers
-    pub(crate) envelope: Envelope<D>,
+    pub(crate) envelope: Envelope<CS::Hash>,
     /// The user's public key
     pub(crate) client_s_pk: Key,
-    pub(crate) _g: PhantomData<G>,
 }
 
-impl<D: Hash, G: Group> TryFrom<&[u8]> for RegistrationUpload<D, G> {
+impl<CS: CipherSuite> TryFrom<&[u8]> for RegistrationUpload<CS> {
     type Error = ProtocolError;
 
     fn try_from(third_message_bytes: &[u8]) -> Result<Self, Self::Error> {
         let key_len = <Key as SizedBytes>::Len::to_usize();
-        let envelope_size = key_len + Envelope::<D>::additional_size();
+        let envelope_size = key_len + Envelope::<CS::Hash>::additional_size();
         let checked_bytes = check_slice_size(
             third_message_bytes,
             envelope_size + key_len,
             "third_message",
         )?;
         let unchecked_client_s_pk = Key::from_bytes(&checked_bytes[envelope_size..])?;
-        let client_s_pk = KeyPair::<G>::check_public_key(unchecked_client_s_pk)?;
+        let client_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_client_s_pk)?;
 
         Ok(Self {
-            envelope: Envelope::<D>::from_bytes(&checked_bytes[..envelope_size])?,
+            envelope: Envelope::<CS::Hash>::from_bytes(&checked_bytes[..envelope_size])?,
             client_s_pk,
-            _g: PhantomData,
         })
     }
 }
 
-impl<D: Hash, G: Group> RegistrationUpload<D, G> {
+impl<CS: CipherSuite> RegistrationUpload<CS> {
     /// Serialization into bytes
     pub fn serialize(&self) -> Vec<u8> {
         let mut message: Vec<u8> = Vec::new();
@@ -182,7 +172,7 @@ impl<D: Hash, G: Group> RegistrationUpload<D, G> {
     /// Deserialization from bytes
     pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
         let (client_s_pk, remainder) = tokenize(&input, 2)?;
-        let (envelope, remainder) = Envelope::<D>::deserialize(&remainder)?;
+        let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&remainder)?;
 
         if !remainder.is_empty() {
             return Err(PakeError::SerializationError.into());
@@ -190,8 +180,7 @@ impl<D: Hash, G: Group> RegistrationUpload<D, G> {
 
         Ok(Self {
             envelope,
-            client_s_pk: KeyPair::<G>::check_public_key(Key::from_bytes(&client_s_pk)?)?,
-            _g: PhantomData,
+            client_s_pk: KeyPair::<CS::Group>::check_public_key(Key::from_bytes(&client_s_pk)?)?,
         })
     }
 }
diff --git a/src/opaque.rs b/src/opaque.rs
index e52e4ef..9f41bd2 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -23,7 +23,7 @@ use crate::{
 use digest::Digest;
 use generic_array::{typenum::Unsigned, GenericArray};
 use generic_bytes::SizedBytes;
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 use std::{convert::TryFrom, marker::PhantomData};
 use zeroize::Zeroize;
 
@@ -96,7 +96,7 @@ impl Default for ClientRegistrationFinishParameters {
 /// Contains the fields that are returned by a client registration start
 pub struct ClientRegistrationStartResult<CS: CipherSuite> {
     /// The registration request message to be sent to the server
-    pub message: RegistrationRequest<CS::Group>,
+    pub message: RegistrationRequest<CS>,
     /// The client state that must be persisted in order to complete registration
     pub state: ClientRegistration<CS>,
 }
@@ -112,7 +112,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
     /// ```
     /// use opaque_ke::ClientRegistration;
     /// # use opaque_ke::errors::ProtocolError;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -132,19 +132,18 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
         let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, blinding_factor_rng)?;
 
         Ok(ClientRegistrationStartResult {
-            message: RegistrationRequest::<CS::Group> { alpha },
+            message: RegistrationRequest::<CS> { alpha },
             state: Self { token },
         })
     }
 }
 
 /// Contains the fields that are returned by a client registration finish
-pub struct ClientRegistrationFinishResult<D: Hash, G: Group> {
+pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
     /// The registration upload message to be sent to the server
-    pub message: RegistrationUpload<D, G>,
+    pub message: RegistrationUpload<CS>,
     /// The export key output by client registration
-    pub export_key: GenericArray<u8, <D as Digest>::OutputSize>,
-    _g: PhantomData<G>,
+    pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
 }
 
 impl<CS: CipherSuite> ClientRegistration<CS> {
@@ -160,7 +159,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
     /// use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
     /// # use opaque_ke::errors::ProtocolError;
     /// # use opaque_ke::keypair::KeyPair;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -182,9 +181,9 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
     pub fn finish<R: CryptoRng + RngCore>(
         self,
         rng: &mut R,
-        r2: RegistrationResponse<CS::Group>,
+        r2: RegistrationResponse<CS>,
         params: ClientRegistrationFinishParameters,
-    ) -> Result<ClientRegistrationFinishResult<CS::Hash, CS::Group>, ProtocolError> {
+    ) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
         let optional_ids = match params {
             ClientRegistrationFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)),
             ClientRegistrationFinishParameters::Default => None,
@@ -206,10 +205,8 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
             message: RegistrationUpload {
                 envelope,
                 client_s_pk: client_static_keypair.public().clone(),
-                _g: PhantomData,
             },
             export_key,
-            _g: PhantomData,
         })
     }
 }
@@ -245,7 +242,7 @@ impl<CS: CipherSuite> Drop for ClientLogin<CS> {
 /// Contains the fields that are returned by a server registration start
 pub struct ServerRegistrationStartResult<CS: CipherSuite> {
     /// The registration resposne message to send to the client
-    pub message: RegistrationResponse<CS::Group>,
+    pub message: RegistrationResponse<CS>,
     /// The state that the server must keep in order to complete registration
     pub state: ServerRegistration<CS>,
 }
@@ -318,7 +315,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
     /// ```
     /// use opaque_ke::*;
     /// # use opaque_ke::errors::ProtocolError;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -336,7 +333,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
     /// ```
     pub fn start<R: RngCore + CryptoRng>(
         rng: &mut R,
-        message: RegistrationRequest<CS::Group>,
+        message: RegistrationRequest<CS>,
         server_s_pk: &Key,
     ) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
         // RFC: generate oprf_key (salt) and v_u = g^oprf_key
@@ -369,7 +366,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
     /// ```
     /// use opaque_ke::{*, keypair::KeyPair};
     /// # use opaque_ke::errors::ProtocolError;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -388,10 +385,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
     /// let client_record = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
     /// # Ok::<(), ProtocolError>(())
     /// ```
-    pub fn finish(
-        self,
-        message: RegistrationUpload<CS::Hash, CS::Group>,
-    ) -> Result<Self, ProtocolError> {
+    pub fn finish(self, message: RegistrationUpload<CS>) -> Result<Self, ProtocolError> {
         Ok(Self {
             envelope: Some(message.envelope),
             client_s_pk: Some(message.client_s_pk),
@@ -518,7 +512,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
     /// ```
     /// use opaque_ke::{ClientLogin, ClientLoginStartParameters};
     /// # use opaque_ke::errors::ProtocolError;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -568,7 +562,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
     /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
     /// # use opaque_ke::errors::ProtocolError;
     /// # use opaque_ke::keypair::KeyPair;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -723,7 +717,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
     /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
     /// # use opaque_ke::errors::ProtocolError;
     /// # use opaque_ke::keypair::KeyPair;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
@@ -828,7 +822,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
     /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
     /// # use opaque_ke::errors::ProtocolError;
     /// # use opaque_ke::keypair::KeyPair;
-    /// use rand_core::{OsRng, RngCore};
+    /// use rand::{rngs::OsRng, RngCore};
     /// use opaque_ke::ciphersuite::CipherSuite;
     /// struct Default;
     /// impl CipherSuite for Default {
diff --git a/src/oprf.rs b/src/oprf.rs
index 7027277..c3b3f01 100644
--- a/src/oprf.rs
+++ b/src/oprf.rs
@@ -9,7 +9,7 @@ use crate::{
 };
 use digest::Digest;
 use generic_array::GenericArray;
-use rand_core::{CryptoRng, RngCore};
+use rand::{CryptoRng, RngCore};
 
 /// Used to store the OPRF input and blinding factor
 pub struct Token<Grp: Group> {
@@ -117,7 +117,7 @@ mod tests {
     use crate::group::Group;
     use curve25519_dalek::ristretto::RistrettoPoint;
     use generic_array::{arr, GenericArray};
-    use rand_core::OsRng;
+    use rand::rngs::OsRng;
     use sha2::Sha512;
 
     fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray<u8, <Sha512 as Digest>::OutputSize> {


Test patch:
diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs
index 4d96270..22bb3c9 100644
--- a/src/serialization/tests.rs
+++ b/src/serialization/tests.rs
@@ -20,7 +20,7 @@ use curve25519_dalek::ristretto::RistrettoPoint;
 use generic_array::typenum::Unsigned;
 use generic_bytes::SizedBytes;
 use proptest::{collection::vec, prelude::*};
-use rand_core::{OsRng, RngCore};
+use rand::{rngs::OsRng, RngCore};
 
 use sha2::Digest;
 use std::convert::TryFrom;
@@ -103,7 +103,7 @@ fn register_first_message_roundtrip() {
     let mut input = Vec::new();
     input.extend_from_slice(pt_bytes.as_slice());
 
-    let r1 = RegistrationRequest::<RistrettoPoint>::deserialize(input.as_slice()).unwrap();
+    let r1 = RegistrationRequest::<Default>::deserialize(input.as_slice()).unwrap();
     let r1_bytes = r1.serialize();
     assert_eq!(input, r1_bytes);
 }
@@ -123,7 +123,7 @@ fn register_second_message_roundtrip() {
     input.extend_from_slice(&pubkey_length.to_be_bytes()[std::mem::size_of::<usize>() - 2..]);
     input.extend_from_slice(&pubkey_bytes.as_slice());
 
-    let r2 = RegistrationResponse::<RistrettoPoint>::deserialize(input.as_slice()).unwrap();
+    let r2 = RegistrationResponse::<Default>::deserialize(input.as_slice()).unwrap();
     let r2_bytes = r2.serialize();
     assert_eq!(input, r2_bytes);
 }
@@ -157,7 +157,7 @@ fn register_third_message_roundtrip() {
     input.extend_from_slice(&pubkey_bytes[..]);
     input.extend_from_slice(&envelope_bytes);
 
-    let r3 = RegistrationUpload::<sha2::Sha512, RistrettoPoint>::deserialize(&input[..]).unwrap();
+    let r3 = RegistrationUpload::<Default>::deserialize(&input[..]).unwrap();
     let r3_bytes = r3.serialize();
     assert_eq!(input, r3_bytes);
 }
@@ -359,17 +359,17 @@ fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..std::mem::size_of::<usize>()))
 
 #[test]
 fn test_nocrash_register_first_message(bytes in vec(any::<u8>(), 0..200)) {
-    RegistrationRequest::<RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
+    RegistrationRequest::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
 }
 
 #[test]
 fn test_nocrash_register_second_message(bytes in vec(any::<u8>(), 0..200)) {
-    RegistrationResponse::<RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
+    RegistrationResponse::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
 }
 
 #[test]
 fn test_nocrash_register_third_message(bytes in vec(any::<u8>(), 0..200)) {
-    RegistrationUpload::<sha2::Sha512, RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
+    RegistrationUpload::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
 }
 
 #[test]
diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs
index 65ac922..7a49e33 100644
--- a/src/tests/full_test.rs
+++ b/src/tests/full_test.rs
@@ -17,7 +17,7 @@ use crate::{
 use curve25519_dalek::ristretto::RistrettoPoint;
 use generic_array::typenum::Unsigned;
 use generic_bytes::SizedBytes;
-use rand_core::{OsRng, RngCore};
+use rand::{rngs::OsRng, RngCore};
 use serde_json::Value;
 use std::convert::TryFrom;
 
diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs
index 9af7e64..e34157d 100644
--- a/src/tests/mock_rng.rs
+++ b/src/tests/mock_rng.rs
@@ -3,7 +3,7 @@
 // This source code is licensed under the MIT license found in the
 // LICENSE file in the root directory of this source tree.
 
-use rand_core::{CryptoRng, Error, RngCore};
+use rand::{CryptoRng, Error, RngCore};
 use std::cmp::min;
 
 /// A simple implementation of `RngCore` for testing purposes.