From 41cd80ccb5e6a43e1fb243fa0b1651e0db232182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 3 Jul 2020 18:28:33 -0400 Subject: [PATCH] Make oprf::generate_oprf1 generic in the Digest, as long as it matches the hash-to-curve intake of the group We used to have three problems: - overuse of the ::OutputSize, which is just, well, U32. Sometimes used as a parameter (as in generate_oprf1), sometimes as a constant (as in generate_oprf3). - the `hash_to_curve` operation for `RistrettoPoint` which requires 64 bits of input entropy, is fed 64 bits of which the last 32 are zero, - the `hash_to_curve` operation for `Curve25519Point` which requires 32 bits of input entropy, is fed 64 bits of which the last 32 are discarded, This corrects all three and uses U32 where the size of the digest is not meant to be a constraint. Addresses #15 partially. --- Cargo.lock | 1 + Cargo.toml | 1 + src/ciphersuite.rs | 15 ++++++---- src/envelope.rs | 36 ++++++++++-------------- src/group.rs | 2 +- src/key_exchange.rs | 8 +++--- src/lib.rs | 10 +++++++ src/opaque.rs | 16 +++++++++-- src/oprf.rs | 56 +++++++++++++++++++++++++------------ src/slow_hash.rs | 15 +++------- src/tests/opaque_ke_test.rs | 1 + src/tests/serialization.rs | 1 + 12 files changed, 99 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7d61d6..9a0872c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,6 +202,7 @@ dependencies = [ "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 0881562..6966080 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ slow-hash = ["scrypt"] [dependencies] aead = "0.3.1" curve25519-dalek = "2.1.0" +digest = "0.9" generic-array = "0.14.2" hkdf = "0.9.0-alpha.0" hmac = "0.8.0" diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 9f96968..10e2938 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -9,17 +9,22 @@ use crate::{ errors::InternalPakeError, group::Group, keypair::{Key, KeyPair}, + oprf::HkdfDigest, slow_hash::SlowHash, }; -use generic_array::typenum::{U32, U64}; +use digest::FixedOutput; +use generic_array::typenum::{U32}; use rand_core::{CryptoRng, RngCore}; /// Configures the underlying primitives used in OPAQUE -/// * Group: a finite cyclic group along with a point representation -/// * KeyFormat: a keypair type composed of public and private components -/// * SlowHash: a slow hashing function, typically used for password hashing +/// * `Digest`: a digest suitable for use in an Hkdf, with an output length equal +/// to the input of the hash-to-curve function of the `Group` parameter. +/// * `Group`: a finite cyclic group along with a point representation +/// * `KeyFormat`: a keypair type composed of public and private components +/// * `SlowHash`: a slow hashing function, typically used for password hashing pub trait CipherSuite { - type Group: Group; + type Digest: HkdfDigest; + type Group: Group::OutputSize>; type KeyFormat: KeyPair + PartialEq; type SlowHash: SlowHash; diff --git a/src/envelope.rs b/src/envelope.rs index bb6bbb5..7f481e2 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -11,14 +11,16 @@ use generic_array::{ use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; +use sha2::Sha256; // Constant string used as salt for HKDF computation const STR_ENVU: &[u8] = b"EnvU"; /// The length of the "export key" output by the client registration /// and login finish steps -pub(crate) type ExportKeySize = ::OutputSize; +pub(crate) type ExportKeySize = U32; + +const NONCE_LEN: usize = 32; /// This struct is an instantiation of the envelope as described in /// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4 @@ -33,42 +35,32 @@ pub(crate) type ExportKeySize = ::OutputSize; pub(crate) struct Envelope { nonce: Vec, ciphertext: Vec, - hmac: Vec, + hmac: GenericArray, } -type NonceLen = U32; - impl Envelope { /// The additional number of bytes added to the plaintext pub(crate) fn additional_size() -> usize { - Self::nonce_size() + ::OutputSize::to_usize() + NONCE_LEN + U32::to_usize() } fn hmac_key_size() -> usize { - ::OutputSize::to_usize() + U32::to_usize() } fn hmac_size() -> usize { - ::OutputSize::to_usize() - } - - fn nonce_size() -> usize { - NonceLen::to_usize() + U32::to_usize() } fn export_key_size() -> usize { ExportKeySize::to_usize() } - pub(crate) fn new( - nonce: Vec, - ciphertext: Vec, - hmac: &GenericArray::OutputSize>, - ) -> Self { + pub(crate) fn new(nonce: Vec, ciphertext: Vec, hmac: GenericArray) -> Self { Self { nonce, ciphertext, - hmac: hmac.to_vec(), + hmac, } } @@ -76,13 +68,13 @@ impl Envelope { /// nonce | ciphertext | hmac /// nonce_size bytes | variable length | hmac_size bytes pub(crate) fn from_bytes(bytes: &[u8]) -> Result { - let ciphertext_start = Self::nonce_size(); + let ciphertext_start = NONCE_LEN; let ciphertext_end = bytes.len() - Self::hmac_size(); Ok(Self::new( bytes[..ciphertext_start].to_vec(), bytes[ciphertext_start..ciphertext_end].to_vec(), - GenericArray::from_slice(&bytes[ciphertext_end..]), + GenericArray::clone_from_slice(&bytes[ciphertext_end..]), )) } @@ -98,7 +90,7 @@ impl Envelope { aad: &[u8], rng: &mut R, ) -> Result<(Self, GenericArray), InternalPakeError> { - let mut nonce = vec![0u8; Self::nonce_size()]; + let mut nonce = vec![0u8; NONCE_LEN]; rng.fill_bytes(&mut nonce); let h = Hkdf::::new(Some(&nonce), &key); @@ -121,7 +113,7 @@ impl Envelope { hmac.update(&aad); Ok(( - Self::new(nonce, ciphertext.to_vec(), &hmac.finalize().into_bytes()), + Self::new(nonce, ciphertext.to_vec(), hmac.finalize().into_bytes()), *GenericArray::from_slice(&export_key), )) } diff --git a/src/group.rs b/src/group.rs index 44547d2..27d0b39 100644 --- a/src/group.rs +++ b/src/group.rs @@ -140,7 +140,7 @@ impl Group for EdwardsPoint { *GenericArray::from_slice(c.as_bytes()) } - type UniformBytesLen = U64; + type UniformBytesLen = U32; fn hash_to_curve(uniform_bytes: &GenericArray) -> Self { let mut result = [0u8; 32]; let mut counter = 0; diff --git a/src/key_exchange.rs b/src/key_exchange.rs index 39ca876..494f5a9 100644 --- a/src/key_exchange.rs +++ b/src/key_exchange.rs @@ -9,7 +9,7 @@ use crate::{ sized_bytes_using_constant_and_try_from, }; use generic_array::{ - typenum::{U64, U96}, + typenum::{U32, U64, U96}, GenericArray, }; use hkdf::Hkdf; @@ -199,9 +199,9 @@ struct TripleDHComponents { // Consists of a shared secret, followed by two mac keys type TripleDHDerivationResult = ( - GenericArray::OutputSize>, - GenericArray::OutputSize>, - GenericArray::OutputSize>, + GenericArray, + GenericArray, + GenericArray, ); // Internal function which takes the public and private components of the client and server keypairs, along diff --git a/src/lib.rs b/src/lib.rs index cea3e78..897889d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ //! use opaque_ke::ciphersuite::CipherSuite; //! struct Default; //! impl CipherSuite for Default { +//! type Digest = sha2::Sha512; //! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -41,6 +42,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -72,6 +74,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -100,6 +103,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -131,6 +135,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -164,6 +169,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -205,6 +211,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -233,6 +240,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -276,6 +284,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -331,6 +340,7 @@ //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { +//! # type Digest = sha2::Sha512; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeyFormat = opaque_ke::keypair::X25519KeyPair; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; diff --git a/src/opaque.rs b/src/opaque.rs index 5b306cc..82493e9 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -290,6 +290,7 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -306,7 +307,11 @@ impl ClientRegistration { let OprfClientBytes { alpha, blinding_factor, - } = oprf::generate_oprf1::(&password, pepper, blinding_factor_rng)?; + } = oprf::generate_oprf1::( + &password, + pepper, + blinding_factor_rng, + )?; Ok(( RegisterFirstMessage:: { alpha }, @@ -340,6 +345,7 @@ impl ClientRegistration { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -503,6 +509,7 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -549,6 +556,7 @@ where /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -637,6 +645,7 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -653,7 +662,7 @@ impl ClientLogin { let OprfClientBytes { alpha, blinding_factor, - } = oprf::generate_oprf1::(&password, pepper, rng)?; + } = oprf::generate_oprf1::(&password, pepper, rng)?; let (ke1_state, ke1_message) = generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?; @@ -688,6 +697,7 @@ impl ClientLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -783,6 +793,7 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -852,6 +863,7 @@ impl ServerLogin { /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { + /// type Digest = sha2::Sha512; /// type Group = curve25519_dalek::ristretto::RistrettoPoint; /// type KeyFormat = opaque_ke::keypair::X25519KeyPair; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; diff --git a/src/oprf.rs b/src/oprf.rs index 98c066d..c9542c8 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -4,29 +4,51 @@ // LICENSE file in the root directory of this source tree. use crate::{errors::InternalPakeError, group::Group}; -use generic_array::{typenum::U64, GenericArray}; +use digest::{BlockInput, FixedOutput, Reset, Update}; +use generic_array::{typenum::U32, ArrayLength, GenericArray}; use hkdf::Hkdf; use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; pub(crate) struct OprfClientBytes { pub(crate) alpha: Grp, pub(crate) blinding_factor: Grp::Scalar, } +/// The `HkDFDigest` trait specifies the interface required for a parameter of `hkdf::Hkdf`. +/// +/// It's a convenience wrapper around [`Blockinput`], [`Update`], [`FixedOutput`], [`Reset`], +/// [`Clone`], and [`Default`] traits. +pub trait HkdfDigest: Update + BlockInput + FixedOutput + Reset + Default + Clone +where + Self::BlockSize: ArrayLength, + Self::OutputSize: ArrayLength, +{ +} + +impl HkdfDigest for T +where + T: Update + BlockInput + FixedOutput + Reset + Default + Clone, + T::BlockSize: ArrayLength, + T::OutputSize: ArrayLength, +{ +} + /// Computes the first step for the multiplicative blinding version of DH-OPRF. This /// message is sent from the client (who holds the input) to the server (who holds the OPRF key). /// The client can also pass in an optional "pepper" string to be mixed in with the input through /// an HKDF computation. -pub(crate) fn generate_oprf1>( +pub(crate) fn generate_oprf1< + R: RngCore + CryptoRng, + D: HkdfDigest, + G: Group, +>( input: &[u8], pepper: Option<&[u8]>, blinding_factor_rng: &mut R, ) -> Result, InternalPakeError> { - let (hashed_input, _) = Hkdf::::extract(pepper, &input); - let curve_input: Vec = [hashed_input.as_slice(), &[0u8; 32]].concat(); + let (hashed_input, _) = Hkdf::::extract(pepper, &input); let blinding_factor = G::random_scalar(blinding_factor_rng); - let alpha = G::hash_to_curve(GenericArray::from_slice(&curve_input)) * &blinding_factor; + let alpha = G::hash_to_curve(GenericArray::from_slice(&hashed_input)) * &blinding_factor; Ok(OprfClientBytes { alpha, blinding_factor, @@ -48,10 +70,10 @@ pub(crate) fn generate_oprf3( input: &[u8], point: G, blinding_factor: &G::Scalar, -) -> Result::OutputSize>, InternalPakeError> { +) -> Result, InternalPakeError> { let unblinded = point * &G::scalar_invert(&blinding_factor); let ikm: Vec = [&unblinded.to_arr()[..], input].concat(); - let (prk, _) = Hkdf::::extract(None, &ikm); + let (prk, _) = Hkdf::::extract(None, &ikm); Ok(prk) } @@ -66,14 +88,14 @@ mod tests { use generic_array::{arr, GenericArray}; use hkdf::Hkdf; use rand_core::OsRng; + use sha2::{Digest, Sha256, Sha512}; fn prf( input: &[u8], oprf_key: &[u8; 32], ) -> GenericArray::ElemLen> { - let (hashed_input, _) = Hkdf::::extract(None, &input); - let curve_input: Vec = [hashed_input.as_slice(), &[0u8; 32]].concat(); - let point = RistrettoPoint::hash_to_curve(GenericArray::from_slice(&curve_input)); + let (hashed_input, _) = Hkdf::::extract(None, &input); + let point = RistrettoPoint::hash_to_curve(GenericArray::from_slice(&hashed_input)); let scalar = RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); let res = point * scalar; @@ -90,7 +112,7 @@ mod tests { let OprfClientBytes { alpha, blinding_factor, - } = generate_oprf1::<_, RistrettoPoint>(&input[..], None, &mut rng)?; + } = generate_oprf1::<_, Sha512, RistrettoPoint>(&input[..], None, &mut rng)?; let salt_bytes = arr![ u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -111,17 +133,15 @@ mod tests { let OprfClientBytes { alpha, blinding_factor, - } = generate_oprf1::<_, RistrettoPoint>(&input, None, &mut rng).unwrap(); + } = generate_oprf1::<_, Sha512, RistrettoPoint>(&input, None, &mut rng).unwrap(); let res = generate_oprf3::(&input, alpha, &blinding_factor).unwrap(); - let (hashed_input, _) = Hkdf::::extract(None, &input); - let mut curve_input: Vec = Vec::new(); - curve_input.extend_from_slice(&hashed_input); - curve_input.extend_from_slice(&[0u8; 32]); + let (hashed_input, _) = Hkdf::::extract(None, &input); + // This is because RistrettoPoint is on an obsolete sha2 version let mut bits = [0u8; 64]; let mut hasher = sha2::Sha512::new(); - hasher.update(&curve_input[..]); + Digest::update(&mut hasher, &hashed_input[..]); bits.copy_from_slice(&hasher.finalize()); let point = RistrettoPoint::from_uniform_bytes(&bits); diff --git a/src/slow_hash.rs b/src/slow_hash.rs index 7a502a5..537d521 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -7,33 +7,26 @@ use crate::errors::InternalPakeError; -use generic_array::GenericArray; -use sha2::{Digest, Sha256}; +use generic_array::{typenum::U32, GenericArray}; /// Used for the slow hashing function in OPAQUE pub trait SlowHash { /// Computes the slow hashing function - fn hash( - input: GenericArray::OutputSize>, - ) -> Result, InternalPakeError>; + fn hash(input: GenericArray) -> Result, InternalPakeError>; } /// A no-op hash which simply returns its input pub struct NoOpHash; impl SlowHash for NoOpHash { - fn hash( - input: GenericArray::OutputSize>, - ) -> Result, InternalPakeError> { + fn hash(input: GenericArray) -> Result, InternalPakeError> { Ok(input.to_vec()) } } #[cfg(feature = "slow-hash")] impl SlowHash for scrypt::ScryptParams { - fn hash( - input: GenericArray::OutputSize>, - ) -> Result, InternalPakeError> { + fn hash(input: GenericArray) -> Result, InternalPakeError> { let params = scrypt::ScryptParams::new(15, 8, 1).unwrap(); let mut output = [0u8; 32]; scrypt::scrypt(&input, &[], ¶ms, &mut output) diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index 60c81e9..2d5e9ec 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -23,6 +23,7 @@ use std::convert::TryFrom; struct X255193dhNoSlowHash; impl CipherSuite for X255193dhNoSlowHash { + type Digest = sha2::Sha256; type Group = EdwardsPoint; type KeyFormat = X25519KeyPair; type SlowHash = NoOpHash; diff --git a/src/tests/serialization.rs b/src/tests/serialization.rs index 0a755fe..3690dfd 100644 --- a/src/tests/serialization.rs +++ b/src/tests/serialization.rs @@ -21,6 +21,7 @@ use std::convert::TryFrom; struct Default; impl CipherSuite for Default { + type Digest = sha2::Sha512; type Group = RistrettoPoint; type KeyFormat = crate::keypair::X25519KeyPair; type SlowHash = crate::slow_hash::NoOpHash;