Adding support for "internal mode" and fake credential response + test vectors (#155)

* Adding support for internal and external mode
This commit is contained in:
Kevin Lewi
2021-06-21 01:29:39 -07:00
committed by Kevin Lewi
parent f0c13945d1
commit 1572ff0104
12 changed files with 907 additions and 927 deletions
+4 -4
View File
@@ -21,7 +21,6 @@ pub trait KeyExchange<D: Hash, G: Group> {
type KE3Message: FromBytes + ToBytes + Clone;
fn generate_ke1<R: RngCore + CryptoRng>(
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
@@ -35,8 +34,8 @@ pub trait KeyExchange<D: Hash, G: Group> {
server_s_sk: PrivateKey,
id_u: Vec<u8>,
id_s: Vec<u8>,
e_info: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError>;
context: Vec<u8>,
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>;
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn generate_ke3(
@@ -48,7 +47,8 @@ pub trait KeyExchange<D: Hash, G: Group> {
client_s_sk: PrivateKey,
id_u: Vec<u8>,
id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError>;
context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError>;
#[allow(clippy::type_complexity)]
fn finish_ke(
+33 -90
View File
@@ -14,7 +14,7 @@ use crate::{
hash::Hash,
key_exchange::traits::{FromBytes, KeyExchange, ToBytes, ToBytesWithPointers},
keypair::{KeyPair, PrivateKey, PublicKey, SizedBytesExt},
serialization::{serialize, tokenize},
serialization::serialize,
};
use digest::{Digest, FixedOutput};
use generic_array::{
@@ -30,14 +30,12 @@ use zeroize::Zeroize;
const KEY_LEN: usize = 32;
pub(crate) type NonceLen = U32;
static STR_3DH: &[u8] = b"3DH";
static STR_CLIENT_MAC: &[u8] = b"client mac";
static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret";
static STR_SERVER_MAC: &[u8] = b"server mac";
static STR_HANDSHAKE_ENC: &[u8] = b"handshake enc";
static STR_ENCRYPTION_PAD: &[u8] = b"encryption pad";
static STR_SESSION_SECRET: &[u8] = b"session secret";
static STR_OPAQUE: &[u8] = b"OPAQUE ";
static STR_RFC: &[u8] = b"RFCXXXX";
static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
static STR_SERVER_MAC: &[u8] = b"ServerMAC";
static STR_SESSION_KEY: &[u8] = b"SessionKey";
static STR_OPAQUE: &[u8] = b"OPAQUE-";
#[allow(clippy::upper_case_acronyms)]
/// The Triple Diffie-Hellman key exchange implementation
@@ -51,7 +49,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>(
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng);
@@ -59,7 +56,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
let ke1_message = Ke1Message {
client_nonce,
info,
client_e_pk: client_e_kp.public().clone(),
};
@@ -82,21 +78,22 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
server_s_sk: PrivateKey,
id_u: Vec<u8>,
id_s: Vec<u8>,
e_info: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> {
context: Vec<u8>,
) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce = generate_nonce::<R>(rng);
let mut transcript_hasher = D::new()
.chain(STR_3DH)
.chain(&serialize(&id_u, 2))
.chain(STR_RFC)
.chain(&serialize(&context, 2))
.chain(&id_u)
.chain(&serialized_credential_request[..])
.chain(&serialize(&id_s, 2))
.chain(&id_s)
.chain(&l2_bytes[..])
.chain(&server_nonce[..])
.chain(&server_e_kp.public().to_arr());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
let (session_key, km2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
pk1: ke1_message.client_e_pk.clone(),
sk1: server_e_kp.private().clone(),
@@ -108,19 +105,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
&transcript_hasher.clone().finalize(),
)?;
// Compute encryption of e_info
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
let mut encryption_pad = vec![0u8; e_info.len()];
h.expand(STR_ENCRYPTION_PAD, &mut encryption_pad)
.map_err(|_| InternalPakeError::HkdfError)?;
let ciphertext: Vec<u8> = encryption_pad
.iter()
.zip(e_info.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
transcript_hasher.update(&serialize(&ciphertext, 2));
let mut mac_hasher =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
mac_hasher.update(&transcript_hasher.clone().finalize());
@@ -129,7 +113,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
transcript_hasher.update(&mac);
Ok((
ke1_message.info,
Ke2State {
km3,
hashed_transcript: transcript_hasher.finalize(),
@@ -138,7 +121,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Ke2Message {
server_nonce,
server_e_pk: server_e_kp.public().clone(),
e_info: ciphertext,
mac,
},
))
@@ -154,16 +136,18 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
client_s_sk: PrivateKey,
id_u: Vec<u8>,
id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> {
context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError> {
let mut transcript_hasher = D::new()
.chain(STR_3DH)
.chain(&serialize(&id_u, 2))
.chain(STR_RFC)
.chain(&serialize(&context, 2))
.chain(&id_u)
.chain(&serialized_credential_request)
.chain(&serialize(&id_s, 2))
.chain(&id_s)
.chain(&l2_component[..])
.chain(&ke2_message.to_bytes_without_info_or_mac());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
let (session_key, km2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(),
sk1: ke1_state.client_e_sk.clone(),
@@ -175,8 +159,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
&transcript_hasher.clone().finalize(),
)?;
transcript_hasher.update(&serialize(&ke2_message.e_info[..], 2));
let mut server_mac =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
server_mac.update(&transcript_hasher.clone().finalize());
@@ -193,19 +175,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
client_mac.update(&transcript_hasher.finalize());
// Compute decryption of e_info
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
let mut encryption_pad = vec![0u8; ke2_message.e_info.len()];
h.expand(STR_ENCRYPTION_PAD, &mut encryption_pad)
.map_err(|_| InternalPakeError::HkdfError)?;
let plaintext: Vec<u8> = encryption_pad
.iter()
.zip(ke2_message.e_info.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
Ok((
plaintext,
session_key.to_vec(),
Ke3Message {
mac: client_mac.finalize().into_bytes(),
@@ -248,7 +218,6 @@ pub struct Ke1State {
#[derive(PartialEq, Eq, Clone)]
pub struct Ke1Message {
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
pub(crate) info: Vec<u8>,
pub(crate) client_e_pk: PublicKey,
}
@@ -286,12 +255,7 @@ impl ToBytesWithPointers for Ke1State {
impl ToBytes for Ke1Message {
fn to_bytes(&self) -> Vec<u8> {
[
&self.client_nonce[..],
&serialize(&self.info, 2),
&self.client_e_pk.to_arr(),
]
.concat()
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
}
}
@@ -299,20 +263,11 @@ impl FromBytes for Ke1Message {
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, PakeError> {
let nonce_len = NonceLen::to_usize();
let checked_nonce =
check_slice_size_atleast(ke1_message_bytes, nonce_len, "ke1_message nonce")?;
let (info, remainder) = tokenize(&checked_nonce[nonce_len..], 2)?;
// Check the public key bytes
let unchecked_client_e_pk =
check_slice_size(&remainder, KEY_LEN, "ke1_message client_e_pk")?;
let client_e_pk =
KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(unchecked_client_e_pk)?)?;
check_slice_size(ke1_message_bytes, nonce_len + KEY_LEN, "ke1_message nonce")?;
Ok(Self {
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
info,
client_e_pk,
client_e_pk: PublicKey::from_bytes(&checked_nonce[nonce_len..])?,
})
}
}
@@ -364,7 +319,6 @@ impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
pub struct Ke2Message<HashLen: ArrayLength<u8>> {
server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: PublicKey,
e_info: Vec<u8>,
mac: GenericArray<u8, HashLen>,
}
@@ -385,12 +339,7 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
impl<HashLen: ArrayLength<u8>> ToBytes for Ke2Message<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[
&self.to_bytes_without_info_or_mac(),
&serialize(&self.e_info, 2),
&self.mac[..],
]
.concat()
[&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat()
}
}
@@ -410,8 +359,11 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2Message<HashLen> {
KEY_LEN,
"ke2_message server_e_pk",
)?;
let (e_info, remainder) = tokenize(&unchecked_server_e_pk[KEY_LEN..], 2)?;
let checked_mac = check_slice_size(&remainder, HashLen::to_usize(), "ke1_message mac")?;
let checked_mac = check_slice_size(
&unchecked_server_e_pk[KEY_LEN..],
HashLen::to_usize(),
"ke1_message mac",
)?;
// Check the public key bytes
let server_e_pk = KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
@@ -420,8 +372,7 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2Message<HashLen> {
Ok(Self {
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
server_e_pk,
e_info,
server_e_pk: PublicKey::from_bytes(&server_e_pk)?,
mac: GenericArray::clone_from_slice(checked_mac),
})
}
@@ -439,12 +390,11 @@ struct TripleDHComponents {
}
#[allow(clippy::upper_case_acronyms)]
// Consists of a session key, followed by two mac keys and an encryption key: (session_key, km2, ke2, km3)
// Consists of a session key, followed by two mac keys: (session_key, km2, km3)
type TripleDHDerivationResult<D> = (
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
);
/// The third key exchange message
@@ -492,7 +442,7 @@ fn derive_3dh_keys<D: Hash, G: Group>(
)?;
let session_key = derive_secrets::<D>(
&extracted_ikm,
STR_SESSION_SECRET,
STR_SESSION_KEY,
hashed_derivation_transcript,
)?;
@@ -502,12 +452,6 @@ fn derive_3dh_keys<D: Hash, G: Group>(
b"",
<D as Digest>::OutputSize::to_usize(),
)?;
let ke2 = hkdf_expand_label::<D>(
&handshake_secret,
STR_HANDSHAKE_ENC,
b"",
<D as Digest>::OutputSize::to_usize(),
)?;
let km3 = hkdf_expand_label::<D>(
&handshake_secret,
STR_CLIENT_MAC,
@@ -518,7 +462,6 @@ fn derive_3dh_keys<D: Hash, G: Group>(
Ok((
GenericArray::clone_from_slice(&session_key),
GenericArray::clone_from_slice(&km2),
GenericArray::clone_from_slice(&ke2),
GenericArray::clone_from_slice(&km3),
))
}