Adding a hash type to CipherSuite (#24)
This commit is contained in:
Generated
+345
-346
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ u32_backend = ["curve25519-dalek/u32_backend", "x25519-dalek/u32_backend"]
|
||||
|
||||
[dependencies]
|
||||
curve25519-dalek = { version = "2.1.0", default-features = false, features = ["std"]}
|
||||
digest = "0.9.0"
|
||||
displaydoc = "0.1.7"
|
||||
generic-array = "0.14.3"
|
||||
hkdf = "0.9.0"
|
||||
|
||||
+7
-4
@@ -7,6 +7,7 @@
|
||||
|
||||
use crate::{
|
||||
errors::InternalPakeError,
|
||||
hash::Hash,
|
||||
key_exchange::traits::KeyExchange,
|
||||
keypair::{Key, KeyPair},
|
||||
map_to_curve::GroupWithMapToCurve,
|
||||
@@ -22,7 +23,8 @@ use rand_core::{CryptoRng, RngCore};
|
||||
/// `map_to_curve::GroupWithMapToCurve`.
|
||||
/// * `KeyFormat`: a keypair type composed of public and private components
|
||||
/// * `KeyExchange`: The key exchange protocol to use in the login step
|
||||
/// * `SlowHash`: a slow hashing function, typically used for password hashing
|
||||
/// * `Hash`: The main hashing function to use
|
||||
/// * `SlowHash`: A slow hashing function, typically used for password hashing
|
||||
pub trait CipherSuite {
|
||||
/// A finite cyclic group along with a point representation along with
|
||||
/// an extension trait PasswordToCurve that allows some customization on
|
||||
@@ -32,10 +34,11 @@ pub trait CipherSuite {
|
||||
/// A keypair type composed of public and private components
|
||||
type KeyFormat: KeyPair<Repr = Key> + PartialEq;
|
||||
/// A key exchange protocol
|
||||
type KeyExchange: KeyExchange;
|
||||
type KeyExchange: KeyExchange<Self::Hash>;
|
||||
/// The main hash function use (for HKDF computations and hashing transcripts)
|
||||
type Hash: Hash;
|
||||
/// A slow hashing function, typically used for password hashing
|
||||
type SlowHash: SlowHash;
|
||||
|
||||
type SlowHash: SlowHash<Self::Hash>;
|
||||
/// Generating a random key pair given a cryptographic rng
|
||||
fn generate_random_keypair<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
|
||||
+32
-22
@@ -4,6 +4,8 @@
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
use crate::errors::InternalPakeError;
|
||||
use crate::hash::Hash;
|
||||
use digest::Digest;
|
||||
use generic_array::{
|
||||
typenum::{Unsigned, U32},
|
||||
GenericArray,
|
||||
@@ -11,7 +13,6 @@ use generic_array::{
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::Sha256;
|
||||
|
||||
// Constant string used as salt for HKDF computation
|
||||
const STR_ENVU: &[u8] = b"EnvU";
|
||||
@@ -32,31 +33,40 @@ const NONCE_LEN: usize = 32;
|
||||
/// The specification update has simplified this assumption by taking
|
||||
/// an XOR-based approach without compromising on security, and to avoid
|
||||
/// the confusion around the implementation of an RKR-secure encryption.
|
||||
pub(crate) struct Envelope {
|
||||
pub(crate) struct Envelope<D: Hash> {
|
||||
nonce: Vec<u8>,
|
||||
ciphertext: Vec<u8>,
|
||||
hmac: GenericArray<u8, U32>,
|
||||
hmac: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
pub(crate) struct OpenedEnvelope {
|
||||
pub(crate) plaintext: Vec<u8>,
|
||||
pub(crate) export_key: GenericArray<u8, ExportKeySize>,
|
||||
}
|
||||
|
||||
impl<D: Hash> Envelope<D> {
|
||||
/// The additional number of bytes added to the plaintext
|
||||
pub(crate) fn additional_size() -> usize {
|
||||
NONCE_LEN + U32::to_usize()
|
||||
NONCE_LEN + <D as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn hmac_key_size() -> usize {
|
||||
U32::to_usize()
|
||||
<D as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn hmac_size() -> usize {
|
||||
U32::to_usize()
|
||||
<D as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn export_key_size() -> usize {
|
||||
ExportKeySize::to_usize()
|
||||
}
|
||||
|
||||
pub(crate) fn new(nonce: Vec<u8>, ciphertext: Vec<u8>, hmac: GenericArray<u8, U32>) -> Self {
|
||||
pub(crate) fn new(
|
||||
nonce: Vec<u8>,
|
||||
ciphertext: Vec<u8>,
|
||||
hmac: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
nonce,
|
||||
ciphertext,
|
||||
@@ -93,7 +103,7 @@ impl Envelope {
|
||||
let mut nonce = vec![0u8; NONCE_LEN];
|
||||
rng.fill_bytes(&mut nonce);
|
||||
|
||||
let h = Hkdf::<Sha256>::new(Some(&nonce), &key);
|
||||
let h = Hkdf::<D>::new(Some(&nonce), &key);
|
||||
let mut okm = vec![0u8; plaintext.len() + Self::hmac_key_size() + Self::export_key_size()];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
@@ -108,7 +118,7 @@ impl Envelope {
|
||||
.collect();
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
Hmac::<D>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
hmac.update(&ciphertext);
|
||||
hmac.update(&aad);
|
||||
|
||||
@@ -120,12 +130,8 @@ impl Envelope {
|
||||
|
||||
/// Attempts to decrypt the envelope using a key, which is successful only if the key and
|
||||
/// aad used to construct the envelope are the same.
|
||||
pub(crate) fn open(
|
||||
&self,
|
||||
key: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<(Vec<u8>, GenericArray<u8, ExportKeySize>), InternalPakeError> {
|
||||
let h = Hkdf::<Sha256>::new(Some(&self.nonce), &key);
|
||||
pub(crate) fn open(&self, key: &[u8], aad: &[u8]) -> Result<OpenedEnvelope, InternalPakeError> {
|
||||
let h = Hkdf::<D>::new(Some(&self.nonce), &key);
|
||||
let mut okm =
|
||||
vec![0u8; self.ciphertext.len() + Self::hmac_key_size() + Self::export_key_size()];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
@@ -135,7 +141,7 @@ impl Envelope {
|
||||
let export_key = &okm[self.ciphertext.len() + Self::hmac_key_size()..];
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
Hmac::<D>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
hmac.update(&self.ciphertext);
|
||||
hmac.update(aad);
|
||||
if hmac.verify(&self.hmac).is_err() {
|
||||
@@ -147,7 +153,10 @@ impl Envelope {
|
||||
.zip(self.ciphertext.iter())
|
||||
.map(|(&x1, &x2)| x1 ^ x2)
|
||||
.collect();
|
||||
Ok((plaintext, *GenericArray::from_slice(&export_key)))
|
||||
Ok(OpenedEnvelope {
|
||||
plaintext,
|
||||
export_key: *GenericArray::from_slice(&export_key),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,9 +174,10 @@ mod tests {
|
||||
let mut msg = [0u8; 100];
|
||||
rng.fill_bytes(&mut msg);
|
||||
|
||||
let (ciphertext, export_key_1) = Envelope::seal(&key, &msg, b"aad", &mut rng).unwrap();
|
||||
let (plaintext, export_key_2) = ciphertext.open(&key, b"aad").unwrap();
|
||||
assert_eq!(&msg.to_vec(), &plaintext);
|
||||
assert_eq!(&export_key_1.to_vec(), &export_key_2.to_vec());
|
||||
let (envelope, export_key_1) =
|
||||
Envelope::<sha2::Sha256>::seal(&key, &msg, b"aad", &mut rng).unwrap();
|
||||
let opened_envelope = envelope.open(&key, b"aad").unwrap();
|
||||
assert_eq!(&msg.to_vec(), &opened_envelope.plaintext);
|
||||
assert_eq!(&export_key_1.to_vec(), &opened_envelope.export_key.to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
+8
-10
@@ -13,14 +13,13 @@ use curve25519_dalek::{
|
||||
ristretto::{CompressedRistretto, RistrettoPoint},
|
||||
scalar::Scalar,
|
||||
};
|
||||
use digest::Digest;
|
||||
use generic_array::{
|
||||
typenum::{U32, U64},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use sha2::Sha256;
|
||||
use std::ops::Mul;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@@ -56,6 +55,7 @@ pub trait Group: Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
|
||||
/// may not be necessary as this function is going to be called with the
|
||||
/// output of a kdf.
|
||||
type UniformBytesLen: ArrayLength<u8>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes of the correct length to a curve point
|
||||
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> Self;
|
||||
}
|
||||
@@ -98,11 +98,8 @@ impl Group for RistrettoPoint {
|
||||
|
||||
type UniformBytesLen = U64;
|
||||
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> Self {
|
||||
// This is because RistrettoPoint is on an obsolete sha2 version, see https://github.com/dalek-cryptography/curve25519-dalek/pull/327
|
||||
let mut bits = [0u8; 64];
|
||||
let mut hasher = sha2::Sha512::new();
|
||||
hasher.update(uniform_bytes);
|
||||
bits.copy_from_slice(&hasher.finalize());
|
||||
bits.copy_from_slice(&uniform_bytes);
|
||||
|
||||
RistrettoPoint::from_uniform_bytes(&bits)
|
||||
}
|
||||
@@ -146,16 +143,17 @@ impl Group for EdwardsPoint {
|
||||
|
||||
type UniformBytesLen = U32;
|
||||
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> Self {
|
||||
let mut result = [0u8; 32];
|
||||
const HASH_SIZE: usize = 32;
|
||||
let mut result = [0u8; HASH_SIZE];
|
||||
let mut counter = 0;
|
||||
let mut wrapped_point: Option<EdwardsPoint> = None;
|
||||
|
||||
while wrapped_point.is_none() {
|
||||
result.copy_from_slice(
|
||||
&Sha256::new()
|
||||
.chain(&uniform_bytes[..32])
|
||||
.chain(&uniform_bytes[..HASH_SIZE])
|
||||
.chain(&[counter])
|
||||
.finalize()[..32],
|
||||
.finalize()[..HASH_SIZE],
|
||||
);
|
||||
wrapped_point = CompressedEdwardsY::from_slice(&result).decompress();
|
||||
counter += 1;
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// 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 digest::{BlockInput, FixedOutput, Reset, Update};
|
||||
use generic_array::ArrayLength;
|
||||
|
||||
/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC
|
||||
// Associated types could be simplified when they are made as defaults:
|
||||
// https://github.com/rust-lang/rust/issues/29661
|
||||
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 {
|
||||
type BlockSize = T::BlockSize;
|
||||
type OutputSize = T::OutputSize;
|
||||
}
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
use crate::{
|
||||
errors::{InternalPakeError, ProtocolError},
|
||||
hash::Hash,
|
||||
keypair::{Key, KeyPair},
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub trait KeyExchange {
|
||||
pub trait KeyExchange<D: Hash> {
|
||||
type KE1State: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||
type KE2State: TryFrom<Vec<u8>, Error = ProtocolError> + ToBytes;
|
||||
type KE1Message: TryFrom<Vec<u8>, Error = InternalPakeError> + ToBytes;
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
//! An implementation of the Triple Diffie-Hellman key exchange protocol
|
||||
use crate::{
|
||||
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
|
||||
hash::Hash,
|
||||
key_exchange::traits::{KeyExchange, ToBytes},
|
||||
keypair::{Key, KeyPair, SizedBytes},
|
||||
sized_bytes_using_constant_and_try_from,
|
||||
};
|
||||
use digest::Digest;
|
||||
use generic_array::{
|
||||
typenum::{U64, U96},
|
||||
GenericArray,
|
||||
@@ -18,7 +20,6 @@ use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
const KEY_LEN: usize = 32;
|
||||
@@ -29,9 +30,9 @@ const KE2_MESSAGE_LEN: usize = NONCE_LEN + 2 * KEY_LEN;
|
||||
static STR_3DH: &[u8] = b"3DH keys";
|
||||
|
||||
/// The Triple Diffie-Hellman key exchange implementation
|
||||
pub struct TripleDH {}
|
||||
pub struct TripleDH;
|
||||
|
||||
impl KeyExchange for TripleDH {
|
||||
impl<D: Hash> KeyExchange<D> for TripleDH {
|
||||
type KE1State = KE1State;
|
||||
type KE2State = KE2State;
|
||||
type KE1Message = KE1Message;
|
||||
@@ -52,7 +53,7 @@ impl KeyExchange for TripleDH {
|
||||
};
|
||||
|
||||
let l1_data: Vec<u8> = [&l1_component[..], &ke1_message.to_bytes()].concat();
|
||||
let mut hasher = Sha256::new();
|
||||
let mut hasher = D::new();
|
||||
hasher.update(&l1_data);
|
||||
let hashed_l1 = hasher.finalize();
|
||||
|
||||
@@ -78,7 +79,7 @@ impl KeyExchange for TripleDH {
|
||||
let mut server_nonce = [0u8; NONCE_LEN];
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
|
||||
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat>(
|
||||
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat, D>(
|
||||
TripleDHComponents {
|
||||
pk1: ke1_message.client_e_pk.clone(),
|
||||
sk1: server_e_kp.private().clone(),
|
||||
@@ -93,7 +94,7 @@ impl KeyExchange for TripleDH {
|
||||
KeyFormat::public_from_private(&server_s_sk),
|
||||
)?;
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
let mut hasher = D::new();
|
||||
hasher.update(&l1_bytes);
|
||||
let hashed_l1 = hasher.finalize();
|
||||
|
||||
@@ -105,11 +106,11 @@ impl KeyExchange for TripleDH {
|
||||
]
|
||||
.concat();
|
||||
|
||||
let mut hasher2 = Sha256::new();
|
||||
let mut hasher2 = D::new();
|
||||
hasher2.update(&transcript2);
|
||||
let hashed_transcript = hasher2.finalize();
|
||||
|
||||
let mut mac = Hmac::<Sha256>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||
let mut mac = Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||
mac.update(&hashed_transcript);
|
||||
|
||||
Ok((
|
||||
@@ -133,7 +134,7 @@ impl KeyExchange for TripleDH {
|
||||
server_s_pk: KeyFormat::Repr,
|
||||
client_s_sk: KeyFormat::Repr,
|
||||
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError> {
|
||||
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat>(
|
||||
let (shared_secret, km2, km3) = derive_3dh_keys::<KeyFormat, D>(
|
||||
TripleDHComponents {
|
||||
pk1: ke2_message.server_e_pk.clone(),
|
||||
sk1: ke1_state.client_e_sk.clone(),
|
||||
@@ -156,12 +157,12 @@ impl KeyExchange for TripleDH {
|
||||
]
|
||||
.concat();
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
let mut hasher = D::new();
|
||||
hasher.update(&transcript);
|
||||
let hashed_transcript = hasher.finalize();
|
||||
|
||||
let mut server_mac =
|
||||
Hmac::<Sha256>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
|
||||
server_mac.update(&hashed_transcript);
|
||||
|
||||
if ke2_message.mac != server_mac.finalize().into_bytes().to_vec() {
|
||||
@@ -171,7 +172,7 @@ impl KeyExchange for TripleDH {
|
||||
}
|
||||
|
||||
let mut client_mac =
|
||||
Hmac::<Sha256>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
|
||||
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
|
||||
client_mac.update(&hashed_transcript);
|
||||
|
||||
Ok((
|
||||
@@ -187,7 +188,7 @@ impl KeyExchange for TripleDH {
|
||||
ke2_state: &Self::KE2State,
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
let mut client_mac =
|
||||
Hmac::<Sha256>::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);
|
||||
|
||||
if ke3_message.mac != client_mac.finalize().into_bytes().to_vec() {
|
||||
@@ -350,21 +351,21 @@ struct TripleDHComponents {
|
||||
}
|
||||
|
||||
// Consists of a shared secret, followed by two mac keys
|
||||
type TripleDHDerivationResult = (
|
||||
GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
type TripleDHDerivationResult<D> = (
|
||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
||||
GenericArray<u8, <D as Hash>::OutputSize>,
|
||||
);
|
||||
|
||||
// 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
|
||||
fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>>(
|
||||
fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>, D: Hash>(
|
||||
dh: TripleDHComponents,
|
||||
client_nonce: &[u8],
|
||||
server_nonce: &[u8],
|
||||
client_s_pk: KeyFormat::Repr,
|
||||
server_s_pk: KeyFormat::Repr,
|
||||
) -> Result<TripleDHDerivationResult, ProtocolError> {
|
||||
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
|
||||
let ikm: Vec<u8> = [
|
||||
&KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..],
|
||||
&KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..],
|
||||
@@ -383,13 +384,13 @@ fn derive_3dh_keys<KeyFormat: KeyPair<Repr = Key>>(
|
||||
|
||||
const OUTPUT_SIZE: usize = 32;
|
||||
let mut okm = [0u8; 3 * OUTPUT_SIZE];
|
||||
let h = Hkdf::<Sha256>::new(None, &ikm);
|
||||
let h = Hkdf::<D>::new(None, &ikm);
|
||||
h.expand(&info, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
Ok((
|
||||
*GenericArray::from_slice(&okm[..OUTPUT_SIZE]),
|
||||
*GenericArray::from_slice(&okm[OUTPUT_SIZE..2 * OUTPUT_SIZE]),
|
||||
*GenericArray::from_slice(&okm[2 * OUTPUT_SIZE..]),
|
||||
GenericArray::clone_from_slice(&okm[..OUTPUT_SIZE]),
|
||||
GenericArray::clone_from_slice(&okm[OUTPUT_SIZE..2 * OUTPUT_SIZE]),
|
||||
GenericArray::clone_from_slice(&okm[2 * OUTPUT_SIZE..]),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -14,7 +14,8 @@
|
||||
//! to be kept consistent throughout protocol execution. These include:
|
||||
//! * a finite cyclic group along with a point representation,
|
||||
//! * a keypair type,
|
||||
//! * a key exchange protocol, and
|
||||
//! * a key exchange protocol,
|
||||
//! * a hashing function, and
|
||||
//! * a slow hashing function.
|
||||
//!
|
||||
//! We will use the following choices in this example:
|
||||
@@ -25,6 +26,7 @@
|
||||
//! type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! type Hash = sha2::Sha256;
|
||||
//! type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! }
|
||||
//! ```
|
||||
@@ -46,6 +48,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use rand_core::{OsRng, RngCore};
|
||||
@@ -78,6 +81,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use rand_core::{OsRng, RngCore};
|
||||
@@ -107,6 +111,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -139,6 +144,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -173,6 +179,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -215,6 +222,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -244,6 +252,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -288,6 +297,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -344,6 +354,7 @@
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand_core::{OsRng, RngCore};
|
||||
@@ -400,6 +411,7 @@ pub mod opaque;
|
||||
|
||||
pub mod ciphersuite;
|
||||
mod envelope;
|
||||
mod hash;
|
||||
|
||||
pub mod group;
|
||||
pub mod map_to_curve;
|
||||
|
||||
+4
-3
@@ -11,8 +11,9 @@ use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint};
|
||||
|
||||
use generic_array::GenericArray;
|
||||
use hkdf::Hkdf;
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
/// A subtrait of Group specifying how to has a password into a point
|
||||
/// A subtrait of Group specifying how to hash a password into a point
|
||||
pub trait GroupWithMapToCurve: Group {
|
||||
/// transforms a password and optional pepper into a curve point
|
||||
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self;
|
||||
@@ -20,14 +21,14 @@ pub trait GroupWithMapToCurve: Group {
|
||||
|
||||
impl GroupWithMapToCurve for RistrettoPoint {
|
||||
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
||||
let (hashed_input, _) = Hkdf::<sha2::Sha512>::extract(pepper, password);
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(pepper, password);
|
||||
<Self as Group>::hash_to_curve(GenericArray::from_slice(&hashed_input))
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupWithMapToCurve for EdwardsPoint {
|
||||
fn map_to_curve(password: &[u8], pepper: Option<&[u8]>) -> Self {
|
||||
let (hashed_input, _) = Hkdf::<sha2::Sha256>::extract(pepper, password);
|
||||
let (hashed_input, _) = Hkdf::<Sha256>::extract(pepper, password);
|
||||
<Self as Group>::hash_to_curve(GenericArray::from_slice(&hashed_input))
|
||||
}
|
||||
}
|
||||
|
||||
+62
-44
@@ -10,6 +10,7 @@ use crate::{
|
||||
envelope::{Envelope, ExportKeySize},
|
||||
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
|
||||
group::Group,
|
||||
hash::Hash,
|
||||
key_exchange::traits::{KeyExchange, ToBytes},
|
||||
keypair::{Key, KeyPair, SizedBytes},
|
||||
oprf,
|
||||
@@ -87,17 +88,18 @@ where
|
||||
|
||||
/// The final message from the client, containing sealed cryptographic
|
||||
/// identifiers
|
||||
pub struct RegisterThirdMessage<KeyFormat: KeyPair> {
|
||||
pub struct RegisterThirdMessage<KeyFormat: KeyPair, D: Hash> {
|
||||
/// The "envelope" generated by the user, containing sealed
|
||||
/// cryptographic identifiers
|
||||
envelope: Envelope,
|
||||
envelope: Envelope<D>,
|
||||
/// The user's public key
|
||||
client_s_pk: KeyFormat::Repr,
|
||||
}
|
||||
|
||||
impl<KeyFormat> RegisterThirdMessage<KeyFormat>
|
||||
impl<KeyFormat, D> RegisterThirdMessage<KeyFormat, D>
|
||||
where
|
||||
KeyFormat: KeyPair,
|
||||
D: Hash,
|
||||
{
|
||||
/// byte representation for the registration upload message
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -105,15 +107,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<KeyFormat> TryFrom<&[u8]> for RegisterThirdMessage<KeyFormat>
|
||||
impl<KeyFormat, D> TryFrom<&[u8]> for RegisterThirdMessage<KeyFormat, D>
|
||||
where
|
||||
KeyFormat: KeyPair,
|
||||
D: Hash,
|
||||
{
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn try_from(third_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let envelope_size = key_len + Envelope::<D>::additional_size();
|
||||
let checked_bytes = check_slice_size(
|
||||
third_message_bytes,
|
||||
envelope_size + key_len,
|
||||
@@ -123,7 +126,7 @@ where
|
||||
let client_s_pk = KeyFormat::check_public_key(unchecked_client_s_pk)?;
|
||||
|
||||
Ok(Self {
|
||||
envelope: Envelope::from_bytes(&checked_bytes[..envelope_size])?,
|
||||
envelope: Envelope::<D>::from_bytes(&checked_bytes[..envelope_size])?,
|
||||
client_s_pk,
|
||||
})
|
||||
}
|
||||
@@ -133,7 +136,7 @@ where
|
||||
pub struct LoginFirstMessage<CS: CipherSuite> {
|
||||
/// blinded password information
|
||||
alpha: CS::Group,
|
||||
ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message,
|
||||
ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
||||
@@ -145,7 +148,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for LoginFirstMessage<CS> {
|
||||
let arr = GenericArray::from_slice(&first_message_bytes[..elem_len]);
|
||||
let alpha = CS::Group::from_element_slice(arr)?;
|
||||
|
||||
let ke1_message = <CS::KeyExchange as KeyExchange>::KE1Message::try_from(
|
||||
let ke1_message = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1Message::try_from(
|
||||
first_message_bytes[elem_len..].to_vec(),
|
||||
)?;
|
||||
Ok(Self { alpha, ke1_message })
|
||||
@@ -161,25 +164,27 @@ impl<CS: CipherSuite> LoginFirstMessage<CS> {
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// login attempt.
|
||||
pub struct LoginSecondMessage<Grp, KeyFormat, KE>
|
||||
pub struct LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
KeyFormat: KeyPair<Repr = Key>,
|
||||
KE: KeyExchange,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
_key_format: PhantomData<KeyFormat>,
|
||||
_key_exchange: PhantomData<KE>,
|
||||
/// the server's oprf output
|
||||
beta: Grp,
|
||||
/// the user's sealed information,
|
||||
envelope: Envelope,
|
||||
envelope: Envelope<D>,
|
||||
ke2_message: KE::KE2Message,
|
||||
}
|
||||
|
||||
impl<Grp, KeyFormat, KE> LoginSecondMessage<Grp, KeyFormat, KE>
|
||||
impl<Grp, KeyFormat, KE, D> LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair<Repr = Key>,
|
||||
KE: KeyExchange,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
/// byte representation for the login response
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -192,16 +197,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Grp, KeyFormat, KE> TryFrom<&[u8]> for LoginSecondMessage<Grp, KeyFormat, KE>
|
||||
impl<Grp, KeyFormat, KE, D> TryFrom<&[u8]> for LoginSecondMessage<Grp, KeyFormat, KE, D>
|
||||
where
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair<Repr = Key>,
|
||||
KE: KeyExchange,
|
||||
KE: KeyExchange<D>,
|
||||
D: Hash,
|
||||
{
|
||||
type Error = ProtocolError;
|
||||
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let envelope_size = key_len + Envelope::<D>::additional_size();
|
||||
let elem_len = Grp::ElemLen::to_usize();
|
||||
let ke2_message_size = KE::ke2_message_size();
|
||||
let checked_slice = check_slice_size(
|
||||
@@ -216,7 +222,8 @@ where
|
||||
let arr = GenericArray::from_slice(beta_bytes);
|
||||
let beta = Grp::from_element_slice(arr)?;
|
||||
|
||||
let envelope = Envelope::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||
let envelope =
|
||||
Envelope::<D>::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||
|
||||
let ke2_message =
|
||||
KE::KE2Message::try_from(checked_slice[elem_len + envelope_size..].to_vec())?;
|
||||
@@ -234,14 +241,15 @@ where
|
||||
/// The answer sent by the client to the server, upon reception of the
|
||||
/// sealed envelope
|
||||
pub struct LoginThirdMessage<CS: CipherSuite> {
|
||||
ke3_message: <CS::KeyExchange as KeyExchange>::KE3Message,
|
||||
ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for LoginThirdMessage<CS> {
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let ke3_message = <CS::KeyExchange as KeyExchange>::KE3Message::try_from(bytes.to_vec())?;
|
||||
let ke3_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::KE3Message::try_from(bytes.to_vec())?;
|
||||
Ok(Self { ke3_message })
|
||||
}
|
||||
}
|
||||
@@ -310,6 +318,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut rng = OsRng;
|
||||
@@ -336,8 +345,8 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
type ClientRegistrationFinishResult<KeyFormat> = (
|
||||
RegisterThirdMessage<KeyFormat>,
|
||||
type ClientRegistrationFinishResult<KeyFormat, D> = (
|
||||
RegisterThirdMessage<KeyFormat, D>,
|
||||
GenericArray<u8, ExportKeySize>,
|
||||
);
|
||||
|
||||
@@ -361,6 +370,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -378,16 +388,16 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
r2: RegisterSecondMessage<CS::Group>,
|
||||
server_s_pk: &<CS::KeyFormat as KeyPair>::Repr,
|
||||
rng: &mut R,
|
||||
) -> Result<ClientRegistrationFinishResult<CS::KeyFormat>, ProtocolError> {
|
||||
) -> Result<ClientRegistrationFinishResult<CS::KeyFormat, CS::Hash>, ProtocolError> {
|
||||
let client_static_keypair = CS::KeyFormat::generate_random(rng)?;
|
||||
|
||||
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash>(
|
||||
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(
|
||||
self.password.clone(),
|
||||
r2.beta,
|
||||
&self.blinding_factor,
|
||||
)?;
|
||||
|
||||
let (envelope, export_key) = Envelope::seal(
|
||||
let (envelope, export_key) = Envelope::<CS::Hash>::seal(
|
||||
&password_derived_key,
|
||||
&client_static_keypair.private().to_arr(),
|
||||
&server_s_pk.to_arr(),
|
||||
@@ -434,7 +444,7 @@ impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
||||
|
||||
/// The state elements the server holds to record a registration
|
||||
pub struct ServerRegistration<CS: CipherSuite> {
|
||||
envelope: Option<Envelope>,
|
||||
envelope: Option<Envelope<CS::Hash>>,
|
||||
client_s_pk: Option<<CS::KeyFormat as KeyPair>::Repr>,
|
||||
pub(crate) oprf_key: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
@@ -452,7 +462,7 @@ where
|
||||
fn try_from(server_registration_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let key_len = <<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let envelope_size = key_len + Envelope::<CS::Hash>::additional_size();
|
||||
|
||||
if server_registration_bytes.len() == scalar_len {
|
||||
return Ok(Self {
|
||||
@@ -476,7 +486,7 @@ where
|
||||
)?;
|
||||
let client_s_pk = CS::KeyFormat::check_public_key(unchecked_client_s_pk)?;
|
||||
Ok(Self {
|
||||
envelope: Some(Envelope::from_bytes(
|
||||
envelope: Some(Envelope::<CS::Hash>::from_bytes(
|
||||
&checked_bytes[checked_bytes.len() - envelope_size..],
|
||||
)?),
|
||||
client_s_pk: Some(client_s_pk),
|
||||
@@ -526,6 +536,7 @@ where
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -573,6 +584,7 @@ where
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -588,7 +600,7 @@ where
|
||||
/// ```
|
||||
pub fn finish(
|
||||
self,
|
||||
message: RegisterThirdMessage<CS::KeyFormat>,
|
||||
message: RegisterThirdMessage<CS::KeyFormat, CS::Hash>,
|
||||
) -> Result<Self, ProtocolError> {
|
||||
Ok(Self {
|
||||
envelope: Some(message.envelope),
|
||||
@@ -610,7 +622,7 @@ pub struct ClientLogin<CS: CipherSuite> {
|
||||
blinding_factor: <CS::Group as Group>::Scalar,
|
||||
/// The user's password
|
||||
password: Vec<u8>,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange>::KE1State,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State,
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||
@@ -619,8 +631,8 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
|
||||
let blinding_factor_bytes = GenericArray::from_slice(&bytes[..scalar_len]);
|
||||
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
|
||||
let ke1_state_size = <CS::KeyExchange as KeyExchange>::ke1_state_size();
|
||||
let ke1_state = <CS::KeyExchange as KeyExchange>::KE1State::try_from(
|
||||
let ke1_state_size = <CS::KeyExchange as KeyExchange<CS::Hash>>::ke1_state_size();
|
||||
let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash>>::KE1State::try_from(
|
||||
bytes[scalar_len..scalar_len + ke1_state_size].to_vec(),
|
||||
)?;
|
||||
let password = bytes[scalar_len + ke1_state_size..].to_vec();
|
||||
@@ -670,6 +682,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -722,6 +735,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -738,19 +752,19 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// ```
|
||||
pub fn finish<R: RngCore + CryptoRng>(
|
||||
self,
|
||||
l2: LoginSecondMessage<CS::Group, CS::KeyFormat, CS::KeyExchange>,
|
||||
l2: LoginSecondMessage<CS::Group, CS::KeyFormat, CS::KeyExchange, CS::Hash>,
|
||||
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
||||
_client_e_sk_rng: &mut R,
|
||||
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
||||
let l2_bytes: Vec<u8> = [&l2.beta.to_arr()[..], &l2.envelope.to_bytes()].concat();
|
||||
|
||||
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash>(
|
||||
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(
|
||||
self.password.clone(),
|
||||
l2.beta,
|
||||
&self.blinding_factor,
|
||||
)?;
|
||||
|
||||
let (client_s_sk, export_key) = &l2
|
||||
let opened_envelope = &l2
|
||||
.envelope
|
||||
.open(&password_derived_key, &server_s_pk.to_arr())
|
||||
.map_err(|e| match e {
|
||||
@@ -763,20 +777,20 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
l2.ke2_message,
|
||||
&self.ke1_state,
|
||||
server_s_pk.clone(),
|
||||
Key::from_bytes(client_s_sk)?,
|
||||
Key::from_bytes(&opened_envelope.plaintext)?,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
LoginThirdMessage { ke3_message },
|
||||
shared_secret,
|
||||
*export_key,
|
||||
opened_envelope.export_key,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// The state elements the server holds to record a login
|
||||
pub struct ServerLogin<CS: CipherSuite> {
|
||||
ke2_state: <CS::KeyExchange as KeyExchange>::KE2State,
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State,
|
||||
_cs: PhantomData<CS>,
|
||||
}
|
||||
|
||||
@@ -785,7 +799,9 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ServerLogin<CS> {
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
_cs: PhantomData,
|
||||
ke2_state: <CS::KeyExchange as KeyExchange>::KE2State::try_from(bytes.to_vec())?,
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash>>::KE2State::try_from(
|
||||
bytes.to_vec(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -795,6 +811,7 @@ type ServerLoginStartResult<CS> = (
|
||||
<CS as CipherSuite>::Group,
|
||||
<CS as CipherSuite>::KeyFormat,
|
||||
<CS as CipherSuite>::KeyExchange,
|
||||
<CS as CipherSuite>::Hash,
|
||||
>,
|
||||
ServerLogin<CS>,
|
||||
);
|
||||
@@ -825,6 +842,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -901,6 +919,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
/// type Hash = sha2::Sha256;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
/// }
|
||||
/// let mut client_rng = OsRng;
|
||||
@@ -918,24 +937,23 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// # Ok::<(), ProtocolError>(())
|
||||
/// ```
|
||||
pub fn finish(&self, message: LoginThirdMessage<CS>) -> Result<Vec<u8>, ProtocolError> {
|
||||
<CS::KeyExchange as KeyExchange>::finish_ke(message.ke3_message, &self.ke2_state).map_err(
|
||||
|e| match e {
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash>>::finish_ke(message.ke3_message, &self.ke2_state)
|
||||
.map_err(|e| match e {
|
||||
ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => {
|
||||
ProtocolError::VerificationError(PakeError::InvalidLoginError)
|
||||
}
|
||||
err => err,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn get_password_derived_key<G: Group, SH: SlowHash>(
|
||||
fn get_password_derived_key<G: Group, SH: SlowHash<D>, D: Hash>(
|
||||
password: Vec<u8>,
|
||||
beta: G,
|
||||
blinding_factor: &G::Scalar,
|
||||
) -> Result<Vec<u8>, InternalPakeError> {
|
||||
let oprf_output = oprf::generate_oprf3::<G>(&password, beta, blinding_factor)?;
|
||||
let oprf_output = oprf::generate_oprf3::<G, D>(&password, beta, blinding_factor)?;
|
||||
SH::hash(oprf_output)
|
||||
}
|
||||
|
||||
+13
-13
@@ -3,8 +3,11 @@
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
use crate::{errors::InternalPakeError, group::Group, map_to_curve::GroupWithMapToCurve};
|
||||
use generic_array::{typenum::U32, GenericArray};
|
||||
use crate::{
|
||||
errors::InternalPakeError, group::Group, hash::Hash, map_to_curve::GroupWithMapToCurve,
|
||||
};
|
||||
use digest::Digest;
|
||||
use generic_array::GenericArray;
|
||||
use hkdf::Hkdf;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
@@ -42,14 +45,14 @@ pub(crate) fn generate_oprf2<G: Group>(
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of DH-OPRF, in which
|
||||
/// the client unblinds the server's message.
|
||||
pub(crate) fn generate_oprf3<G: Group>(
|
||||
pub(crate) fn generate_oprf3<G: Group, H: Hash>(
|
||||
input: &[u8],
|
||||
point: G,
|
||||
blinding_factor: &G::Scalar,
|
||||
) -> Result<GenericArray<u8, U32>, InternalPakeError> {
|
||||
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, InternalPakeError> {
|
||||
let unblinded = point * &G::scalar_invert(&blinding_factor);
|
||||
let ikm: Vec<u8> = [&unblinded.to_arr()[..], input].concat();
|
||||
let (prk, _) = Hkdf::<sha2::Sha256>::extract(None, &ikm);
|
||||
let (prk, _) = Hkdf::<H>::extract(None, &ikm);
|
||||
Ok(prk)
|
||||
}
|
||||
|
||||
@@ -94,7 +97,7 @@ mod tests {
|
||||
use generic_array::{arr, GenericArray};
|
||||
use hkdf::Hkdf;
|
||||
use rand_core::OsRng;
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
fn prf(
|
||||
input: &[u8],
|
||||
@@ -125,7 +128,7 @@ mod tests {
|
||||
];
|
||||
let salt = RistrettoPoint::from_scalar_slice(&salt_bytes)?;
|
||||
let beta = generate_oprf2::<RistrettoPoint>(alpha, &salt)?;
|
||||
let res = generate_oprf3::<RistrettoPoint>(input, beta, &blinding_factor)?;
|
||||
let res = generate_oprf3::<RistrettoPoint, sha2::Sha256>(input, beta, &blinding_factor)?;
|
||||
let res2 = prf(&input[..], &salt.as_bytes());
|
||||
assert_eq!(res, res2);
|
||||
Ok(())
|
||||
@@ -140,15 +143,12 @@ mod tests {
|
||||
alpha,
|
||||
blinding_factor,
|
||||
} = generate_oprf1::<_, RistrettoPoint>(&input, None, &mut rng).unwrap();
|
||||
let res = generate_oprf3::<RistrettoPoint>(&input, alpha, &blinding_factor).unwrap();
|
||||
let res = generate_oprf3::<RistrettoPoint, sha2::Sha256>(&input, alpha, &blinding_factor)
|
||||
.unwrap();
|
||||
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(None, &input);
|
||||
|
||||
// This is because RistrettoPoint is on an obsolete sha2 version
|
||||
let mut bits = [0u8; 64];
|
||||
let mut hasher = sha2::Sha512::new();
|
||||
Digest::update(&mut hasher, &hashed_input[..]);
|
||||
bits.copy_from_slice(&hasher.finalize());
|
||||
bits.copy_from_slice(&hashed_input);
|
||||
|
||||
let point = RistrettoPoint::from_uniform_bytes(&bits);
|
||||
let mut ikm: Vec<u8> = Vec::new();
|
||||
|
||||
+16
-9
@@ -6,29 +6,36 @@
|
||||
//! Trait specifying a slow hashing function
|
||||
|
||||
use crate::errors::InternalPakeError;
|
||||
|
||||
use generic_array::{typenum::U32, GenericArray};
|
||||
use crate::hash::Hash;
|
||||
use digest::Digest;
|
||||
use generic_array::GenericArray;
|
||||
|
||||
/// Used for the slow hashing function in OPAQUE
|
||||
pub trait SlowHash {
|
||||
pub trait SlowHash<D: Hash> {
|
||||
/// Computes the slow hashing function
|
||||
fn hash(input: GenericArray<u8, U32>) -> Result<Vec<u8>, InternalPakeError>;
|
||||
fn hash(
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalPakeError>;
|
||||
}
|
||||
|
||||
/// A no-op hash which simply returns its input
|
||||
pub struct NoOpHash;
|
||||
|
||||
impl SlowHash for NoOpHash {
|
||||
fn hash(input: GenericArray<u8, U32>) -> Result<Vec<u8>, InternalPakeError> {
|
||||
impl<D: Hash> SlowHash<D> for NoOpHash {
|
||||
fn hash(
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalPakeError> {
|
||||
Ok(input.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "slow-hash")]
|
||||
impl SlowHash for scrypt::ScryptParams {
|
||||
fn hash(input: GenericArray<u8, U32>) -> Result<Vec<u8>, InternalPakeError> {
|
||||
impl<D: Hash> SlowHash<D> for scrypt::ScryptParams {
|
||||
fn hash(
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalPakeError> {
|
||||
let params = scrypt::ScryptParams::new(15, 8, 1).unwrap();
|
||||
let mut output = [0u8; 32];
|
||||
let mut output = [0u8; <D as Digest>::OutputSize::to_usize()];
|
||||
scrypt::scrypt(&input, &[], ¶ms, &mut output)
|
||||
.map_err(|_| InternalPakeError::SlowHashError)?;
|
||||
Ok(output.to_vec())
|
||||
|
||||
+14
-12
@@ -26,6 +26,7 @@ impl CipherSuite for X255193dhNoSlowHash {
|
||||
type Group = EdwardsPoint;
|
||||
type KeyFormat = X25519KeyPair;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = NoOpHash;
|
||||
}
|
||||
|
||||
@@ -470,18 +471,19 @@ fn test_l3() -> Result<(), PakeError> {
|
||||
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 (l3, shared_secret, opaque_key_login) =
|
||||
ClientLogin::<X255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
.unwrap()
|
||||
.finish(
|
||||
LoginSecondMessage::<EdwardsPoint, X25519KeyPair, TripleDH>::try_from(
|
||||
¶meters.l2[..],
|
||||
)
|
||||
.unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||
&mut client_e_sk_rng,
|
||||
)
|
||||
.unwrap();
|
||||
let (l3, shared_secret, opaque_key_login) = ClientLogin::<X255193dhNoSlowHash>::try_from(
|
||||
¶meters.client_login_state[..],
|
||||
)
|
||||
.unwrap()
|
||||
.finish(
|
||||
LoginSecondMessage::<EdwardsPoint, X25519KeyPair, TripleDH, sha2::Sha256>::try_from(
|
||||
¶meters.l2[..],
|
||||
)
|
||||
.unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||
&mut client_e_sk_rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.shared_secret),
|
||||
|
||||
@@ -27,6 +27,7 @@ impl CipherSuite for Default {
|
||||
type Group = RistrettoPoint;
|
||||
type KeyFormat = crate::keypair::X25519KeyPair;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ fn server_registration_roundtrip() {
|
||||
// the whole kit
|
||||
let key_len =
|
||||
<<<Default as CipherSuite>::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let envelope_size = key_len + Envelope::<sha2::Sha256>::additional_size();
|
||||
let mut mock_envelope_bytes = vec![0u8; envelope_size];
|
||||
rng.fill_bytes(&mut mock_envelope_bytes);
|
||||
println!("{}", mock_envelope_bytes.len());
|
||||
@@ -118,10 +119,11 @@ fn register_third_message_roundtrip() {
|
||||
let mut msg = [0u8; 32];
|
||||
rng.fill_bytes(&mut msg);
|
||||
|
||||
let (ciphertext, _) = Envelope::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap();
|
||||
let (ciphertext, _) =
|
||||
Envelope::<sha2::Sha256>::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap();
|
||||
|
||||
let message: Vec<u8> = [&ciphertext.to_bytes(), &pubkey_bytes[..]].concat();
|
||||
let r3 = RegisterThirdMessage::<X25519KeyPair>::try_from(&message[..]).unwrap();
|
||||
let r3 = RegisterThirdMessage::<X25519KeyPair, sha2::Sha256>::try_from(&message[..]).unwrap();
|
||||
let r3_bytes = r3.to_bytes();
|
||||
assert_eq!(message, r3_bytes);
|
||||
}
|
||||
@@ -164,7 +166,8 @@ fn login_first_message_roundtrip() {
|
||||
rng.fill_bytes(&mut client_nonce);
|
||||
|
||||
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
|
||||
let reg = <TripleDH as KeyExchange>::KE1Message::try_from(ke1m[..].to_vec()).unwrap();
|
||||
let reg =
|
||||
<TripleDH as KeyExchange<sha2::Sha256>>::KE1Message::try_from(ke1m[..].to_vec()).unwrap();
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, ke1m);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user