Skip to content

Commit

Permalink
Addressing various TODOs and minor cleanups (facebook#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinlewi committed Feb 2, 2021
1 parent be6d042 commit 9d3963f
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 135 deletions.
46 changes: 0 additions & 46 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,22 @@ readme = "README.md"
default = ["u64_backend"]
slow-hash = ["scrypt"]
bench = []
u64_backend = ["curve25519-dalek/u64_backend", "x25519-dalek/u64_backend"]
u32_backend = ["curve25519-dalek/u32_backend", "x25519-dalek/u32_backend"]
u64_backend = ["curve25519-dalek/u64_backend"]
u32_backend = ["curve25519-dalek/u32_backend"]

[dependencies]
curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] }
digest = "0.9.0"
displaydoc = "0.1.7"
fiat-crypto = { version = "0.1.5"}
generic-array = "0.14.4"
generic-bytes = { version = "0.1.0" }
generic-bytes-derive = { version = "0.1.0" }
hkdf = "0.10.0"
hmac = "0.10.1"
rand_core = "0.5.1"
scrypt = { version = "0.5.0", optional = true }
sha2 = "0.9.2"
subtle = { version = "2.3.0", default-features = false }
thiserror = "1.0.22"
x25519-dalek = { version = "1.1.0", default-features = false, features = ["std"] }
zeroize = "1.1.1"

[dev-dependencies]
Expand All @@ -42,6 +39,7 @@ criterion = "0.3.3"
hex = "0.4.2"
lazy_static = "1.4.0"
serde_json = "1.0.60"
sha2 = "0.9.2"
proptest = "0.10.1"
rand = "0.7"
rustyline = "7.0.0"
Expand Down
28 changes: 23 additions & 5 deletions examples/digital_locker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ use std::process::exit;

use opaque_ke::{
ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
ClientRegistration, ClientRegistrationFinishParameters, CredentialRequest, CredentialResponse,
RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginStartParameters, ServerRegistration,
ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
};

// The ciphersuite trait allows to specify the underlying primitives
Expand Down Expand Up @@ -173,9 +173,27 @@ fn open_locker(
return Err(String::from("Incorrect password, please try again."));
}
let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();

// Decrypt contents of locker
let plaintext = decrypt(&client_login_finish_result.export_key, &locker.contents);
// Client sends credential_finalization_bytes to server

let server_login_finish_result = server_login_start_result
.state
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes[..]).unwrap())
.unwrap();

// Server sends locker contents, encrypted under the session key, to the client
let encrypted_locker_contents =
encrypt(&server_login_finish_result.shared_secret, &locker.contents);

// Client decrypts contents of locker, first under the shared secret, and then under the export key
let plaintext = decrypt(
&client_login_finish_result.export_key,
&decrypt(
&client_login_finish_result.shared_secret,
&encrypted_locker_contents,
),
);
String::from_utf8(plaintext).map_err(|_| String::from("UTF8 error"))
}

Expand Down
3 changes: 3 additions & 0 deletions src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ pub(crate) struct Envelope<D: Hash> {
hmac: GenericArray<u8, <D as Digest>::OutputSize>,
}

// Note that this struct represents an envelope that has been "opened" with the asssociated
// key. This key is also used to derive the export_key parameter, which is technically
// unrelated to the envelope's encrypted and authenticated contents.
pub(crate) struct OpenedEnvelope<D: Hash> {
pub(crate) client_s_sk: Vec<u8>,
pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>,
Expand Down
2 changes: 1 addition & 1 deletion src/key_exchange/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub trait KeyExchange<D: Hash, G: Group> {
type KE3Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;

fn generate_ke1<R: RngCore + CryptoRng>(
l1_component: Vec<u8>,
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
Expand All @@ -44,6 +43,7 @@ pub trait KeyExchange<D: Hash, G: Group> {
l2_component: Vec<u8>,
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State,
serialized_credential_request: &[u8],
server_s_pk: Key,
client_s_sk: Key,
id_u: Vec<u8>,
Expand Down
47 changes: 19 additions & 28 deletions src/key_exchange/tripledh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use rand_core::{CryptoRng, RngCore};
use std::convert::TryFrom;

const KEY_LEN: usize = 32;
pub(crate) const NONCE_LEN: usize = 32;
pub(crate) type NonceLen = U32;

static STR_3DH: &[u8] = b"3DH keys";
Expand All @@ -51,15 +50,14 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;

fn generate_ke1<R: RngCore + CryptoRng>(
alpha_bytes: Vec<u8>,
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng);
let client_nonce: GenericArray<u8, NonceLen> = {
let mut client_nonce_bytes = [0u8; NONCE_LEN];
let mut client_nonce_bytes = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce_bytes);
client_nonce_bytes.into()
GenericArray::clone_from_slice(&client_nonce_bytes)
};

let ke1_message = KE1Message {
Expand All @@ -68,14 +66,10 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
client_e_pk: client_e_kp.public().clone(),
};

// TODO: must match the serialization of a credential request, could be done more cleanly
let serialized_credential_request = [alpha_bytes, ke1_message.to_bytes()].concat();

Ok((
KE1State {
client_e_sk: client_e_kp.private().clone(),
client_nonce,
serialized_credential_request,
},
ke1_message,
))
Expand All @@ -95,9 +89,9 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce: GenericArray<u8, NonceLen> = {
let mut server_nonce_bytes = [0u8; NONCE_LEN];
let mut server_nonce_bytes = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce_bytes);
server_nonce_bytes.into()
GenericArray::clone_from_slice(&server_nonce_bytes)
};

let (session_secret, km2, ke2, km3) = derive_3dh_keys::<D, G>(
Expand Down Expand Up @@ -169,6 +163,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
l2_component: Vec<u8>,
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State,
serialized_credential_request: &[u8],
server_s_pk: Key,
client_s_sk: Key,
id_u: Vec<u8>,
Expand All @@ -190,7 +185,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
)?;

let transcript: Vec<u8> = [
&ke1_state.serialized_credential_request[..],
&serialized_credential_request,
&l2_component[..],
&ke2_message.to_bytes_without_mac(),
]
Expand Down Expand Up @@ -258,7 +253,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
}

fn ke2_message_size() -> usize {
NONCE_LEN + KEY_LEN + <<D as FixedOutput>::OutputSize as Unsigned>::to_usize()
NonceLen::to_usize() + KEY_LEN + <<D as FixedOutput>::OutputSize as Unsigned>::to_usize()
}
}

Expand All @@ -267,7 +262,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
pub struct KE1State {
client_e_sk: Key,
client_nonce: GenericArray<u8, NonceLen>,
serialized_credential_request: Vec<u8>,
}

/// The first key exchange message
Expand All @@ -282,26 +276,21 @@ impl TryFrom<&[u8]> for KE1State {
type Error = PakeError;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let checked_bytes = check_slice_size_atleast(bytes, KEY_LEN + NONCE_LEN, "ke1_state")?;
let nonce_len = NonceLen::to_usize();
let checked_bytes = check_slice_size_atleast(bytes, KEY_LEN + nonce_len, "ke1_state")?;

Ok(Self {
client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?,
client_nonce: GenericArray::clone_from_slice(
&checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN],
&checked_bytes[KEY_LEN..KEY_LEN + nonce_len],
),
serialized_credential_request: checked_bytes[KEY_LEN + NONCE_LEN..].to_vec(),
})
}
}

impl ToBytes for KE1State {
fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [
&self.client_e_sk.to_arr(),
&self.client_nonce[..],
&self.serialized_credential_request[..],
]
.concat();
let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
output
}
}
Expand All @@ -321,15 +310,16 @@ impl TryFrom<&[u8]> for KE1Message {
type Error = PakeError;

fn try_from(ke1_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let nonce_len = NonceLen::to_usize();
let checked_nonce =
check_slice_size_atleast(ke1_message_bytes, NONCE_LEN, "ke1_message nonce")?;
check_slice_size_atleast(ke1_message_bytes, nonce_len, "ke1_message nonce")?;

let (info, remainder) = tokenize(&checked_nonce[NONCE_LEN..], 2)?;
let (info, remainder) = tokenize(&checked_nonce[nonce_len..], 2)?;

let checked_client_e_pk = check_slice_size(&remainder, KEY_LEN, "ke1_message client_e_pk")?;

Ok(Self {
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..NONCE_LEN]),
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
info,
client_e_pk: Key::from_bytes(&checked_client_e_pk)?,
})
Expand Down Expand Up @@ -401,17 +391,18 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
type Error = PakeError;

fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
let checked_nonce = check_slice_size_atleast(input, NONCE_LEN, "ke2_message nonce")?;
let nonce_len = NonceLen::to_usize();
let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?;
let checked_server_e_pk = check_slice_size_atleast(
&checked_nonce[NONCE_LEN..],
&checked_nonce[nonce_len..],
KEY_LEN,
"ke2_message server_e_pk",
)?;
let (e_info, remainder) = tokenize(&checked_server_e_pk[KEY_LEN..], 2)?;
let checked_mac = check_slice_size(&remainder, HashLen::to_usize(), "ke1_message mac")?;

Ok(Self {
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..NONCE_LEN]),
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
server_e_pk: Key::from_bytes(&checked_server_e_pk[..KEY_LEN])?,
e_info,
mac: GenericArray::clone_from_slice(&checked_mac),
Expand Down
1 change: 0 additions & 1 deletion src/map_to_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ impl GroupWithMapToCurve for RistrettoPoint {
// Implements the hash_to_ristretto255() function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError> {
// FIXME use generic_array and turn this into a compile-time error if size mismatch
let uniform_bytes =
expand_message_xmd::<H>(msg, dst, <H as Digest>::OutputSize::to_usize())?;
Ok(<Self as Group>::hash_to_curve(
Expand Down
24 changes: 18 additions & 6 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,24 @@ pub struct CredentialResponse<CS: CipherSuite> {
impl<CS: CipherSuite> CredentialResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut credential_response: Vec<u8> = Vec::new();
credential_response.extend_from_slice(&self.beta.to_arr());
credential_response.extend_from_slice(&serialize(&self.server_s_pk.to_arr().to_vec(), 2));
credential_response.extend_from_slice(&self.envelope.to_bytes());
credential_response.extend_from_slice(&self.ke2_message.to_bytes());
credential_response
[
Self::serialize_without_ke(&self.beta, &self.server_s_pk, &self.envelope),
self.ke2_message.to_bytes(),
]
.concat()
}

pub(crate) fn serialize_without_ke(
beta: &CS::Group,
server_s_pk: &Key,
envelope: &Envelope<CS::Hash>,
) -> Vec<u8> {
[
&beta.to_arr(),
&serialize(&server_s_pk.to_arr().to_vec(), 2)[..],
&envelope.to_bytes(),
]
.concat()
}

/// Deserialization from bytes
Expand Down

0 comments on commit 9d3963f

Please sign in to comment.