From 9c06c98ad665558a437a378a950101b5a46cf717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 14 Aug 2020 16:20:42 -0400 Subject: [PATCH] Introduce enum structs to replicate TLS message format This PR favors native coercions of enums to numerical types to help ser/de operations. Small rearrangement of the serialization module. --- benches/oprf.rs | 3 +- src/elligator/field.rs | 13 +- src/envelope.rs | 8 +- src/group.rs | 3 +- src/lib.rs | 3 +- src/opaque.rs | 28 ++- src/serialization.rs | 352 ------------------------------------- src/serialization/mod.rs | 92 ++++++++++ src/serialization/tests.rs | 314 +++++++++++++++++++++++++++++++++ src/slow_hash.rs | 3 +- 10 files changed, 432 insertions(+), 387 deletions(-) delete mode 100644 src/serialization.rs create mode 100644 src/serialization/mod.rs create mode 100644 src/serialization/tests.rs diff --git a/benches/oprf.rs b/benches/oprf.rs index 13aa16f..ab8c776 100644 --- a/benches/oprf.rs +++ b/benches/oprf.rs @@ -7,8 +7,7 @@ extern crate criterion; use criterion::Criterion; -use curve25519_dalek::edwards::EdwardsPoint; -use curve25519_dalek::ristretto::RistrettoPoint; +use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint}; use generic_array::arr; use opaque_ke::{ group::Group, diff --git a/src/elligator/field.rs b/src/elligator/field.rs index 1c070e7..511d5be 100644 --- a/src/elligator/field.rs +++ b/src/elligator/field.rs @@ -7,15 +7,12 @@ //! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(64\\)-bit //! limbs with \\(128\\)-bit products. -use core::fmt::Debug; -use core::ops::Neg; -use core::ops::{Add, AddAssign}; -use core::ops::{Mul, MulAssign}; +use core::{ + fmt::Debug, + ops::{Add, AddAssign, Mul, MulAssign, Neg}, +}; -use subtle::Choice; -use subtle::ConditionallyNegatable; -use subtle::ConditionallySelectable; -use subtle::ConstantTimeEq; +use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq}; use zeroize::Zeroize; diff --git a/src/envelope.rs b/src/envelope.rs index 0b079af..a6beec7 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -3,9 +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, ProtocolError}; -use crate::hash::Hash; -use crate::serialization::{serialize, tokenize}; +use crate::{ + errors::{InternalPakeError, ProtocolError}, + hash::Hash, + serialization::{serialize, tokenize}, +}; use digest::Digest; use generic_array::{ typenum::{Unsigned, U32}, diff --git a/src/group.rs b/src/group.rs index 7a4ed55..de3e4d7 100644 --- a/src/group.rs +++ b/src/group.rs @@ -6,8 +6,7 @@ //! Defines the Group trait to specify the underlying prime order group used in //! OPAQUE's OPRF -use crate::elligator; -use crate::errors::InternalPakeError; +use crate::{elligator, errors::InternalPakeError}; use curve25519_dalek::{ edwards::{CompressedEdwardsY, EdwardsPoint}, diff --git a/src/lib.rs b/src/lib.rs index d0de782..d8e2bb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,7 +416,6 @@ pub mod opaque; pub mod ciphersuite; mod envelope; pub mod hash; -mod serialization; mod elligator; pub mod group; @@ -433,5 +432,7 @@ mod oprf; pub mod slow_hash; +mod serialization; + #[cfg(test)] mod tests; diff --git a/src/opaque.rs b/src/opaque.rs index 4eedb5d..c9c6b41 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -15,7 +15,7 @@ use crate::{ keypair::{KeyPair, SizedBytes}, oprf, oprf::OprfClientBytes, - serialization::{serialize, tokenize}, + serialization::{serialize, tokenize, ProtocolMessageType}, slow_hash::SlowHash, }; use generic_array::{typenum::Unsigned, GenericArray}; @@ -23,12 +23,6 @@ use rand_core::{CryptoRng, RngCore}; use std::{convert::TryFrom, marker::PhantomData}; use zeroize::Zeroize; -const REGISTRATION_REQUEST: u8 = 0x01; -const REGISTRATION_RESPONSE: u8 = 0x02; -const REGISTRATION_UPLOAD: u8 = 0x03; -const CREDENTIAL_REQUEST: u8 = 0x04; -const CREDENTIAL_RESPONSE: u8 = 0x05; - const CREDENTIAL_TYPE_SKU: u8 = 0x01; const CREDENTIAL_TYPE_PKS: u8 = 0x03; @@ -72,14 +66,14 @@ impl RegisterFirstMessage { registration_request.extend_from_slice(&serialize((&self.to_bytes()).to_vec(), 2)); let mut output: Vec = Vec::new(); - output.push(REGISTRATION_REQUEST); + output.push(ProtocolMessageType::from(self) as u8 + 1); output.extend_from_slice(&serialize(registration_request, 3)); output } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - if input[0] != REGISTRATION_REQUEST { + if input[0] != ProtocolMessageType::RegistrationRequest as u8 + 1 { return Err(PakeError::SerializationError.into()); } @@ -146,14 +140,14 @@ where registration_response.extend_from_slice(&serialize(vec![CREDENTIAL_TYPE_PKS], 1)); let mut output: Vec = Vec::new(); - output.push(REGISTRATION_RESPONSE); + output.push(ProtocolMessageType::from(self) as u8 + 1); output.extend_from_slice(&serialize(registration_response, 3)); output } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - if input[0] != REGISTRATION_RESPONSE { + if input[0] != ProtocolMessageType::RegistrationResponse as u8 + 1 { return Err(PakeError::SerializationError.into()); } @@ -225,14 +219,14 @@ where registration_upload.extend_from_slice(&serialize(self.client_s_pk.to_arr().to_vec(), 2)); let mut output: Vec = Vec::new(); - output.push(REGISTRATION_UPLOAD); + output.push(ProtocolMessageType::from(self) as u8 + 1); output.extend_from_slice(&serialize(registration_upload, 3)); output } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - if input[0] != REGISTRATION_UPLOAD { + if input[0] != ProtocolMessageType::RegistrationUpload as u8 + 1 { return Err(PakeError::SerializationError.into()); } @@ -302,7 +296,7 @@ impl LoginFirstMessage { credential_request.extend_from_slice(&serialize((&self.alpha.to_arr()).to_vec(), 2)); let mut output: Vec = Vec::new(); - output.push(CREDENTIAL_REQUEST); + output.push(ProtocolMessageType::from(self) as u8 + 1); output.extend_from_slice(&serialize(credential_request, 3)); output.extend_from_slice(&self.ke1_message.to_bytes()); output @@ -310,7 +304,7 @@ impl LoginFirstMessage { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - if input[0] != CREDENTIAL_REQUEST { + if input[0] != ProtocolMessageType::CredentialRequest as u8 + 1 { return Err(PakeError::SerializationError.into()); } @@ -347,7 +341,7 @@ impl LoginSecondMessage { credential_response.extend_from_slice(&serialize(Vec::new(), 2)); let mut output: Vec = Vec::new(); - output.push(CREDENTIAL_RESPONSE); + output.push(ProtocolMessageType::from(self) as u8 + 1); output.extend_from_slice(&serialize(credential_response, 3)); output.extend_from_slice(&self.ke2_message.to_bytes()); output @@ -355,7 +349,7 @@ impl LoginSecondMessage { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - if input[0] != CREDENTIAL_RESPONSE { + if input[0] != ProtocolMessageType::CredentialResponse as u8 + 1 { return Err(PakeError::SerializationError.into()); } diff --git a/src/serialization.rs b/src/serialization.rs deleted file mode 100644 index dbef5f2..0000000 --- a/src/serialization.rs +++ /dev/null @@ -1,352 +0,0 @@ -// 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 crate::errors::PakeError; - -pub(crate) fn serialize(input: Vec, max_bytes: usize) -> Vec { - let mut output: Vec = Vec::new(); - output.extend_from_slice(&input.len().to_be_bytes()[8 - max_bytes..]); - output.extend_from_slice(&input[..]); - output -} - -pub(crate) fn tokenize(input: Vec, size_bytes: usize) -> Result<(Vec, Vec), PakeError> { - if size_bytes > 8 { - return Err(PakeError::SerializationError); - } - - let mut size_array = [0u8; 8]; - for i in 0..size_bytes { - size_array[8 - size_bytes + i] = input[i]; - } - let size = usize::from_be_bytes(size_array); - - if size_bytes + size > input.len() { - return Err(PakeError::SerializationError); - } - - Ok(( - input[size_bytes..size_bytes + size].to_vec(), - input[size_bytes + size..].to_vec(), - )) -} - -#[cfg(test)] -mod tests { - use crate::{ - ciphersuite::CipherSuite, - envelope::Envelope, - group::Group, - key_exchange::{ - traits::{KeyExchange, ToBytes}, - tripledh::{TripleDH, NONCE_LEN}, - }, - keypair::{KeyPair, SizedBytes, X25519KeyPair}, - opaque::*, - }; - - use curve25519_dalek::ristretto::RistrettoPoint; - use generic_array::typenum::Unsigned; - use proptest::{collection::vec, prelude::*}; - use rand_core::{OsRng, RngCore}; - - use sha2::{Digest, Sha256}; - use std::convert::TryFrom; - - struct Default; - 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; - } - - fn random_ristretto_point() -> RistrettoPoint { - let mut rng = OsRng; - let mut random_bits = [0u8; 64]; - rng.fill_bytes(&mut random_bits); - - // This is because RistrettoPoint is on an obsolete sha2 version - let mut bits = [0u8; 64]; - let mut hasher = sha2::Sha512::new(); - hasher.update(&random_bits[..]); - bits.copy_from_slice(&hasher.finalize()); - - RistrettoPoint::from_uniform_bytes(&bits) - } - - #[test] - fn client_registration_roundtrip() { - let pw = b"hunter2"; - let mut rng = OsRng; - let sc = ::random_scalar(&mut rng); - // serialization order: scalar, password - let bytes: Vec = [&sc.as_bytes()[..], &pw[..]].concat(); - let reg = ClientRegistration::::try_from(&bytes[..]).unwrap(); - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, bytes); - } - - #[test] - fn server_registration_roundtrip() { - // If we don't have envelope and client_pk, the server registration just - // contains the prf key - let mut rng = OsRng; - let sc = ::random_scalar(&mut rng); - let mut oprf_bytes: Vec = vec![]; - oprf_bytes.extend_from_slice(sc.as_bytes()); - let reg = ServerRegistration::::try_from(&oprf_bytes[..]).unwrap(); - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, oprf_bytes); - // If we do have envelope and client pk, the server registration contains - // the whole kit - let key_len = - <<::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize(); - let envelope_size = key_len + Envelope::::additional_size(); - let mut mock_envelope_bytes = vec![0u8; envelope_size]; - rng.fill_bytes(&mut mock_envelope_bytes); - println!("{}", mock_envelope_bytes.len()); - let mock_client_kp = Default::generate_random_keypair(&mut rng).unwrap(); - // serialization order: scalar, public key, envelope - let mut bytes = Vec::::new(); - bytes.extend_from_slice(sc.as_bytes()); - bytes.extend_from_slice(&mock_client_kp.public().to_arr()); - bytes.extend_from_slice(&mock_envelope_bytes); - let reg = ServerRegistration::::try_from(&bytes[..]).unwrap(); - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, bytes); - } - - #[test] - fn register_first_message_roundtrip() { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr().to_vec(); - let header = [1, 0, 0, 36, 0, 0, 0, 32]; - - let mut input = Vec::new(); - input.extend_from_slice(&header); - input.extend_from_slice(pt_bytes.as_slice()); - - let r1 = RegisterFirstMessage::::deserialize(input.as_slice()).unwrap(); - let r1_bytes = r1.serialize(); - assert_eq!(input, r1_bytes); - } - - #[test] - fn register_second_message_roundtrip() { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr(); - let header = [2, 0, 0, 40, 0, 32]; - let tail = [0, 0, 1, 1, 1, 3]; - - let mut input = Vec::new(); - input.extend_from_slice(&header); - input.extend_from_slice(pt_bytes.as_slice()); - input.extend_from_slice(&tail); - - let r2 = RegisterSecondMessage::::deserialize(input.as_slice()).unwrap(); - let r2_bytes = r2.serialize(); - assert_eq!(input, r2_bytes); - } - - #[test] - fn register_third_message_roundtrip() { - let mut rng = OsRng; - let skp = Default::generate_random_keypair(&mut rng).unwrap(); - let pubkey_bytes = skp.public().to_arr(); - - let header = [3, 0, 0, 136]; - let intermediate = [0, 32]; - - let mut key = [0u8; 32]; - rng.fill_bytes(&mut key); - - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - - let (envelope, _) = - Envelope::::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap(); - - let mut input = Vec::new(); - input.extend_from_slice(&header); - input.extend_from_slice(&envelope.serialize()); - input.extend_from_slice(&intermediate); - input.extend_from_slice(&pubkey_bytes[..]); - - let r3 = - RegisterThirdMessage::::deserialize(&input[..]).unwrap(); - let r3_bytes = r3.serialize(); - assert_eq!(input, r3_bytes); - } - - #[test] - fn login_first_message_roundtrip() { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr().to_vec(); - let header = [4, 0, 0, 36, 0, 0, 0, 32]; - - let mut rng = OsRng; - - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut client_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut client_nonce); - - let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); - - let mut input = Vec::new(); - input.extend_from_slice(&header); - input.extend_from_slice(pt_bytes.as_slice()); - input.extend_from_slice(&ke1m[..]); - - let l1 = LoginFirstMessage::::deserialize(input.as_slice()).unwrap(); - let l1_bytes = l1.serialize(); - assert_eq!(input, l1_bytes); - } - - #[test] - fn login_second_message_roundtrip() { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr().to_vec(); - let header = [5, 0, 0, 134, 0, 32]; - - let mut rng = OsRng; - let skp = Default::generate_random_keypair(&mut rng).unwrap(); - let pubkey_bytes = skp.public().to_arr(); - - let intermediate1 = [0, 96]; - let intermediate2 = [0, 0]; - - let mut key = [0u8; 32]; - rng.fill_bytes(&mut key); - - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - - let (envelope, _) = - Envelope::::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap(); - - let server_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut mac = [0u8; 32]; - rng.fill_bytes(&mut mac); - let mut server_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut server_nonce); - - let ke2m: Vec = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat(); - - let mut input = Vec::new(); - input.extend_from_slice(&header); - input.extend_from_slice(pt_bytes.as_slice()); - input.extend_from_slice(&intermediate1[..]); - input.extend_from_slice(&envelope.to_bytes()); - input.extend_from_slice(&intermediate2[..]); - input.extend_from_slice(&ke2m[..]); - - let l2 = LoginSecondMessage::::deserialize(input.as_slice()).unwrap(); - let l2_bytes = l2.serialize(); - assert_eq!(input, l2_bytes); - } - - #[test] - fn client_login_roundtrip() { - let pw = b"hunter2"; - let mut rng = OsRng; - let sc = ::random_scalar(&mut rng); - - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut client_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut client_nonce); - - let l1_data = [&sc.to_bytes()[..], &client_nonce, client_e_kp.public()].concat(); - let mut hasher = Sha256::new(); - hasher.update(l1_data); - let hashed_l1 = hasher.finalize(); - - // serialization order: scalar, password, ke1_state - let bytes: Vec = [ - &sc.as_bytes()[..], - &pw[..], - client_e_kp.public(), - &client_nonce, - hashed_l1.as_slice(), - ] - .concat(); - let reg = ClientLogin::::try_from(&bytes[..]).unwrap(); - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, bytes); - } - - #[test] - fn ke1_message_roundtrip() { - let mut rng = OsRng; - - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut client_nonce = [0u8; NONCE_LEN]; - rng.fill_bytes(&mut client_nonce); - - let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); - let reg = - >::KE1Message::try_from( - ke1m[..].to_vec(), - ) - .unwrap(); - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, ke1m); - } - - proptest! { - - #[test] - fn test_nocrash_register_first_message(bytes in vec(any::(), 0..200)) { - RegisterFirstMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_register_second_message(bytes in vec(any::(), 0..200)) { - RegisterSecondMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_register_third_message(bytes in vec(any::(), 0..200)) { - RegisterThirdMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_login_first_message(bytes in vec(any::(), 0..500)) { - LoginFirstMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_login_second_message(bytes in vec(any::(), 0..500)) { - LoginSecondMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_login_third_message(bytes in vec(any::(), 0..500)) { - LoginThirdMessage::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_client_registration(bytes in vec(any::(), 0..700)) { - ClientRegistration::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_server_registration(bytes in vec(any::(), 0..700)) { - ServerRegistration::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_client_login(bytes in vec(any::(), 0..700)) { - ClientLogin::::try_from(&bytes[..]).map_or(true, |_| true); - } - - #[test] - fn test_nocrash_server_login(bytes in vec(any::(), 0..700)) { - ServerLogin::::try_from(&bytes[..]).map_or(true, |_| true); - } - - } -} diff --git a/src/serialization/mod.rs b/src/serialization/mod.rs new file mode 100644 index 0000000..f39348e --- /dev/null +++ b/src/serialization/mod.rs @@ -0,0 +1,92 @@ +// 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 crate::errors::PakeError; + +use crate::{ + ciphersuite::CipherSuite, + hash::Hash, + keypair::KeyPair, + opaque::{ + LoginFirstMessage, LoginSecondMessage, RegisterFirstMessage, RegisterSecondMessage, + RegisterThirdMessage, + }, +}; + +pub enum ProtocolMessageType { + RegistrationRequest, + RegistrationResponse, + RegistrationUpload, + CredentialRequest, + CredentialResponse, +} + +pub enum CredentialType { + SkU, + PkU, + PkS, + IdU, + IdS, +} + +impl From<&RegisterFirstMessage> for ProtocolMessageType { + fn from(_mt: &RegisterFirstMessage) -> Self { + ProtocolMessageType::RegistrationRequest + } +} + +impl From<&RegisterSecondMessage> for ProtocolMessageType { + fn from(_mt: &RegisterSecondMessage) -> Self { + ProtocolMessageType::RegistrationResponse + } +} + +impl From<&RegisterThirdMessage> for ProtocolMessageType { + fn from(_mt: &RegisterThirdMessage) -> Self { + ProtocolMessageType::RegistrationUpload + } +} + +impl From<&LoginFirstMessage> for ProtocolMessageType { + fn from(_mt: &LoginFirstMessage) -> Self { + ProtocolMessageType::CredentialRequest + } +} + +impl From<&LoginSecondMessage> for ProtocolMessageType { + fn from(_mt: &LoginSecondMessage) -> Self { + ProtocolMessageType::CredentialResponse + } +} + +pub(crate) fn serialize(input: Vec, max_bytes: usize) -> Vec { + let mut output: Vec = Vec::new(); + output.extend_from_slice(&input.len().to_be_bytes()[8 - max_bytes..]); + output.extend_from_slice(&input[..]); + output +} + +pub(crate) fn tokenize(input: Vec, size_bytes: usize) -> Result<(Vec, Vec), PakeError> { + if size_bytes > 8 { + return Err(PakeError::SerializationError); + } + + let mut size_array = [0u8; 8]; + for i in 0..size_bytes { + size_array[8 - size_bytes + i] = input[i]; + } + let size = usize::from_be_bytes(size_array); + + if size_bytes + size > input.len() { + return Err(PakeError::SerializationError); + } + + Ok(( + input[size_bytes..size_bytes + size].to_vec(), + input[size_bytes + size..].to_vec(), + )) +} + +#[cfg(test)] +mod tests; diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs new file mode 100644 index 0000000..c7221ec --- /dev/null +++ b/src/serialization/tests.rs @@ -0,0 +1,314 @@ +// 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 crate::{ + ciphersuite::CipherSuite, + envelope::Envelope, + group::Group, + key_exchange::{ + traits::{KeyExchange, ToBytes}, + tripledh::{TripleDH, NONCE_LEN}, + }, + keypair::{KeyPair, SizedBytes, X25519KeyPair}, + opaque::*, +}; + +use curve25519_dalek::ristretto::RistrettoPoint; +use generic_array::typenum::Unsigned; +use proptest::{collection::vec, prelude::*}; +use rand_core::{OsRng, RngCore}; + +use sha2::{Digest, Sha256}; +use std::convert::TryFrom; + +struct Default; +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; +} + +fn random_ristretto_point() -> RistrettoPoint { + let mut rng = OsRng; + let mut random_bits = [0u8; 64]; + rng.fill_bytes(&mut random_bits); + + // This is because RistrettoPoint is on an obsolete sha2 version + let mut bits = [0u8; 64]; + let mut hasher = sha2::Sha512::new(); + hasher.update(&random_bits[..]); + bits.copy_from_slice(&hasher.finalize()); + + RistrettoPoint::from_uniform_bytes(&bits) +} + +#[test] +fn client_registration_roundtrip() { + let pw = b"hunter2"; + let mut rng = OsRng; + let sc = ::random_scalar(&mut rng); + // serialization order: scalar, password + let bytes: Vec = [&sc.as_bytes()[..], &pw[..]].concat(); + let reg = ClientRegistration::::try_from(&bytes[..]).unwrap(); + let reg_bytes = reg.to_bytes(); + assert_eq!(reg_bytes, bytes); +} + +#[test] +fn server_registration_roundtrip() { + // If we don't have envelope and client_pk, the server registration just + // contains the prf key + let mut rng = OsRng; + let sc = ::random_scalar(&mut rng); + let mut oprf_bytes: Vec = vec![]; + oprf_bytes.extend_from_slice(sc.as_bytes()); + let reg = ServerRegistration::::try_from(&oprf_bytes[..]).unwrap(); + let reg_bytes = reg.to_bytes(); + assert_eq!(reg_bytes, oprf_bytes); + // If we do have envelope and client pk, the server registration contains + // the whole kit + let key_len = + <<::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize(); + let envelope_size = key_len + Envelope::::additional_size(); + let mut mock_envelope_bytes = vec![0u8; envelope_size]; + rng.fill_bytes(&mut mock_envelope_bytes); + println!("{}", mock_envelope_bytes.len()); + let mock_client_kp = Default::generate_random_keypair(&mut rng).unwrap(); + // serialization order: scalar, public key, envelope + let mut bytes = Vec::::new(); + bytes.extend_from_slice(sc.as_bytes()); + bytes.extend_from_slice(&mock_client_kp.public().to_arr()); + bytes.extend_from_slice(&mock_envelope_bytes); + let reg = ServerRegistration::::try_from(&bytes[..]).unwrap(); + let reg_bytes = reg.to_bytes(); + assert_eq!(reg_bytes, bytes); +} + +#[test] +fn register_first_message_roundtrip() { + let pt = random_ristretto_point(); + let pt_bytes = pt.to_arr().to_vec(); + let header = [1, 0, 0, 36, 0, 0, 0, 32]; + + let mut input = Vec::new(); + input.extend_from_slice(&header); + input.extend_from_slice(pt_bytes.as_slice()); + + let r1 = RegisterFirstMessage::::deserialize(input.as_slice()).unwrap(); + let r1_bytes = r1.serialize(); + assert_eq!(input, r1_bytes); +} + +#[test] +fn register_second_message_roundtrip() { + let pt = random_ristretto_point(); + let pt_bytes = pt.to_arr(); + let header = [2, 0, 0, 40, 0, 32]; + let tail = [0, 0, 1, 1, 1, 3]; + + let mut input = Vec::new(); + input.extend_from_slice(&header); + input.extend_from_slice(pt_bytes.as_slice()); + input.extend_from_slice(&tail); + + let r2 = RegisterSecondMessage::::deserialize(input.as_slice()).unwrap(); + let r2_bytes = r2.serialize(); + assert_eq!(input, r2_bytes); +} + +#[test] +fn register_third_message_roundtrip() { + let mut rng = OsRng; + let skp = Default::generate_random_keypair(&mut rng).unwrap(); + let pubkey_bytes = skp.public().to_arr(); + + let header = [3, 0, 0, 136]; + let intermediate = [0, 32]; + + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + + let mut msg = [0u8; 32]; + rng.fill_bytes(&mut msg); + + let (envelope, _) = + Envelope::::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap(); + + let mut input = Vec::new(); + input.extend_from_slice(&header); + input.extend_from_slice(&envelope.serialize()); + input.extend_from_slice(&intermediate); + input.extend_from_slice(&pubkey_bytes[..]); + + let r3 = RegisterThirdMessage::::deserialize(&input[..]).unwrap(); + let r3_bytes = r3.serialize(); + assert_eq!(input, r3_bytes); +} + +#[test] +fn login_first_message_roundtrip() { + let pt = random_ristretto_point(); + let pt_bytes = pt.to_arr().to_vec(); + let header = [4, 0, 0, 36, 0, 0, 0, 32]; + + let mut rng = OsRng; + + let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let mut client_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut client_nonce); + + let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); + + let mut input = Vec::new(); + input.extend_from_slice(&header); + input.extend_from_slice(pt_bytes.as_slice()); + input.extend_from_slice(&ke1m[..]); + + let l1 = LoginFirstMessage::::deserialize(input.as_slice()).unwrap(); + let l1_bytes = l1.serialize(); + assert_eq!(input, l1_bytes); +} + +#[test] +fn login_second_message_roundtrip() { + let pt = random_ristretto_point(); + let pt_bytes = pt.to_arr().to_vec(); + let header = [5, 0, 0, 134, 0, 32]; + + let mut rng = OsRng; + let skp = Default::generate_random_keypair(&mut rng).unwrap(); + let pubkey_bytes = skp.public().to_arr(); + + let intermediate1 = [0, 96]; + let intermediate2 = [0, 0]; + + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + + let mut msg = [0u8; 32]; + rng.fill_bytes(&mut msg); + + let (envelope, _) = + Envelope::::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap(); + + let server_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let mut mac = [0u8; 32]; + rng.fill_bytes(&mut mac); + let mut server_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut server_nonce); + + let ke2m: Vec = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat(); + + let mut input = Vec::new(); + input.extend_from_slice(&header); + input.extend_from_slice(pt_bytes.as_slice()); + input.extend_from_slice(&intermediate1[..]); + input.extend_from_slice(&envelope.to_bytes()); + input.extend_from_slice(&intermediate2[..]); + input.extend_from_slice(&ke2m[..]); + + let l2 = LoginSecondMessage::::deserialize(input.as_slice()).unwrap(); + let l2_bytes = l2.serialize(); + assert_eq!(input, l2_bytes); +} + +#[test] +fn client_login_roundtrip() { + let pw = b"hunter2"; + let mut rng = OsRng; + let sc = ::random_scalar(&mut rng); + + let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let mut client_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut client_nonce); + + let l1_data = [&sc.to_bytes()[..], &client_nonce, client_e_kp.public()].concat(); + let mut hasher = Sha256::new(); + hasher.update(l1_data); + let hashed_l1 = hasher.finalize(); + + // serialization order: scalar, password, ke1_state + let bytes: Vec = [ + &sc.as_bytes()[..], + &pw[..], + client_e_kp.public(), + &client_nonce, + hashed_l1.as_slice(), + ] + .concat(); + let reg = ClientLogin::::try_from(&bytes[..]).unwrap(); + let reg_bytes = reg.to_bytes(); + assert_eq!(reg_bytes, bytes); +} + +#[test] +fn ke1_message_roundtrip() { + let mut rng = OsRng; + + let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let mut client_nonce = [0u8; NONCE_LEN]; + rng.fill_bytes(&mut client_nonce); + + let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); + let reg = + >::KE1Message::try_from(&ke1m[..]).unwrap(); + let reg_bytes = reg.to_bytes(); + assert_eq!(reg_bytes, ke1m); +} + +proptest! { + +#[test] +fn test_nocrash_register_first_message(bytes in vec(any::(), 0..200)) { + RegisterFirstMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_register_second_message(bytes in vec(any::(), 0..200)) { + RegisterSecondMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_register_third_message(bytes in vec(any::(), 0..200)) { + RegisterThirdMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_login_first_message(bytes in vec(any::(), 0..500)) { + LoginFirstMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_login_second_message(bytes in vec(any::(), 0..500)) { + LoginSecondMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_login_third_message(bytes in vec(any::(), 0..500)) { + LoginThirdMessage::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_client_registration(bytes in vec(any::(), 0..700)) { + ClientRegistration::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_server_registration(bytes in vec(any::(), 0..700)) { + ServerRegistration::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_client_login(bytes in vec(any::(), 0..700)) { + ClientLogin::::try_from(&bytes[..]).map_or(true, |_| true); +} + +#[test] +fn test_nocrash_server_login(bytes in vec(any::(), 0..700)) { + ServerLogin::::try_from(&bytes[..]).map_or(true, |_| true); +} + +} diff --git a/src/slow_hash.rs b/src/slow_hash.rs index 129fafc..5c51df8 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -5,8 +5,7 @@ //! Trait specifying a slow hashing function -use crate::errors::InternalPakeError; -use crate::hash::Hash; +use crate::{errors::InternalPakeError, hash::Hash}; use digest::Digest; use generic_array::GenericArray;