Separate AKE from OPRF take 2 (#222)

* Separate AKE from OPRF

Introduce X25519 implementation

* Rename `AkeGroup` to `KeGroup` and `Group` to `OprfGroup`

* Add documentation to "Overview"
This commit is contained in:
daxpedda
2021-08-04 12:24:46 -07:00
committed by GitHub
parent 10a38bc58b
commit 88673d8e05
14 changed files with 377 additions and 157 deletions
+2 -1
View File
@@ -44,7 +44,8 @@ use opaque_ke::{
#[allow(dead_code)]
struct Default;
impl CipherSuite for Default {
type Group = curve25519_dalek::ristretto::RistrettoPoint;
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
type Hash = sha2::Sha512;
type SlowHash = opaque_ke::slow_hash::NoOpHash;
+2 -1
View File
@@ -37,7 +37,8 @@ use opaque_ke::{
#[allow(dead_code)]
struct Default;
impl CipherSuite for Default {
type Group = curve25519_dalek::ristretto::RistrettoPoint;
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
type Hash = sha2::Sha512;
type SlowHash = opaque_ke::slow_hash::NoOpHash;
+6 -3
View File
@@ -8,9 +8,10 @@
use crate::{group::Group, hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash};
/// Configures the underlying primitives used in OPAQUE
/// * `Group`: a finite cyclic group along with a point representation, along
/// * `OprfGroup`: a finite cyclic group along with a point representation, along
/// with an extension trait PasswordToCurve that allows some customization on
/// how to hash a password to a curve point. See `group::Group`.
/// * `KeGroup`: A `Group` used for the `KeyExchange`.
/// * `KeyExchange`: The key exchange protocol to use in the login step
/// * `Hash`: The main hashing function to use
/// * `SlowHash`: A slow hashing function, typically used for password hashing
@@ -18,9 +19,11 @@ pub trait CipherSuite {
/// A finite cyclic group along with a point representation along with
/// an extension trait PasswordToCurve that allows some customization on
/// how to hash a password to a curve point. See `group::Group`.
type Group: Group;
type OprfGroup: Group;
/// A `Group` used for the `KeyExchange`.
type KeGroup: Group;
/// A key exchange protocol
type KeyExchange: KeyExchange<Self::Hash, Self::Group>;
type KeyExchange: KeyExchange<Self::Hash, Self::KeGroup>;
/// The main hash function use (for HKDF computations and hashing transcripts)
type Hash: Hash;
/// A slow hashing function, typically used for password hashing
+19 -15
View File
@@ -31,15 +31,17 @@ const NONCE_LEN: usize = 32;
fn build_inner_envelope_internal<CS: CipherSuite>(
random_pwd: &[u8],
nonce: &[u8],
) -> Result<PublicKey<CS::Group>, ProtocolError> {
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <PrivateKey<CS::Group> as SizedBytes>::Len::to_usize()];
let mut keypair_seed = vec![0u8; <PrivateKey<CS::KeGroup> as SizedBytes>::Len::to_usize()];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalPakeError::HkdfError)?;
let client_static_keypair =
KeyPair::<CS::Group>::from_private_key_slice(&CS::Group::scalar_as_bytes(
CS::Group::hash_to_scalar::<CS::Hash>(&keypair_seed[..], STR_OPAQUE_HASH_TO_SCALAR)?,
))?;
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
&keypair_seed[..],
STR_OPAQUE_HASH_TO_SCALAR,
)?),
)?;
Ok(client_static_keypair.public().clone())
}
@@ -47,15 +49,17 @@ fn build_inner_envelope_internal<CS: CipherSuite>(
fn recover_keys_internal<CS: CipherSuite>(
random_pwd: &[u8],
nonce: &[u8],
) -> Result<KeyPair<CS::Group>, ProtocolError> {
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <PrivateKey<CS::Group> as SizedBytes>::Len::to_usize()];
let mut keypair_seed = vec![0u8; <PrivateKey<CS::KeGroup> as SizedBytes>::Len::to_usize()];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalPakeError::HkdfError)?;
let client_static_keypair =
KeyPair::<CS::Group>::from_private_key_slice(&CS::Group::scalar_as_bytes(
CS::Group::hash_to_scalar::<CS::Hash>(&keypair_seed[..], STR_OPAQUE_HASH_TO_SCALAR)?,
))?;
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
&keypair_seed[..],
STR_OPAQUE_HASH_TO_SCALAR,
)?),
)?;
Ok(client_static_keypair)
}
@@ -110,7 +114,7 @@ impl_debug_eq_hash_for!(struct Envelope<CS: CipherSuite>, [mode, nonce, hmac]);
// key. This key is also used to derive the export_key parameter, which is technically
// unrelated to the envelope's encrypted and authenticated contents.
pub(crate) struct OpenedEnvelope<CS: CipherSuite> {
pub(crate) client_static_keypair: KeyPair<CS::Group>,
pub(crate) client_static_keypair: KeyPair<CS::KeGroup>,
pub(crate) export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
pub(crate) id_u: Vec<u8>,
pub(crate) id_s: Vec<u8>,
@@ -134,13 +138,13 @@ type SealRawResult<CS> = (
#[cfg(not(test))]
type SealResult<CS> = (
Envelope<CS>,
PublicKey<<CS as CipherSuite>::Group>,
PublicKey<<CS as CipherSuite>::KeGroup>,
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
);
#[cfg(test)]
type SealResult<CS> = (
Envelope<CS>,
PublicKey<<CS as CipherSuite>::Group>,
PublicKey<<CS as CipherSuite>::KeGroup>,
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
Vec<u8>,
);
+1
View File
@@ -10,6 +10,7 @@ mod expand;
#[cfg(feature = "p256")]
pub(crate) mod p256;
mod ristretto;
mod x25519;
use crate::errors::{InternalPakeError, ProtocolError};
use crate::hash::Hash;
+182
View File
@@ -0,0 +1,182 @@
// 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.
use super::Group;
use crate::errors::{InternalPakeError, ProtocolError};
use crate::hash::Hash;
use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar};
use generic_array::{typenum::U32, GenericArray};
use rand::{CryptoRng, RngCore};
/// The implementation of such a subgroup for Ristretto
impl Group for MontgomeryPoint {
const SUITE_ID: usize = 0xFFFF;
fn map_to_curve<H: Hash>(_msg: &[u8], _dst: &[u8]) -> Result<Self, ProtocolError> {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
fn hash_to_scalar<H: Hash>(_input: &[u8], _dst: &[u8]) -> Result<Self::Scalar, ProtocolError> {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
type Scalar = Scalar;
type ScalarLen = U32;
fn from_scalar_slice(
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalPakeError> {
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
}
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng
#[cfg(test)]
{
let mut scalar_bytes = [0u8; 32];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order(scalar_bytes)
}
};
if scalar != Scalar::zero() {
break scalar;
}
}
}
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.to_bytes().into()
}
fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
// The byte length necessary to represent group elements
type ElemLen = U32;
fn from_element_slice(
element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalPakeError> {
Ok(Self(*element_bits.as_ref()))
}
// serialization of a group element
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
self.to_bytes().into()
}
fn base_point() -> Self {
X25519_BASEPOINT
}
fn mult_by_slice(&self, scalar: &GenericArray<u8, Self::ScalarLen>) -> Self {
self * Scalar::from_bits(*scalar.as_ref())
}
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
fn ct_equal(&self, _other: &Self) -> bool {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
}
#[test]
fn test() -> Result<(), ProtocolError> {
use crate::{
errors::PakeError, key_exchange::tripledh::TripleDH, slow_hash::NoOpHash, CipherSuite,
ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
ClientRegistrationStartResult, ServerLogin, ServerLoginStartParameters,
ServerLoginStartResult, ServerRegistration, ServerSetup,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use rand::rngs::OsRng;
struct X25519Sha512NoSlowHash;
impl CipherSuite for X25519Sha512NoSlowHash {
type OprfGroup = RistrettoPoint;
type KeGroup = MontgomeryPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = NoOpHash;
}
const PASSWORD: &[u8] = b"1234";
let server_setup = ServerSetup::<X25519Sha512NoSlowHash>::new(&mut OsRng);
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::start(&mut OsRng, PASSWORD)?;
let message = ServerRegistration::start(&server_setup, message, &[])?.message;
let ClientRegistrationFinishResult {
message,
export_key: register_export_key,
..
} = client.finish(
&mut OsRng,
message,
ClientRegistrationFinishParameters::Default,
)?;
let server_registration = ServerRegistration::finish(message);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult {
message,
state: server,
..
} = ServerLogin::start(
&mut OsRng,
&server_setup,
Some(server_registration),
message,
&[],
ServerLoginStartParameters::default(),
)?;
let ClientLoginFinishResult {
message,
session_key: client_session_key,
export_key: login_export_key,
..
} = client.finish(message, ClientLoginFinishParameters::Default)?;
let server_session_key = server.finish(message)?.session_key;
assert_eq!(register_export_key, login_export_key);
assert_eq!(client_session_key, server_session_key);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult { message, .. } = ServerLogin::start(
&mut OsRng,
&server_setup,
None,
message,
&[],
ServerLoginStartParameters::default(),
)?;
assert!(matches!(
client.finish(message, ClientLoginFinishParameters::Default),
Err(ProtocolError::VerificationError(
PakeError::InvalidLoginError
))
));
Ok(())
}
+1 -1
View File
@@ -411,7 +411,7 @@ impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
)?;
// Check the public key bytes
let server_e_pk = KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
let server_e_pk = KeyPair::<CS::OprfGroup>::check_public_key(PublicKey::from_bytes(
&unchecked_server_e_pk[..key_len],
)?)?;
+2 -1
View File
@@ -462,7 +462,8 @@ mod tests {
struct Default;
impl CipherSuite for Default {
type Group = RistrettoPoint;
type OprfGroup = RistrettoPoint;
type KeGroup = RistrettoPoint;
type KeyExchange = crate::key_exchange::tripledh::TripleDH;
type Hash = sha2::Sha512;
type SlowHash = crate::slow_hash::NoOpHash;
+35 -17
View File
@@ -16,7 +16,9 @@
//!
//! 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 finite cyclic group along with a point representation
//! * for the OPRF and
//! * for the key exchange
//! * a key exchange protocol,
//! * a hashing function, and
//! * a slow hashing function.
@@ -26,7 +28,8 @@
//! use opaque_ke::CipherSuite;
//! struct Default;
//! impl CipherSuite for Default {
//! type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! type Hash = sha2::Sha512;
//! type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -47,7 +50,8 @@
//! # use opaque_ke::ServerSetup;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -79,7 +83,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -109,7 +114,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -146,7 +152,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -184,7 +191,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -226,7 +234,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -259,7 +268,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -312,7 +322,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -357,7 +368,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -432,7 +444,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -509,7 +522,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -574,7 +588,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -611,7 +626,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -660,7 +676,8 @@
//! # use opaque_ke::CipherSuite;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
@@ -737,7 +754,8 @@
//! # use zeroize::Zeroize;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = RistrettoPoint;
//! # type OprfGroup = RistrettoPoint;
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
+38 -38
View File
@@ -28,13 +28,13 @@ use rand::{CryptoRng, RngCore};
/// The message sent by the client to the server, to initiate registration
pub struct RegistrationRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: CS::Group,
pub(crate) alpha: CS::OprfGroup,
}
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::Group {
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
self.alpha
}
}
@@ -46,7 +46,7 @@ impl<CS: CipherSuite> Clone for RegistrationRequest<CS> {
}
}
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [alpha], [CS::Group]);
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [alpha], [CS::OprfGroup]);
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Serialization into bytes
@@ -56,12 +56,12 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let elem_len = <CS::OprfGroup 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 = CS::Group::from_element_slice(arr)?;
let alpha = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
@@ -77,9 +77,9 @@ impl_serialize_and_deserialize_for!(RegistrationRequest);
/// registration attempt
pub struct RegistrationResponse<CS: CipherSuite> {
/// The server's oprf output
pub(crate) beta: CS::Group,
pub(crate) beta: CS::OprfGroup,
/// Server's static public key
pub(crate) server_s_pk: PublicKey<CS::Group>,
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -95,7 +95,7 @@ impl<CS: CipherSuite> Clone for RegistrationResponse<CS> {
impl_debug_eq_hash_for!(
struct RegistrationResponse<CS: CipherSuite>,
[beta, server_s_pk],
[CS::Group],
[CS::OprfGroup],
);
impl<CS: CipherSuite> RegistrationResponse<CS> {
@@ -106,15 +106,15 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <PublicKey<CS::Group> as SizedBytes>::Len::to_usize();
let elem_len = <CS::OprfGroup as Group>::ElemLen::to_usize();
let key_len = <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize();
let checked_slice =
check_slice_size(input, elem_len + key_len, "registration_response_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 = CS::Group::from_element_slice(arr)?;
let beta = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if beta.is_identity() {
@@ -122,7 +122,7 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
}
// Ensure that public key is valid
let server_s_pk = KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
let server_s_pk = KeyPair::<CS::KeGroup>::check_public_key(PublicKey::from_bytes(
&checked_slice[elem_len..],
)?)?;
@@ -132,7 +132,7 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::Group) -> Self {
pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self {
Self {
beta: new_beta,
server_s_pk: self.server_s_pk.clone(),
@@ -151,7 +151,7 @@ pub struct RegistrationUpload<CS: CipherSuite> {
/// The masking key used to mask the envelope
pub(crate) masking_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The user's public key
pub(crate) client_s_pk: PublicKey<CS::Group>,
pub(crate) client_s_pk: PublicKey<CS::KeGroup>,
}
impl_clone_for!(
@@ -176,7 +176,7 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <PublicKey<CS::Group> as SizedBytes>::Len::to_usize();
let key_len = <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize();
let hash_len = <CS::Hash as Digest>::OutputSize::to_usize();
let checked_slice =
check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?;
@@ -186,14 +186,14 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
masking_key: GenericArray::clone_from_slice(
&checked_slice[key_len..key_len + hash_len],
),
client_s_pk: KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
client_s_pk: KeyPair::<CS::KeGroup>::check_public_key(PublicKey::from_bytes(
&checked_slice[..key_len],
)?)?,
})
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng, S: SecretKey<CS::Group>>(
pub(crate) fn dummy<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
rng: &mut R,
server_setup: &ServerSetup<CS, S>,
) -> Self {
@@ -213,8 +213,8 @@ impl_serialize_and_deserialize_for!(RegistrationUpload);
/// The message sent by the user to the server, to initiate registration
pub struct CredentialRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: CS::Group,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message,
pub(crate) alpha: CS::OprfGroup,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -231,8 +231,8 @@ impl_debug_eq_hash_for!(
struct CredentialRequest<CS: CipherSuite>,
[alpha, ke1_message],
[
CS::Group,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message
CS::OprfGroup,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message
],
);
@@ -244,14 +244,14 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let elem_len = <CS::OprfGroup as Group>::ElemLen::to_usize();
let checked_slice = check_slice_size_atleast(input, elem_len, "login_first_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 alpha = CS::Group::from_element_slice(arr)?;
let alpha = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
@@ -259,7 +259,7 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
}
let ke1_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message::from_bytes::<CS>(
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message::from_bytes::<CS>(
&checked_slice[elem_len..],
)?;
@@ -268,7 +268,7 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::Group {
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
self.alpha
}
}
@@ -279,10 +279,10 @@ impl_serialize_and_deserialize_for!(CredentialRequest);
/// login attempt
pub struct CredentialResponse<CS: CipherSuite> {
/// the server's oprf output
pub(crate) beta: CS::Group,
pub(crate) beta: CS::OprfGroup,
pub(crate) masking_nonce: Vec<u8>,
pub(crate) masked_response: Vec<u8>,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -301,8 +301,8 @@ impl_debug_eq_hash_for!(
struct CredentialResponse<CS: CipherSuite>,
[beta, masking_nonce, masked_response, ke2_message],
[
CS::Group,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message,
CS::OprfGroup,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
],
);
@@ -317,7 +317,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
}
pub(crate) fn serialize_without_ke(
beta: &CS::Group,
beta: &CS::OprfGroup,
masking_nonce: &[u8],
masked_response: &[u8],
) -> Vec<u8> {
@@ -326,8 +326,8 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <PublicKey<CS::Group> as SizedBytes>::Len::to_usize();
let elem_len = <CS::OprfGroup as Group>::ElemLen::to_usize();
let key_len = <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize();
let nonce_len: usize = 32;
let envelope_len = Envelope::<CS>::len();
let masked_response_len = key_len + envelope_len;
@@ -343,7 +343,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
// correct subgroup
let beta_bytes = &checked_slice[..elem_len];
let arr = GenericArray::from_slice(beta_bytes);
let beta = CS::Group::from_element_slice(arr)?;
let beta = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if beta.is_identity() {
@@ -355,7 +355,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
[elem_len + nonce_len..elem_len + nonce_len + masked_response_len]
.to_vec();
let ke2_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message::from_bytes::<CS>(
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message::from_bytes::<CS>(
&checked_slice[elem_len + nonce_len + masked_response_len..],
)?;
@@ -370,7 +370,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::Group) -> Self {
pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self {
Self {
beta: new_beta,
masking_nonce: self.masking_nonce.clone(),
@@ -385,14 +385,14 @@ impl_serialize_and_deserialize_for!(CredentialResponse);
/// The answer sent by the client to the server, upon reception of the
/// sealed envelope
pub struct CredentialFinalization<CS: CipherSuite> {
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message,
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message,
}
impl_clone_for!(struct CredentialFinalization<CS: CipherSuite>, [ke3_message]);
impl_debug_eq_hash_for!(
struct CredentialFinalization<CS: CipherSuite>,
[ke3_message],
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message],
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message],
);
impl<CS: CipherSuite> CredentialFinalization<CS> {
@@ -404,7 +404,7 @@ impl<CS: CipherSuite> CredentialFinalization<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let ke3_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message::from_bytes::<CS>(
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message::from_bytes::<CS>(
input,
)?;
Ok(Self { ke3_message })
+64 -59
View File
@@ -40,32 +40,32 @@ const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair";
feature = "serialize",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "KeyPair<CS::Group, S>: serde::Deserialize<'de>",
serialize = "KeyPair<CS::Group, S>: serde::Serialize"
deserialize = "KeyPair<CS::KeGroup, S>: serde::Deserialize<'de>",
serialize = "KeyPair<CS::KeGroup, S>: serde::Serialize"
))
)]
pub struct ServerSetup<
CS: CipherSuite,
S: SecretKey<CS::Group> = PrivateKey<<CS as CipherSuite>::Group>,
S: SecretKey<CS::KeGroup> = PrivateKey<<CS as CipherSuite>::KeGroup>,
> {
oprf_seed: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
keypair: KeyPair<CS::Group, S>,
pub(crate) fake_keypair: KeyPair<CS::Group>,
keypair: KeyPair<CS::KeGroup, S>,
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
}
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::Group>> {
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
/// Generate a new instance of server setup
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
let keypair = KeyPair::<CS::Group>::generate_random(rng);
let keypair = KeyPair::<CS::KeGroup>::generate_random(rng);
Self::new_with_key(rng, keypair)
}
}
impl<CS: CipherSuite, S: SecretKey<CS::Group>> ServerSetup<CS, S> {
impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
/// Create [`ServerSetup`] with the given keypair
pub fn new_with_key<R: CryptoRng + RngCore>(
rng: &mut R,
keypair: KeyPair<CS::Group, S>,
keypair: KeyPair<CS::KeGroup, S>,
) -> Self {
let mut seed = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut seed);
@@ -73,7 +73,7 @@ impl<CS: CipherSuite, S: SecretKey<CS::Group>> ServerSetup<CS, S> {
Self {
oprf_seed: GenericArray::clone_from_slice(&seed[..]),
keypair,
fake_keypair: KeyPair::<CS::Group>::generate_random(rng),
fake_keypair: KeyPair::<CS::KeGroup>::generate_random(rng),
}
}
@@ -90,7 +90,7 @@ impl<CS: CipherSuite, S: SecretKey<CS::Group>> ServerSetup<CS, S> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<S::Error>> {
let seed_len = <CS::Hash as Digest>::OutputSize::to_usize();
let key_len = <PrivateKey<CS::Group> as SizedBytes>::Len::to_usize();
let key_len = <PrivateKey<CS::KeGroup> as SizedBytes>::Len::to_usize();
let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?;
Ok(Self {
@@ -102,7 +102,7 @@ impl<CS: CipherSuite, S: SecretKey<CS::Group>> ServerSetup<CS, S> {
}
/// Returns the keypair
pub fn keypair(&self) -> &KeyPair<CS::Group, S> {
pub fn keypair(&self) -> &KeyPair<CS::KeGroup, S> {
&self.keypair
}
}
@@ -122,16 +122,16 @@ impl_debug_eq_hash_for!(
/// The state elements the client holds to register itself
pub struct ClientRegistration<CS: CipherSuite> {
alpha: CS::Group,
alpha: CS::OprfGroup,
/// token containing the client's password and the blinding factor
pub(crate) token: oprf::Token<CS::Group>,
pub(crate) token: oprf::Token<CS::OprfGroup>,
}
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
impl_debug_eq_hash_for!(
struct ClientRegistration<CS: CipherSuite>,
[token],
[oprf::Token<CS::Group>],
[oprf::Token<CS::OprfGroup>],
);
impl<CS: CipherSuite> ClientRegistration<CS> {
@@ -139,7 +139,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
pub fn serialize(&self) -> Vec<u8> {
[
&self.alpha.to_arr().to_vec(),
&CS::Group::scalar_as_bytes(self.token.blind)[..],
&CS::OprfGroup::scalar_as_bytes(self.token.blind)[..],
&self.token.data,
]
.concat()
@@ -147,8 +147,8 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
let elem_len = <CS::OprfGroup as Group>::ElemLen::to_usize();
let scalar_len = <CS::OprfGroup as Group>::ScalarLen::to_usize();
let min_expected_len = elem_len + scalar_len;
let checked_slice = (if input.len() <= min_expected_len {
Err(InternalPakeError::SizeError {
@@ -160,14 +160,15 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
Ok(input)
})?;
let alpha =
CS::Group::from_element_slice(GenericArray::from_slice(&checked_slice[..elem_len]))?;
let alpha = CS::OprfGroup::from_element_slice(GenericArray::from_slice(
&checked_slice[..elem_len],
))?;
// Check that the message is actually containing an element of the
// correct subgroup
let blinding_factor_bytes =
GenericArray::from_slice(&checked_slice[elem_len..elem_len + scalar_len]);
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?;
let password = checked_slice[elem_len + scalar_len..].to_vec();
Ok(Self {
@@ -257,7 +258,8 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
blinding_factor_rng: &mut R,
password: &[u8],
) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(password, blinding_factor_rng)?;
let (token, alpha) =
oprf::blind::<R, CS::OprfGroup, CS::Hash>(password, blinding_factor_rng)?;
Ok(ClientRegistrationStartResult {
message: RegistrationRequest::<CS> { alpha },
@@ -273,7 +275,7 @@ pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
/// The export key output by client registration
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The server's static public key
pub server_s_pk: PublicKey<CS::Group>,
pub server_s_pk: PublicKey<CS::KeGroup>,
/// Instance of the ClientRegistration, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientRegistration<CS>,
@@ -321,8 +323,10 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
return Err(ProtocolError::ReflectedValueError);
}
let password_derived_key =
get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?;
let password_derived_key = get_password_derived_key::<CS::OprfGroup, CS::SlowHash, CS::Hash>(
&self.token,
r2.beta,
)?;
#[cfg_attr(not(test), allow(unused_variables))]
let (randomized_pwd, h) = Hkdf::<CS::Hash>::extract(None, &password_derived_key);
@@ -358,7 +362,7 @@ pub struct ServerRegistrationStartResult<CS: CipherSuite> {
pub message: RegistrationResponse<CS>,
/// OPRF key, only used in tests
#[cfg(test)]
pub oprf_key: GenericArray<u8, <CS::Group as Group>::ScalarLen>,
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -403,18 +407,18 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
/// From the client's "blinded" password, returns a response to be
/// sent back to the client, as well as a ServerRegistration
pub fn start<S: SecretKey<CS::Group>>(
pub fn start<S: SecretKey<CS::KeGroup>>(
server_setup: &ServerSetup<CS, S>,
message: RegistrationRequest<CS>,
credential_identifier: &[u8],
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
let oprf_key = oprf_key_from_seed::<CS::Group, CS::Hash>(
let oprf_key = oprf_key_from_seed::<CS::OprfGroup, CS::Hash>(
&server_setup.oprf_seed,
credential_identifier,
)?;
// Compute beta = alpha^oprf_key
let beta = oprf::evaluate::<CS::Group>(message.alpha, &oprf_key);
let beta = oprf::evaluate::<CS::OprfGroup>(message.alpha, &oprf_key);
Ok(ServerRegistrationStartResult {
message: RegistrationResponse {
@@ -422,7 +426,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
server_s_pk: server_setup.keypair.public().clone(),
},
#[cfg(test)]
oprf_key: CS::Group::scalar_as_bytes(oprf_key),
oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key),
})
}
@@ -433,7 +437,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng, S: SecretKey<CS::Group>>(
pub(crate) fn dummy<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
rng: &mut R,
server_setup: &ServerSetup<CS, S>,
) -> Self {
@@ -451,14 +455,14 @@ impl_serialize_and_deserialize_for!(ServerRegistration);
#[cfg_attr(
feature = "serialize",
serde(bound(
deserialize = "oprf::Token<CS::Group>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State: serde::Deserialize<'de>",
serialize = "oprf::Token<CS::Group>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State: serde::Serialize"
deserialize = "oprf::Token<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
))
)]
pub struct ClientLogin<CS: CipherSuite> {
/// token containing the client's password and the blinding factor
token: oprf::Token<CS::Group>,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State,
token: oprf::Token<CS::OprfGroup>,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
serialized_credential_request: Vec<u8>,
}
@@ -466,14 +470,14 @@ impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serializ
impl_debug_eq_hash_for!(
struct ClientLogin<CS: CipherSuite>,
[token, ke1_state, serialized_credential_request],
[oprf::Token<CS::Group>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State],
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
);
impl<CS: CipherSuite> ClientLogin<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
let output: Vec<u8> = [
&CS::Group::scalar_as_bytes(self.token.blind)[..],
&CS::OprfGroup::scalar_as_bytes(self.token.blind)[..],
&serialize(&self.serialized_credential_request, 2)?,
&serialize(&self.ke1_state.to_bytes(), 2)?,
&self.token.data,
@@ -484,7 +488,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
let scalar_len = <CS::OprfGroup as Group>::ScalarLen::to_usize();
let checked_slice = (if input.len() <= scalar_len {
Err(InternalPakeError::SizeError {
name: "client_login_bytes",
@@ -496,13 +500,13 @@ impl<CS: CipherSuite> ClientLogin<CS> {
})?;
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?;
let (serialized_credential_request, remainder) = tokenize(&checked_slice[scalar_len..], 2)?;
let (ke1_state_bytes, password) = tokenize(&remainder, 2)?;
let ke1_state =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State::from_bytes::<CS>(
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State::from_bytes::<CS>(
&ke1_state_bytes[..],
)?;
Ok(Self {
@@ -575,7 +579,7 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
/// The client-side export key
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The server's static public key
pub server_s_pk: PublicKey<CS::Group>,
pub server_s_pk: PublicKey<CS::KeGroup>,
/// Instance of the ClientLogin, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientLogin<CS>,
@@ -611,7 +615,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
rng: &mut R,
password: &[u8],
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(password, rng)?;
let (token, alpha) = oprf::blind::<R, CS::OprfGroup, CS::Hash>(password, rng)?;
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?;
@@ -652,7 +656,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
return Err(ProtocolError::ReflectedValueError);
}
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(
let password_derived_key = get_password_derived_key::<CS::OprfGroup, CS::SlowHash, CS::Hash>(
&self.token,
credential_response.beta,
)?;
@@ -722,7 +726,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// The state elements the server holds to record a login
pub struct ServerLogin<CS: CipherSuite> {
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State,
_cs: PhantomData<CS>,
}
@@ -730,7 +734,7 @@ impl_clone_for!(struct ServerLogin<CS: CipherSuite>, [ke2_state, _cs]);
impl_debug_eq_hash_for!(
struct ServerLogin<CS: CipherSuite>,
[ke2_state, _cs],
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State],
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State],
);
/// Optional parameters for server login start
@@ -766,7 +770,7 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
pub server_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// OPRF key, only used in tests
#[cfg(test)]
pub oprf_key: GenericArray<u8, <CS::Group as Group>::ScalarLen>,
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -817,15 +821,16 @@ impl<CS: CipherSuite> ServerLogin<CS> {
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
_cs: PhantomData,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State::from_bytes::<
CS,
>(bytes)?,
ke2_state:
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State::from_bytes::<CS>(
bytes,
)?,
})
}
/// From the client's "blinded" password, returns a challenge to be
/// sent back to the client, as well as a ServerLogin
pub fn start<R: RngCore + CryptoRng, S: SecretKey<CS::Group>>(
pub fn start<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
rng: &mut R,
server_setup: &ServerSetup<CS, S>,
password_file: Option<ServerRegistration<CS>>,
@@ -871,7 +876,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let l1_bytes = &l1.serialize();
let oprf_key = oprf_key_from_seed::<CS::Group, CS::Hash>(
let oprf_key = oprf_key_from_seed::<CS::OprfGroup, CS::Hash>(
&server_setup.oprf_seed,
credential_identifier,
)
@@ -911,7 +916,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
#[cfg(test)]
server_mac_key: result.3,
#[cfg(test)]
oprf_key: CS::Group::scalar_as_bytes(oprf_key),
oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key),
})
}
@@ -921,7 +926,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
self,
message: CredentialFinalization<CS>,
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
let session_key = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::finish_ke(
let session_key = <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::finish_ke(
message.ke3_message,
&self.ke2_state,
)
@@ -1033,11 +1038,11 @@ fn oprf_key_from_seed<G: Group, D: Hash>(
fn mask_response<CS: CipherSuite>(
masking_key: &[u8],
masking_nonce: &[u8],
server_s_pk: &PublicKey<CS::Group>,
server_s_pk: &PublicKey<CS::KeGroup>,
envelope: &Envelope<CS>,
) -> Result<Vec<u8>, ProtocolError> {
let mut xor_pad =
vec![0u8; <PublicKey<CS::Group> as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
vec![0u8; <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalPakeError::HkdfError)?
.expand(
@@ -1059,9 +1064,9 @@ fn unmask_response<CS: CipherSuite>(
masking_key: &[u8],
masking_nonce: &[u8],
masked_response: &[u8],
) -> Result<(PublicKey<CS::Group>, Envelope<CS>), ProtocolError> {
) -> Result<(PublicKey<CS::KeGroup>, Envelope<CS>), ProtocolError> {
let mut xor_pad =
vec![0u8; <PublicKey<CS::Group> as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
vec![0u8; <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalPakeError::HkdfError)?
.expand(
@@ -1074,13 +1079,13 @@ fn unmask_response<CS: CipherSuite>(
.zip(masked_response.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
let key_len = <PublicKey<CS::Group> as SizedBytes>::Len::to_usize();
let key_len = <PublicKey<CS::KeGroup> as SizedBytes>::Len::to_usize();
let unchecked_server_s_pk =
PublicKey::from_arr(&GenericArray::clone_from_slice(&plaintext[..key_len]))?;
let envelope = Envelope::deserialize(&plaintext[key_len..])?;
// Ensure that public key is valid
let server_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_server_s_pk)
let server_s_pk = KeyPair::<CS::KeGroup>::check_public_key(unchecked_server_s_pk)
.map_err(|_| ProtocolError::VerificationError(PakeError::SerializationError))?;
Ok((server_s_pk, envelope))
+10 -9
View File
@@ -27,7 +27,8 @@ use sha2::Digest;
struct Default;
impl CipherSuite for Default {
type Group = RistrettoPoint;
type OprfGroup = RistrettoPoint;
type KeGroup = RistrettoPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = crate::slow_hash::NoOpHash;
@@ -78,7 +79,7 @@ fn server_registration_roundtrip() {
// mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key
mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac
let mock_client_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mock_client_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
// serialization order: oprf_key, public key, envelope
let mut bytes = Vec::<u8>::new();
bytes.extend_from_slice(&mock_client_kp.public().to_arr());
@@ -118,7 +119,7 @@ fn registration_response_roundtrip() {
let pt = random_ristretto_point();
let beta_bytes = pt.to_arr();
let mut rng = OsRng;
let skp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let mut input = Vec::new();
@@ -144,7 +145,7 @@ fn registration_response_roundtrip() {
#[test]
fn registration_upload_roundtrip() {
let mut rng = OsRng;
let skp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let mut key = [0u8; 32];
@@ -176,7 +177,7 @@ fn credential_request_roundtrip() {
let alpha = random_ristretto_point();
let alpha_bytes = alpha.to_arr().to_vec();
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -219,7 +220,7 @@ fn credential_response_roundtrip() {
];
rng.fill_bytes(&mut masked_response);
let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
@@ -274,7 +275,7 @@ fn client_login_roundtrip() {
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -298,7 +299,7 @@ fn client_login_roundtrip() {
fn ke1_message_roundtrip() {
let mut rng = OsRng;
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -315,7 +316,7 @@ fn ke1_message_roundtrip() {
fn ke2_message_roundtrip() {
let mut rng = OsRng;
let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
+10 -9
View File
@@ -29,7 +29,8 @@ use zeroize::Zeroize;
struct RistrettoSha5123dhNoSlowHash;
impl CipherSuite for RistrettoSha5123dhNoSlowHash {
type Group = RistrettoPoint;
type OprfGroup = RistrettoPoint;
type KeGroup = RistrettoPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = NoOpHash;
@@ -280,11 +281,11 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut rng = OsRng;
// Inputs
let server_s_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let server_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let client_s_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let client_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let fake_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let server_s_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let server_e_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let client_s_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let client_e_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let fake_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let credential_identifier = b"credIdentifier";
let id_u = b"idU";
let id_s = b"idS";
@@ -307,14 +308,14 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
)
.unwrap();
let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng);
let blinding_factor_bytes = CS::Group::scalar_as_bytes(blinding_factor);
let blinding_factor = CS::OprfGroup::random_nonzero_scalar(&mut rng);
let blinding_factor_bytes = CS::OprfGroup::scalar_as_bytes(blinding_factor);
let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec());
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password).unwrap();
let blinding_factor_bytes_returned =
CS::Group::scalar_as_bytes(client_registration_start_result.state.token.blind);
CS::OprfGroup::scalar_as_bytes(client_registration_start_result.state.token.blind);
assert_eq!(
hex::encode(&blinding_factor_bytes),
hex::encode(&blinding_factor_bytes_returned)
+5 -3
View File
@@ -17,7 +17,8 @@ use serde_json::Value;
struct Ristretto255Sha512NoSlowHash;
impl CipherSuite for Ristretto255Sha512NoSlowHash {
type Group = RistrettoPoint;
type OprfGroup = RistrettoPoint;
type KeGroup = RistrettoPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = NoOpHash;
@@ -27,7 +28,8 @@ impl CipherSuite for Ristretto255Sha512NoSlowHash {
struct P256Sha256NoSlowHash;
#[cfg(feature = "p256")]
impl CipherSuite for P256Sha256NoSlowHash {
type Group = p256_::ProjectivePoint;
type OprfGroup = p256_::ProjectivePoint;
type KeGroup = p256_::ProjectivePoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha256;
type SlowHash = NoOpHash;
@@ -750,7 +752,7 @@ fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> TestVectorParameter
dummy_private_key: parse_default!(
values,
"client_private_key",
vec![0u8; <PrivateKey<CS::Group> as SizedBytes>::Len::to_usize()]
vec![0u8; <PrivateKey<CS::OprfGroup> as SizedBytes>::Len::to_usize()]
),
dummy_masking_key: parse_default!(values, "masking_key", vec![0u8; 64]),
context: parse!(values, "Context"),