Merge pull request #50 from huitseeker/simplifications
Simplifications and Normalizations
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ pub trait CipherSuite {
|
|||||||
/// A keypair type composed of public and private components
|
/// A keypair type composed of public and private components
|
||||||
type KeyFormat: KeyPair<Repr = Key> + PartialEq;
|
type KeyFormat: KeyPair<Repr = Key> + PartialEq;
|
||||||
/// A key exchange protocol
|
/// A key exchange protocol
|
||||||
type KeyExchange: KeyExchange<Self::Hash>;
|
type KeyExchange: KeyExchange<Self::Hash, Self::KeyFormat>;
|
||||||
/// The main hash function use (for HKDF computations and hashing transcripts)
|
/// The main hash function use (for HKDF computations and hashing transcripts)
|
||||||
type Hash: Hash;
|
type Hash: Hash;
|
||||||
/// A slow hashing function, typically used for password hashing
|
/// A slow hashing function, typically used for password hashing
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ pub fn hash_to_point(bytes: &[u8]) -> EdwardsPoint {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Signal tests from //
|
// Signal tests from //
|
||||||
@@ -73,8 +74,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn elligator_correct() {
|
fn elligator_correct() {
|
||||||
let bytes: Vec<u8> = (0u8..32u8).collect();
|
let bytes: Vec<u8> = (0u8..32u8).collect();
|
||||||
let mut bits_in = [0u8; 32];
|
let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken");
|
||||||
bits_in.copy_from_slice(&bytes);
|
|
||||||
let fe = FieldElement51::from_bytes(&bits_in);
|
let fe = FieldElement51::from_bytes(&bits_in);
|
||||||
let eg = elligator_signal(&fe);
|
let eg = elligator_signal(&fe);
|
||||||
assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT);
|
assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT);
|
||||||
|
|||||||
+12
-7
@@ -87,7 +87,7 @@ impl Group for RistrettoPoint {
|
|||||||
) -> Result<Self, InternalPakeError> {
|
) -> Result<Self, InternalPakeError> {
|
||||||
CompressedRistretto::from_slice(element_bits)
|
CompressedRistretto::from_slice(element_bits)
|
||||||
.decompress()
|
.decompress()
|
||||||
.ok_or_else(|| InternalPakeError::PointError)
|
.ok_or(InternalPakeError::PointError)
|
||||||
}
|
}
|
||||||
// serialization of a group element
|
// serialization of a group element
|
||||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||||
@@ -97,9 +97,12 @@ impl Group for RistrettoPoint {
|
|||||||
|
|
||||||
type UniformBytesLen = U64;
|
type UniformBytesLen = U64;
|
||||||
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> Self {
|
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> Self {
|
||||||
let mut bits = [0u8; 64];
|
// https://caniuse.rs/features/array_gt_32_impls
|
||||||
bits.copy_from_slice(&uniform_bytes);
|
let bits: [u8; 64] = {
|
||||||
|
let mut bytes = [0u8; 64];
|
||||||
|
bytes.copy_from_slice(uniform_bytes);
|
||||||
|
bytes
|
||||||
|
};
|
||||||
RistrettoPoint::from_uniform_bytes(&bits)
|
RistrettoPoint::from_uniform_bytes(&bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +135,7 @@ impl Group for EdwardsPoint {
|
|||||||
) -> Result<Self, InternalPakeError> {
|
) -> Result<Self, InternalPakeError> {
|
||||||
let point = CompressedEdwardsY::from_slice(element_bits)
|
let point = CompressedEdwardsY::from_slice(element_bits)
|
||||||
.decompress()
|
.decompress()
|
||||||
.ok_or_else(|| InternalPakeError::PointError)?;
|
.ok_or(InternalPakeError::PointError)?;
|
||||||
|
|
||||||
if point.is_small_order() {
|
if point.is_small_order() {
|
||||||
return Err(InternalPakeError::SubGroupError);
|
return Err(InternalPakeError::SubGroupError);
|
||||||
@@ -155,6 +158,7 @@ impl Group for EdwardsPoint {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
const EIGHT_TORSION: [[u8; 32]; 8] = [
|
const EIGHT_TORSION: [[u8; 32]; 8] = [
|
||||||
[
|
[
|
||||||
@@ -192,8 +196,9 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
fn deserialize_point(pt: &[u8]) -> Result<EdwardsPoint> {
|
fn deserialize_point(pt: &[u8]) -> Result<EdwardsPoint> {
|
||||||
let mut bytes = [0u8; 32];
|
let bytes: [u8; 32] = (&pt[..32])
|
||||||
bytes.copy_from_slice(&pt[..32]);
|
.try_into()
|
||||||
|
.expect("Slice pattern invariant broken");
|
||||||
|
|
||||||
curve25519_dalek::edwards::CompressedEdwardsY(bytes)
|
curve25519_dalek::edwards::CompressedEdwardsY(bytes)
|
||||||
.decompress()
|
.decompress()
|
||||||
|
|||||||
+2
-11
@@ -4,19 +4,10 @@
|
|||||||
// LICENSE file in the root directory of this source tree.
|
// LICENSE file in the root directory of this source tree.
|
||||||
|
|
||||||
use digest::{BlockInput, FixedOutput, Reset, Update};
|
use digest::{BlockInput, FixedOutput, Reset, Update};
|
||||||
use generic_array::ArrayLength;
|
|
||||||
|
|
||||||
/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC
|
/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC
|
||||||
// Associated types could be simplified when they are made as defaults:
|
// Associated types could be simplified when they are made as defaults:
|
||||||
// https://github.com/rust-lang/rust/issues/29661
|
// https://github.com/rust-lang/rust/issues/29661
|
||||||
pub trait Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone {
|
pub trait Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone {}
|
||||||
/// The block size for the hash function
|
|
||||||
type BlockSize: ArrayLength<u8>;
|
|
||||||
/// The output size of the hash function
|
|
||||||
type OutputSize: ArrayLength<u8>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Update + BlockInput + FixedOutput + Reset + Default + Clone> Hash for T {
|
impl<T: Update + BlockInput + FixedOutput + Reset + Default + Clone> Hash for T {}
|
||||||
type BlockSize = T::BlockSize;
|
|
||||||
type OutputSize = T::OutputSize;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ use rand_core::{CryptoRng, RngCore};
|
|||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub trait KeyExchange<D: Hash> {
|
pub trait KeyExchange<D: Hash, KeyFormat: KeyPair<Repr = Key>> {
|
||||||
type KE1State: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
type KE1State: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||||
type KE2State: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
type KE2State: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||||
type KE1Message: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
type KE1Message: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||||
type KE2Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
type KE2Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||||
type KE3Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
type KE3Message: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||||
|
|
||||||
fn generate_ke1<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||||
l1_component: Vec<u8>,
|
l1_component: Vec<u8>,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
||||||
|
|
||||||
fn generate_ke2<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke2<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
l1_bytes: Vec<u8>,
|
l1_bytes: Vec<u8>,
|
||||||
l2_bytes: Vec<u8>,
|
l2_bytes: Vec<u8>,
|
||||||
@@ -33,7 +33,7 @@ pub trait KeyExchange<D: Hash> {
|
|||||||
server_s_sk: KeyFormat::Repr,
|
server_s_sk: KeyFormat::Repr,
|
||||||
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>;
|
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>;
|
||||||
|
|
||||||
fn generate_ke3<KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke3(
|
||||||
l2_component: Vec<u8>,
|
l2_component: Vec<u8>,
|
||||||
ke2_message: Self::KE2Message,
|
ke2_message: Self::KE2Message,
|
||||||
ke1_state: &Self::KE1State,
|
ke1_state: &Self::KE1State,
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ use crate::{
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
key_exchange::traits::{KeyExchange, ToBytes},
|
key_exchange::traits::{KeyExchange, ToBytes},
|
||||||
keypair::{Key, KeyPair, SizedBytes},
|
keypair::{Key, KeyPair, SizedBytes},
|
||||||
sized_bytes_using_constant_and_try_from,
|
|
||||||
};
|
};
|
||||||
use digest::Digest;
|
use digest::{Digest, FixedOutput};
|
||||||
use generic_array::{
|
use generic_array::{
|
||||||
typenum::{U64, U96},
|
typenum::{Unsigned, U32},
|
||||||
GenericArray,
|
ArrayLength, GenericArray,
|
||||||
};
|
};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use hmac::{Hmac, Mac, NewMac};
|
use hmac::{Hmac, Mac, NewMac};
|
||||||
@@ -24,31 +23,34 @@ use std::convert::TryFrom;
|
|||||||
|
|
||||||
const KEY_LEN: usize = 32;
|
const KEY_LEN: usize = 32;
|
||||||
pub(crate) const NONCE_LEN: usize = 32;
|
pub(crate) const NONCE_LEN: usize = 32;
|
||||||
|
pub(crate) type NonceLen = U32;
|
||||||
const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN;
|
const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN;
|
||||||
const KE2_MESSAGE_LEN: usize = NONCE_LEN + 2 * KEY_LEN;
|
|
||||||
|
|
||||||
static STR_3DH: &[u8] = b"3DH keys";
|
static STR_3DH: &[u8] = b"3DH keys";
|
||||||
|
|
||||||
/// The Triple Diffie-Hellman key exchange implementation
|
/// The Triple Diffie-Hellman key exchange implementation
|
||||||
pub struct TripleDH;
|
pub struct TripleDH;
|
||||||
|
|
||||||
impl<D: Hash> KeyExchange<D> for TripleDH {
|
impl<D: Hash, KeyFormat: KeyPair<Repr = Key>> KeyExchange<D, KeyFormat> for TripleDH {
|
||||||
type KE1State = KE1State;
|
type KE1State = KE1State<<D as FixedOutput>::OutputSize>;
|
||||||
type KE2State = KE2State;
|
type KE2State = KE2State<<D as FixedOutput>::OutputSize>;
|
||||||
type KE1Message = KE1Message;
|
type KE1Message = KE1Message;
|
||||||
type KE2Message = KE2Message;
|
type KE2Message = KE2Message<<D as FixedOutput>::OutputSize>;
|
||||||
type KE3Message = KE3Message;
|
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;
|
||||||
|
|
||||||
fn generate_ke1<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||||
l1_component: Vec<u8>,
|
l1_component: Vec<u8>,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
||||||
let client_e_kp = KeyFormat::generate_random(rng)?;
|
let client_e_kp = KeyFormat::generate_random(rng)?;
|
||||||
let mut client_nonce = [0u8; NONCE_LEN];
|
let client_nonce: GenericArray<u8, NonceLen> = {
|
||||||
rng.fill_bytes(&mut client_nonce);
|
let mut client_nonce_bytes = [0u8; NONCE_LEN];
|
||||||
|
rng.fill_bytes(&mut client_nonce_bytes);
|
||||||
|
client_nonce_bytes.into()
|
||||||
|
};
|
||||||
|
|
||||||
let ke1_message = KE1Message {
|
let ke1_message = KE1Message {
|
||||||
client_nonce: client_nonce.to_vec(),
|
client_nonce,
|
||||||
client_e_pk: client_e_kp.public().clone(),
|
client_e_pk: client_e_kp.public().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,14 +62,14 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
Ok((
|
Ok((
|
||||||
KE1State {
|
KE1State {
|
||||||
client_e_sk: client_e_kp.private().clone(),
|
client_e_sk: client_e_kp.private().clone(),
|
||||||
client_nonce: client_nonce.to_vec(),
|
client_nonce,
|
||||||
hashed_l1: hashed_l1.to_vec(),
|
hashed_l1,
|
||||||
},
|
},
|
||||||
ke1_message,
|
ke1_message,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ke2<R: RngCore + CryptoRng, KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke2<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
l1_bytes: Vec<u8>,
|
l1_bytes: Vec<u8>,
|
||||||
l2_bytes: Vec<u8>,
|
l2_bytes: Vec<u8>,
|
||||||
@@ -76,8 +78,11 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
server_s_sk: KeyFormat::Repr,
|
server_s_sk: KeyFormat::Repr,
|
||||||
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> {
|
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> {
|
||||||
let server_e_kp = KeyFormat::generate_random(rng)?;
|
let server_e_kp = KeyFormat::generate_random(rng)?;
|
||||||
let mut server_nonce = [0u8; NONCE_LEN];
|
let server_nonce: GenericArray<u8, NonceLen> = {
|
||||||
rng.fill_bytes(&mut server_nonce);
|
let mut server_nonce_bytes = [0u8; NONCE_LEN];
|
||||||
|
rng.fill_bytes(&mut server_nonce_bytes);
|
||||||
|
server_nonce_bytes.into()
|
||||||
|
};
|
||||||
|
|
||||||
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat, D>(
|
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat, D>(
|
||||||
TripleDHComponents {
|
TripleDHComponents {
|
||||||
@@ -115,19 +120,19 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
KE2State {
|
KE2State {
|
||||||
km3: km3.to_vec(),
|
km3,
|
||||||
hashed_transcript: hashed_transcript.to_vec(),
|
hashed_transcript,
|
||||||
shared_secret: shared_secret.to_vec(),
|
shared_secret,
|
||||||
},
|
},
|
||||||
KE2Message {
|
KE2Message {
|
||||||
server_nonce: server_nonce.to_vec(),
|
server_nonce,
|
||||||
server_e_pk: server_e_kp.public().clone(),
|
server_e_pk: server_e_kp.public().clone(),
|
||||||
mac: mac.finalize().into_bytes().to_vec(),
|
mac: mac.finalize().into_bytes(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ke3<KeyFormat: KeyPair<Repr = Key>>(
|
fn generate_ke3(
|
||||||
l2_component: Vec<u8>,
|
l2_component: Vec<u8>,
|
||||||
ke2_message: Self::KE2Message,
|
ke2_message: Self::KE2Message,
|
||||||
ke1_state: &Self::KE1State,
|
ke1_state: &Self::KE1State,
|
||||||
@@ -165,7 +170,7 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||||
server_mac.update(&hashed_transcript);
|
server_mac.update(&hashed_transcript);
|
||||||
|
|
||||||
if ke2_message.mac != server_mac.finalize().into_bytes().to_vec() {
|
if ke2_message.mac != server_mac.finalize().into_bytes() {
|
||||||
return Err(ProtocolError::VerificationError(
|
return Err(ProtocolError::VerificationError(
|
||||||
PakeError::KeyExchangeMacValidationError,
|
PakeError::KeyExchangeMacValidationError,
|
||||||
));
|
));
|
||||||
@@ -178,7 +183,7 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
Ok((
|
Ok((
|
||||||
shared_secret.to_vec(),
|
shared_secret.to_vec(),
|
||||||
KE3Message {
|
KE3Message {
|
||||||
mac: client_mac.finalize().into_bytes().to_vec(),
|
mac: client_mac.finalize().into_bytes(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -191,7 +196,7 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
Hmac::<D>::new_varkey(&ke2_state.km3).map_err(|_| InternalPakeError::HmacError)?;
|
Hmac::<D>::new_varkey(&ke2_state.km3).map_err(|_| InternalPakeError::HmacError)?;
|
||||||
client_mac.update(&ke2_state.hashed_transcript);
|
client_mac.update(&ke2_state.hashed_transcript);
|
||||||
|
|
||||||
if ke3_message.mac != client_mac.finalize().into_bytes().to_vec() {
|
if ke3_message.mac != client_mac.finalize().into_bytes() {
|
||||||
return Err(ProtocolError::VerificationError(
|
return Err(ProtocolError::VerificationError(
|
||||||
PakeError::KeyExchangeMacValidationError,
|
PakeError::KeyExchangeMacValidationError,
|
||||||
));
|
));
|
||||||
@@ -205,40 +210,46 @@ impl<D: Hash> KeyExchange<D> for TripleDH {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ke2_message_size() -> usize {
|
fn ke2_message_size() -> usize {
|
||||||
KE2_MESSAGE_LEN
|
NONCE_LEN + KEY_LEN + <<D as FixedOutput>::OutputSize as Unsigned>::to_usize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The client state produced after the first key exchange message
|
/// The client state produced after the first key exchange message
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct KE1State {
|
pub struct KE1State<HashLen: ArrayLength<u8>> {
|
||||||
client_e_sk: Key,
|
client_e_sk: Key,
|
||||||
client_nonce: Vec<u8>,
|
client_nonce: GenericArray<u8, NonceLen>,
|
||||||
hashed_l1: Vec<u8>,
|
hashed_l1: GenericArray<u8, HashLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first key exchange message
|
/// The first key exchange message
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct KE1Message {
|
pub struct KE1Message {
|
||||||
pub(crate) client_nonce: Vec<u8>,
|
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
|
||||||
pub(crate) client_e_pk: Key,
|
pub(crate) client_e_pk: Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for KE1State {
|
impl<HashLen: ArrayLength<u8>> TryFrom<Vec<u8>> for KE1State<HashLen> {
|
||||||
type Error = InternalPakeError;
|
type Error = InternalPakeError;
|
||||||
|
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
let checked_bytes = check_slice_size(&bytes, KE1_STATE_LEN, "ke1_state")?;
|
let checked_bytes = check_slice_size(
|
||||||
|
&bytes,
|
||||||
|
KEY_LEN + NONCE_LEN + HashLen::to_usize(),
|
||||||
|
"ke1_state",
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?,
|
client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?,
|
||||||
client_nonce: checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN].to_vec(),
|
client_nonce: GenericArray::clone_from_slice(
|
||||||
hashed_l1: checked_bytes[KEY_LEN + NONCE_LEN..].to_vec(),
|
&checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN],
|
||||||
|
),
|
||||||
|
hashed_l1: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN + NONCE_LEN..]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBytes for KE1State {
|
impl<HashLen: ArrayLength<u8>> ToBytes for KE1State<HashLen> {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
let output: Vec<u8> = [
|
let output: Vec<u8> = [
|
||||||
&self.client_e_sk.to_arr(),
|
&self.client_e_sk.to_arr(),
|
||||||
@@ -250,8 +261,6 @@ impl ToBytes for KE1State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sized_bytes_using_constant_and_try_from!(KE1State, U96);
|
|
||||||
|
|
||||||
impl ToBytes for KE1Message {
|
impl ToBytes for KE1Message {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
|
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
|
||||||
@@ -266,29 +275,27 @@ impl TryFrom<Vec<u8>> for KE1Message {
|
|||||||
check_slice_size(&ke1_message_bytes, NONCE_LEN + KEY_LEN, "ke1_message")?;
|
check_slice_size(&ke1_message_bytes, NONCE_LEN + KEY_LEN, "ke1_message")?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client_nonce: checked_bytes[..NONCE_LEN].to_vec(),
|
client_nonce: GenericArray::clone_from_slice(&checked_bytes[..NONCE_LEN]),
|
||||||
client_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..])?,
|
client_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..])?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sized_bytes_using_constant_and_try_from!(KE1Message, U64);
|
|
||||||
|
|
||||||
/// The server state produced after the second key exchange message
|
/// The server state produced after the second key exchange message
|
||||||
pub struct KE2State {
|
pub struct KE2State<HashLen: ArrayLength<u8>> {
|
||||||
km3: Vec<u8>,
|
km3: GenericArray<u8, HashLen>,
|
||||||
hashed_transcript: Vec<u8>,
|
hashed_transcript: GenericArray<u8, HashLen>,
|
||||||
shared_secret: Vec<u8>,
|
shared_secret: GenericArray<u8, HashLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The second key exchange message
|
/// The second key exchange message
|
||||||
pub struct KE2Message {
|
pub struct KE2Message<HashLen: ArrayLength<u8>> {
|
||||||
server_nonce: Vec<u8>,
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
server_e_pk: Key,
|
server_e_pk: Key,
|
||||||
mac: Vec<u8>,
|
mac: GenericArray<u8, HashLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBytes for KE2State {
|
impl<HashLen: ArrayLength<u8>> ToBytes for KE2State<HashLen> {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
let output: Vec<u8> = [
|
let output: Vec<u8> = [
|
||||||
&self.km3[..],
|
&self.km3[..],
|
||||||
@@ -300,21 +307,21 @@ impl ToBytes for KE2State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for KE2State {
|
impl<HashLen: ArrayLength<u8>> TryFrom<Vec<u8>> for KE2State<HashLen> {
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
|
|
||||||
fn try_from(ke1_message_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(ke1_message_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
let checked_bytes = check_slice_size(&ke1_message_bytes, 3 * KEY_LEN, "ke2_state")?;
|
let checked_bytes = check_slice_size(&ke1_message_bytes, 3 * KEY_LEN, "ke2_state")?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
km3: checked_bytes[..KEY_LEN].to_vec(),
|
km3: GenericArray::clone_from_slice(&checked_bytes[..KEY_LEN]),
|
||||||
hashed_transcript: checked_bytes[KEY_LEN..2 * KEY_LEN].to_vec(),
|
hashed_transcript: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN..2 * KEY_LEN]),
|
||||||
shared_secret: checked_bytes[2 * KEY_LEN..].to_vec(),
|
shared_secret: GenericArray::clone_from_slice(&checked_bytes[2 * KEY_LEN..]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBytes for KE2Message {
|
impl<HashLen: ArrayLength<u8>> ToBytes for KE2Message<HashLen> {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
let output: Vec<u8> = [
|
let output: Vec<u8> = [
|
||||||
&self.server_nonce[..],
|
&self.server_nonce[..],
|
||||||
@@ -326,16 +333,17 @@ impl ToBytes for KE2Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for KE2Message {
|
impl<HashLen: ArrayLength<u8>> TryFrom<Vec<u8>> for KE2Message<HashLen> {
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
|
|
||||||
fn try_from(ke2_message_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(ke2_message_bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
let checked_bytes = check_slice_size(&ke2_message_bytes, KE2_MESSAGE_LEN, "ke2_message")?;
|
let ke2_message_len = NONCE_LEN + KEY_LEN + HashLen::to_usize();
|
||||||
|
let checked_bytes = check_slice_size(&ke2_message_bytes, ke2_message_len, "ke2_message")?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
server_nonce: checked_bytes[..NONCE_LEN].to_vec(),
|
server_nonce: GenericArray::clone_from_slice(&checked_bytes[..NONCE_LEN]),
|
||||||
server_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..NONCE_LEN + KEY_LEN])?,
|
server_e_pk: Key::from_bytes(&checked_bytes[NONCE_LEN..NONCE_LEN + KEY_LEN])?,
|
||||||
mac: checked_bytes[NONCE_LEN + KEY_LEN..].to_vec(),
|
mac: GenericArray::clone_from_slice(&checked_bytes[NONCE_LEN + KEY_LEN..]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,17 +360,17 @@ struct TripleDHComponents {
|
|||||||
|
|
||||||
// Consists of a shared secret, followed by two mac keys
|
// Consists of a shared secret, followed by two mac keys
|
||||||
type TripleDHDerivationResult<D> = (
|
type TripleDHDerivationResult<D> = (
|
||||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Internal function which takes the public and private components of the client and server keypairs, along
|
// Internal function which takes the public and private components of the client and server keypairs, along
|
||||||
// with some auxiliary metadata, to produce the shared secret and two MAC keys
|
// with some auxiliary metadata, to produce the shared secret and two MAC keys
|
||||||
fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>, D: Hash>(
|
fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>, D: Hash>(
|
||||||
dh: TripleDHComponents,
|
dh: TripleDHComponents,
|
||||||
client_nonce: &[u8],
|
client_nonce: &GenericArray<u8, NonceLen>,
|
||||||
server_nonce: &[u8],
|
server_nonce: &GenericArray<u8, NonceLen>,
|
||||||
client_s_pk: KeyFormat::Repr,
|
client_s_pk: KeyFormat::Repr,
|
||||||
server_s_pk: KeyFormat::Repr,
|
server_s_pk: KeyFormat::Repr,
|
||||||
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
|
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
|
||||||
@@ -395,24 +403,24 @@ fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>, D: Hash>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The third key exchange message
|
/// The third key exchange message
|
||||||
pub struct KE3Message {
|
pub struct KE3Message<HashLen: ArrayLength<u8>> {
|
||||||
mac: Vec<u8>,
|
mac: GenericArray<u8, HashLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBytes for KE3Message {
|
impl<HashLen: ArrayLength<u8>> ToBytes for KE3Message<HashLen> {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
self.mac.clone()
|
self.mac.to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<u8>> for KE3Message {
|
impl<HashLen: ArrayLength<u8>> TryFrom<Vec<u8>> for KE3Message<HashLen> {
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
|
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
let checked_bytes = check_slice_size(&bytes, KEY_LEN, "ke3_message")?;
|
let checked_bytes = check_slice_size(&bytes, KEY_LEN, "ke3_message")?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mac: checked_bytes.to_vec(),
|
mac: GenericArray::clone_from_slice(&checked_bytes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-33
@@ -16,6 +16,7 @@ use proptest::prelude::*;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
use rand::{rngs::StdRng, SeedableRng};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use x25519_dalek::{PublicKey, StaticSecret};
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
|
|
||||||
@@ -91,35 +92,6 @@ trait KeyPairExt: KeyPair + Debug {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl<KP> KeyPairExt for KP where KP: KeyPair + Debug {}
|
impl<KP> KeyPairExt for KP where KP: KeyPair + Debug {}
|
||||||
|
|
||||||
/// This assumes you have defined:
|
|
||||||
/// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T`
|
|
||||||
/// - an `fn to_bytes(&self) -> Vec<u8>` in an `impl T` block
|
|
||||||
/// and it both of the above to produce a sensible SizedBytes implementation
|
|
||||||
///
|
|
||||||
/// Because SizedBytes has a strong notion of size, and TryFrom/to_bytes does
|
|
||||||
/// not, it's better to use the macro below rather than this one, where possible.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! sized_bytes_using_constant_and_try_from {
|
|
||||||
($sized_type: ident, $len: ident) => {
|
|
||||||
impl SizedBytes for $sized_type {
|
|
||||||
type Len = $len;
|
|
||||||
|
|
||||||
fn to_arr(&self) -> generic_array::GenericArray<u8, Self::Len> {
|
|
||||||
generic_array::GenericArray::clone_from_slice(&self.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, InternalPakeError> {
|
|
||||||
let checked_bytes = check_slice_size(
|
|
||||||
bytes,
|
|
||||||
<Self::Len as generic_array::typenum::Unsigned>::to_usize(),
|
|
||||||
"bytes",
|
|
||||||
)?;
|
|
||||||
std::convert::TryFrom::try_from(checked_bytes.to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This assumes you have defined a SizedBytes instance for a `T`, and defines:
|
/// This assumes you have defined a SizedBytes instance for a `T`, and defines:
|
||||||
/// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T`
|
/// - an `impl TryFrom<&[u8b], Error = InternalPakeError>` for a non-generic `T`
|
||||||
/// - an `fn to_bytes(&self) -> Vec<u8>` in an `impl T` block
|
/// - an `fn to_bytes(&self) -> Vec<u8>` in an `impl T` block
|
||||||
@@ -245,15 +217,15 @@ impl KeyPair for X25519KeyPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn public_from_private(secret: &Self::Repr) -> Self::Repr {
|
fn public_from_private(secret: &Self::Repr) -> Self::Repr {
|
||||||
let mut secret_data = [0u8; 32];
|
let secret_data: [u8; 32] = (&secret.0[..])
|
||||||
secret_data.copy_from_slice(&secret.0[..]);
|
.try_into()
|
||||||
|
.expect("Keypair::Repr invariant broken");
|
||||||
let base_data = ::x25519_dalek::X25519_BASEPOINT_BYTES;
|
let base_data = ::x25519_dalek::X25519_BASEPOINT_BYTES;
|
||||||
Key(::x25519_dalek::x25519(secret_data, base_data).to_vec())
|
Key(::x25519_dalek::x25519(secret_data, base_data).to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_public_key(key: Self::Repr) -> Result<Self::Repr, InternalPakeError> {
|
fn check_public_key(key: Self::Repr) -> Result<Self::Repr, InternalPakeError> {
|
||||||
let mut key_bytes = [0u8; 32];
|
let key_bytes: [u8; 32] = (&key[..]).try_into().expect("Key invariant broken");
|
||||||
key_bytes.copy_from_slice(&key);
|
|
||||||
let point = ::curve25519_dalek::montgomery::MontgomeryPoint(key_bytes)
|
let point = ::curve25519_dalek::montgomery::MontgomeryPoint(key_bytes)
|
||||||
.to_edwards(1)
|
.to_edwards(1)
|
||||||
.ok_or(InternalPakeError::PointError)?;
|
.ok_or(InternalPakeError::PointError)?;
|
||||||
|
|||||||
+2
-3
@@ -9,7 +9,6 @@
|
|||||||
use crate::group::Group;
|
use crate::group::Group;
|
||||||
use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint};
|
use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint};
|
||||||
|
|
||||||
use generic_array::GenericArray;
|
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use sha2::{Sha256, Sha512};
|
use sha2::{Sha256, Sha512};
|
||||||
|
|
||||||
@@ -22,13 +21,13 @@ pub trait GroupWithMapToCurve: Group {
|
|||||||
impl GroupWithMapToCurve for RistrettoPoint {
|
impl GroupWithMapToCurve for RistrettoPoint {
|
||||||
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
||||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(pepper, password);
|
let (hashed_input, _) = Hkdf::<Sha512>::extract(pepper, password);
|
||||||
<Self as Group>::hash_to_curve(GenericArray::from_slice(&hashed_input))
|
<Self as Group>::hash_to_curve(&hashed_input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroupWithMapToCurve for EdwardsPoint {
|
impl GroupWithMapToCurve for EdwardsPoint {
|
||||||
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
||||||
let (hashed_input, _) = Hkdf::<Sha256>::extract(pepper, password);
|
let (hashed_input, _) = Hkdf::<Sha256>::extract(pepper, password);
|
||||||
<Self as Group>::hash_to_curve(GenericArray::from_slice(&hashed_input))
|
<Self as Group>::hash_to_curve(&hashed_input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-85
@@ -141,7 +141,7 @@ where
|
|||||||
pub struct LoginFirstMessage<CS: CipherSuite> {
|
pub struct LoginFirstMessage<CS: CipherSuite> {
|
||||||
/// blinded password information
|
/// blinded password information
|
||||||
alpha: CS::Group,
|
alpha: CS::Group,
|
||||||
ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message,
|
ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
||||||
@@ -163,9 +163,10 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
|||||||
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
|
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
|
||||||
let alpha = CS::Group::from_element_slice(arr)?;
|
let alpha = CS::Group::from_element_slice(arr)?;
|
||||||
|
|
||||||
let ke1_message = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message::try_from(
|
let ke1_message =
|
||||||
checked_slice[elem_len..].to_vec(),
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message::try_from(
|
||||||
)?;
|
checked_slice[elem_len..].to_vec(),
|
||||||
|
)?;
|
||||||
Ok(Self { alpha, ke1_message })
|
Ok(Self { alpha, ke1_message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,28 +180,15 @@ impl<CS: CipherSuite> LoginFirstMessage<CS> {
|
|||||||
|
|
||||||
/// The answer sent by the server to the user, upon reception of the
|
/// The answer sent by the server to the user, upon reception of the
|
||||||
/// login attempt.
|
/// login attempt.
|
||||||
pub struct LoginSecondMessage<Grp, KeyFormat, KE, D>
|
pub struct LoginSecondMessage<CS: CipherSuite> {
|
||||||
where
|
|
||||||
KeyFormat: KeyPair,
|
|
||||||
KE: KeyExchange<D>,
|
|
||||||
D: Hash,
|
|
||||||
{
|
|
||||||
_key_format: PhantomData<KeyFormat>,
|
|
||||||
_key_exchange: PhantomData<KE>,
|
|
||||||
/// the server's oprf output
|
/// the server's oprf output
|
||||||
beta: Grp,
|
beta: CS::Group,
|
||||||
/// the user's sealed information,
|
/// the user's sealed information,
|
||||||
envelope: Envelope<D>,
|
envelope: Envelope<CS::Hash>,
|
||||||
ke2_message: KE::KE2Message,
|
ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Grp, KeyFormat, KE, D> LoginSecondMessage<Grp, KeyFormat, KE, D>
|
impl<CS: CipherSuite> LoginSecondMessage<CS> {
|
||||||
where
|
|
||||||
Grp: Group,
|
|
||||||
KeyFormat: KeyPair,
|
|
||||||
KE: KeyExchange<D>,
|
|
||||||
D: Hash,
|
|
||||||
{
|
|
||||||
/// byte representation for the login response
|
/// byte representation for the login response
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
[
|
[
|
||||||
@@ -212,19 +200,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Grp, KeyFormat, KE, D> TryFrom<&[u8]> for LoginSecondMessage<Grp, KeyFormat, KE, D>
|
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginSecondMessage<CS> {
|
||||||
where
|
|
||||||
Grp: Group,
|
|
||||||
KeyFormat: KeyPair,
|
|
||||||
KE: KeyExchange<D>,
|
|
||||||
D: Hash,
|
|
||||||
{
|
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
let key_len = <<CS::KeyFormat as KeyPair>::Repr 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 elem_len = Grp::ElemLen::to_usize();
|
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
|
||||||
let ke2_message_size = KE::ke2_message_size();
|
let ke2_message_size = CS::KeyExchange::ke2_message_size();
|
||||||
let checked_slice = check_slice_size(
|
let checked_slice = check_slice_size(
|
||||||
second_message_bytes,
|
second_message_bytes,
|
||||||
elem_len + envelope_size + ke2_message_size,
|
elem_len + envelope_size + ke2_message_size,
|
||||||
@@ -235,17 +217,17 @@ where
|
|||||||
// correct subgroup
|
// correct subgroup
|
||||||
let beta_bytes = &checked_slice[..elem_len];
|
let beta_bytes = &checked_slice[..elem_len];
|
||||||
let arr = GenericArray::from_slice(beta_bytes);
|
let arr = GenericArray::from_slice(beta_bytes);
|
||||||
let beta = Grp::from_element_slice(arr)?;
|
let beta = CS::Group::from_element_slice(arr)?;
|
||||||
|
|
||||||
let envelope =
|
let envelope =
|
||||||
Envelope::<D>::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
Envelope::<CS::Hash>::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||||
|
|
||||||
let ke2_message =
|
let ke2_message =
|
||||||
KE::KE2Message::try_from(checked_slice[elem_len + envelope_size..].to_vec())?;
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message::try_from(
|
||||||
|
checked_slice[elem_len + envelope_size..].to_vec(),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_key_format: PhantomData,
|
|
||||||
_key_exchange: PhantomData,
|
|
||||||
beta,
|
beta,
|
||||||
envelope,
|
envelope,
|
||||||
ke2_message,
|
ke2_message,
|
||||||
@@ -256,7 +238,7 @@ where
|
|||||||
/// The answer sent by the client to the server, upon reception of the
|
/// The answer sent by the client to the server, upon reception of the
|
||||||
/// sealed envelope
|
/// sealed envelope
|
||||||
pub struct LoginThirdMessage<CS: CipherSuite> {
|
pub struct LoginThirdMessage<CS: CipherSuite> {
|
||||||
ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message,
|
ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
||||||
@@ -264,7 +246,9 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
|||||||
|
|
||||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
let ke3_message =
|
let ke3_message =
|
||||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message::try_from(bytes.to_vec())?;
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message::try_from(
|
||||||
|
bytes.to_vec(),
|
||||||
|
)?;
|
||||||
Ok(Self { ke3_message })
|
Ok(Self { ke3_message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -533,14 +517,12 @@ where
|
|||||||
/// byte representation for the server's registration state
|
/// byte representation for the server's registration state
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut output: Vec<u8> = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec();
|
let mut output: Vec<u8> = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec();
|
||||||
match &self.client_s_pk {
|
self.client_s_pk
|
||||||
Some(v) => output.extend_from_slice(&v.to_arr()),
|
.iter()
|
||||||
None => {}
|
.for_each(|v| output.extend_from_slice(&v));
|
||||||
};
|
self.envelope
|
||||||
match &self.envelope {
|
.iter()
|
||||||
Some(v) => output.extend_from_slice(&v.to_bytes()),
|
.for_each(|v| output.extend_from_slice(&v.to_bytes()));
|
||||||
None => {}
|
|
||||||
};
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,21 +623,20 @@ where
|
|||||||
|
|
||||||
/// The state elements the client holds to perform a login
|
/// The state elements the client holds to perform a login
|
||||||
pub struct ClientLogin<CS: CipherSuite> {
|
pub struct ClientLogin<CS: CipherSuite> {
|
||||||
/// A choice of the keypair type
|
|
||||||
_key_format: PhantomData<CS::KeyFormat>,
|
|
||||||
/// A blinding factor, which is used to mask (and unmask) secret
|
/// A blinding factor, which is used to mask (and unmask) secret
|
||||||
/// information before transmission
|
/// information before transmission
|
||||||
blinding_factor: <CS::Group as Group>::Scalar,
|
blinding_factor: <CS::Group as Group>::Scalar,
|
||||||
/// The user's password
|
/// The user's password
|
||||||
password: Vec<u8>,
|
password: Vec<u8>,
|
||||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State,
|
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||||
type Error = ProtocolError;
|
type Error = ProtocolError;
|
||||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
|
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
|
||||||
let ke1_state_size = <CS::KeyExchange as KeyExchange<CS::Hash>>::ke1_state_size();
|
let ke1_state_size =
|
||||||
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::ke1_state_size();
|
||||||
|
|
||||||
let min_expected_len = scalar_len + ke1_state_size;
|
let min_expected_len = scalar_len + ke1_state_size;
|
||||||
let checked_slice = (if bytes.len() <= min_expected_len {
|
let checked_slice = (if bytes.len() <= min_expected_len {
|
||||||
@@ -670,12 +651,12 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
|||||||
|
|
||||||
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
|
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::Group::from_scalar_slice(blinding_factor_bytes)?;
|
||||||
let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State::try_from(
|
let ke1_state =
|
||||||
checked_slice[scalar_len..scalar_len + ke1_state_size].to_vec(),
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State::try_from(
|
||||||
)?;
|
checked_slice[scalar_len..scalar_len + ke1_state_size].to_vec(),
|
||||||
|
)?;
|
||||||
let password = bytes[scalar_len + ke1_state_size..].to_vec();
|
let password = bytes[scalar_len + ke1_state_size..].to_vec();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_key_format: PhantomData,
|
|
||||||
blinding_factor,
|
blinding_factor,
|
||||||
password,
|
password,
|
||||||
ke1_state,
|
ke1_state,
|
||||||
@@ -737,15 +718,13 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
blinding_factor,
|
blinding_factor,
|
||||||
} = oprf::generate_oprf1::<R, CS::Group>(&password, pepper, rng)?;
|
} = oprf::generate_oprf1::<R, CS::Group>(&password, pepper, rng)?;
|
||||||
|
|
||||||
let (ke1_state, ke1_message) =
|
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(alpha.to_arr().to_vec(), rng)?;
|
||||||
CS::KeyExchange::generate_ke1::<_, CS::KeyFormat>(alpha.to_arr().to_vec(), rng)?;
|
|
||||||
|
|
||||||
let l1 = LoginFirstMessage { alpha, ke1_message };
|
let l1 = LoginFirstMessage { alpha, ke1_message };
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
l1,
|
l1,
|
||||||
Self {
|
Self {
|
||||||
_key_format: PhantomData,
|
|
||||||
blinding_factor,
|
blinding_factor,
|
||||||
password: password.to_vec(),
|
password: password.to_vec(),
|
||||||
ke1_state,
|
ke1_state,
|
||||||
@@ -790,7 +769,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn finish<R: RngCore + CryptoRng>(
|
pub fn finish<R: RngCore + CryptoRng>(
|
||||||
self,
|
self,
|
||||||
l2: LoginSecondMessage<CS::Group, CS::KeyFormat, CS::KeyExchange, CS::Hash>,
|
l2: LoginSecondMessage<CS>,
|
||||||
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
||||||
_client_e_sk_rng: &mut R,
|
_client_e_sk_rng: &mut R,
|
||||||
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
||||||
@@ -810,7 +789,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
err => PakeError::from(err),
|
err => PakeError::from(err),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let (shared_secret, ke3_message) = CS::KeyExchange::generate_ke3::<CS::KeyFormat>(
|
let (shared_secret, ke3_message) = CS::KeyExchange::generate_ke3(
|
||||||
l2_bytes,
|
l2_bytes,
|
||||||
l2.ke2_message,
|
l2.ke2_message,
|
||||||
&self.ke1_state,
|
&self.ke1_state,
|
||||||
@@ -828,7 +807,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
|
|
||||||
/// The state elements the server holds to record a login
|
/// The state elements the server holds to record a login
|
||||||
pub struct ServerLogin<CS: CipherSuite> {
|
pub struct ServerLogin<CS: CipherSuite> {
|
||||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State,
|
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State,
|
||||||
_cs: PhantomData<CS>,
|
_cs: PhantomData<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,22 +816,15 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ServerLogin<CS> {
|
|||||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_cs: PhantomData,
|
_cs: PhantomData,
|
||||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State::try_from(
|
ke2_state:
|
||||||
bytes.to_vec(),
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State::try_from(
|
||||||
)?,
|
bytes.to_vec(),
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerLoginStartResult<CS> = (
|
type ServerLoginStartResult<CS> = (LoginSecondMessage<CS>, ServerLogin<CS>);
|
||||||
LoginSecondMessage<
|
|
||||||
<CS as CipherSuite>::Group,
|
|
||||||
<CS as CipherSuite>::KeyFormat,
|
|
||||||
<CS as CipherSuite>::KeyExchange,
|
|
||||||
<CS as CipherSuite>::Hash,
|
|
||||||
>,
|
|
||||||
ServerLogin<CS>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ServerLogin<CS> {
|
impl<CS: CipherSuite> ServerLogin<CS> {
|
||||||
/// byte representation for the server's login state
|
/// byte representation for the server's login state
|
||||||
@@ -911,7 +883,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
|
|
||||||
let l2_component: Vec<u8> = [&beta.to_arr()[..], &envelope.to_bytes()].concat();
|
let l2_component: Vec<u8> = [&beta.to_arr()[..], &envelope.to_bytes()].concat();
|
||||||
|
|
||||||
let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2::<_, CS::KeyFormat>(
|
let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2(
|
||||||
rng,
|
rng,
|
||||||
l1_bytes.to_vec(),
|
l1_bytes.to_vec(),
|
||||||
l2_component,
|
l2_component,
|
||||||
@@ -921,8 +893,6 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let l2 = LoginSecondMessage {
|
let l2 = LoginSecondMessage {
|
||||||
_key_format: PhantomData,
|
|
||||||
_key_exchange: PhantomData,
|
|
||||||
beta,
|
beta,
|
||||||
envelope,
|
envelope,
|
||||||
ke2_message,
|
ke2_message,
|
||||||
@@ -975,18 +945,20 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
/// # Ok::<(), ProtocolError>(())
|
/// # Ok::<(), ProtocolError>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub fn finish(&self, message: LoginThirdMessage<CS>) -> Result<Vec<u8>, ProtocolError> {
|
pub fn finish(&self, message: LoginThirdMessage<CS>) -> Result<Vec<u8>, ProtocolError> {
|
||||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::finish_ke(message.ke3_message, &self.ke2_state)
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::finish_ke(
|
||||||
.map_err(|e| match e {
|
message.ke3_message,
|
||||||
ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => {
|
&self.ke2_state,
|
||||||
ProtocolError::VerificationError(PakeError::InvalidLoginError)
|
)
|
||||||
}
|
.map_err(|e| match e {
|
||||||
err => err,
|
ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => {
|
||||||
})
|
ProtocolError::VerificationError(PakeError::InvalidLoginError)
|
||||||
|
}
|
||||||
|
err => err,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
fn get_password_derived_key<G: Group, SH: SlowHash<D>, D: Hash>(
|
fn get_password_derived_key<G: Group, SH: SlowHash<D>, D: Hash>(
|
||||||
password: Vec<u8>,
|
password: Vec<u8>,
|
||||||
beta: G,
|
beta: G,
|
||||||
|
|||||||
+15
-17
@@ -471,19 +471,15 @@ fn test_l3() -> Result<(), PakeError> {
|
|||||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||||
|
|
||||||
let mut client_e_sk_rng = CycleRng::new(parameters.client_e_sk.to_vec());
|
let mut client_e_sk_rng = CycleRng::new(parameters.client_e_sk.to_vec());
|
||||||
let (l3, shared_secret, export_key_login) = ClientLogin::<X255193dhNoSlowHash>::try_from(
|
let (l3, shared_secret, export_key_login) =
|
||||||
¶meters.client_login_state[..],
|
ClientLogin::<X255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||||
)
|
.unwrap()
|
||||||
.unwrap()
|
.finish(
|
||||||
.finish(
|
LoginSecondMessage::<X255193dhNoSlowHash>::try_from(¶meters.l2[..]).unwrap(),
|
||||||
LoginSecondMessage::<EdwardsPoint, X25519KeyPair, TripleDH, sha2::Sha256>::try_from(
|
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||||
¶meters.l2[..],
|
&mut client_e_sk_rng,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap();
|
||||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
|
||||||
&mut client_e_sk_rng,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hex::encode(¶meters.shared_secret),
|
hex::encode(¶meters.shared_secret),
|
||||||
@@ -558,10 +554,12 @@ fn test_complete_flow(
|
|||||||
hex::encode(login_export_key)
|
hex::encode(login_export_key)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let res = match client_login_result {
|
let res = matches!(
|
||||||
Err(ProtocolError::VerificationError(PakeError::InvalidLoginError)) => true,
|
client_login_result,
|
||||||
_ => false,
|
Err(ProtocolError::VerificationError(
|
||||||
};
|
PakeError::InvalidLoginError
|
||||||
|
))
|
||||||
|
);
|
||||||
assert!(res);
|
assert!(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,8 +167,10 @@ fn login_first_message_roundtrip() {
|
|||||||
rng.fill_bytes(&mut client_nonce);
|
rng.fill_bytes(&mut client_nonce);
|
||||||
|
|
||||||
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
|
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
|
||||||
let reg =
|
let reg = <TripleDH as KeyExchange<sha2::Sha256, crate::keypair::X25519KeyPair>>::KE1Message::try_from(
|
||||||
<TripleDH as KeyExchange<sha2::Sha256>>::KE1Message::try_from(ke1m[..].to_vec()).unwrap();
|
ke1m[..].to_vec(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let reg_bytes = reg.to_bytes();
|
let reg_bytes = reg.to_bytes();
|
||||||
assert_eq!(reg_bytes, ke1m);
|
assert_eq!(reg_bytes, ke1m);
|
||||||
}
|
}
|
||||||
@@ -197,7 +199,7 @@ proptest! {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nocrash_login_second_message(bytes in vec(any::<u8>(), 0..500)) {
|
fn test_nocrash_login_second_message(bytes in vec(any::<u8>(), 0..500)) {
|
||||||
LoginSecondMessage::<RistrettoPoint, crate::keypair::X25519KeyPair, TripleDH, sha2::Sha512>::try_from(&bytes[..]).map_or(true, |_| true);
|
LoginSecondMessage::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user