Addressing various TODOs and minor cleanups (#127)

This commit is contained in:
Kevin Lewi
2021-02-02 12:12:20 -08:00
committed by GitHub
parent be6d042d80
commit 9d3963f98e
12 changed files with 120 additions and 135 deletions
Generated
-46
View File
@@ -389,12 +389,6 @@ version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
[[package]]
name = "fiat-crypto"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f6ab97095615857b6ad00a8330fff0e443f1def9fd357cef82d0ca0677b616b"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@@ -613,7 +607,6 @@ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"digest", "digest",
"displaydoc", "displaydoc",
"fiat-crypto",
"generic-array", "generic-array",
"generic-bytes", "generic-bytes",
"generic-bytes-derive", "generic-bytes-derive",
@@ -630,7 +623,6 @@ dependencies = [
"sha2", "sha2",
"subtle", "subtle",
"thiserror", "thiserror",
"x25519-dalek",
"zeroize", "zeroize",
] ]
@@ -1023,18 +1015,6 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.1.0" version = "3.1.0"
@@ -1255,34 +1235,8 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "x25519-dalek"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc614d95359fd7afc321b66d2107ede58b246b844cf5d8a0adcca413e439f088"
dependencies = [
"curve25519-dalek",
"rand_core",
"zeroize",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.1.1" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a" checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
+3 -5
View File
@@ -13,14 +13,13 @@ readme = "README.md"
default = ["u64_backend"] default = ["u64_backend"]
slow-hash = ["scrypt"] slow-hash = ["scrypt"]
bench = [] bench = []
u64_backend = ["curve25519-dalek/u64_backend", "x25519-dalek/u64_backend"] u64_backend = ["curve25519-dalek/u64_backend"]
u32_backend = ["curve25519-dalek/u32_backend", "x25519-dalek/u32_backend"] u32_backend = ["curve25519-dalek/u32_backend"]
[dependencies] [dependencies]
curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] } curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] }
digest = "0.9.0" digest = "0.9.0"
displaydoc = "0.1.7" displaydoc = "0.1.7"
fiat-crypto = { version = "0.1.5"}
generic-array = "0.14.4" generic-array = "0.14.4"
generic-bytes = { version = "0.1.0" } generic-bytes = { version = "0.1.0" }
generic-bytes-derive = { version = "0.1.0" } generic-bytes-derive = { version = "0.1.0" }
@@ -28,10 +27,8 @@ hkdf = "0.10.0"
hmac = "0.10.1" hmac = "0.10.1"
rand_core = "0.5.1" rand_core = "0.5.1"
scrypt = { version = "0.5.0", optional = true } scrypt = { version = "0.5.0", optional = true }
sha2 = "0.9.2"
subtle = { version = "2.3.0", default-features = false } subtle = { version = "2.3.0", default-features = false }
thiserror = "1.0.22" thiserror = "1.0.22"
x25519-dalek = { version = "1.1.0", default-features = false, features = ["std"] }
zeroize = "1.1.1" zeroize = "1.1.1"
[dev-dependencies] [dev-dependencies]
@@ -42,6 +39,7 @@ criterion = "0.3.3"
hex = "0.4.2" hex = "0.4.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
serde_json = "1.0.60" serde_json = "1.0.60"
sha2 = "0.9.2"
proptest = "0.10.1" proptest = "0.10.1"
rand = "0.7" rand = "0.7"
rustyline = "7.0.0" rustyline = "7.0.0"
+23 -5
View File
@@ -34,9 +34,9 @@ use std::process::exit;
use opaque_ke::{ use opaque_ke::{
ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
ClientRegistration, ClientRegistrationFinishParameters, CredentialRequest, CredentialResponse, ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
ServerLoginStartParameters, ServerRegistration, RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
}; };
// The ciphersuite trait allows to specify the underlying primitives // The ciphersuite trait allows to specify the underlying primitives
@@ -173,9 +173,27 @@ fn open_locker(
return Err(String::from("Incorrect password, please try again.")); return Err(String::from("Incorrect password, please try again."));
} }
let client_login_finish_result = result.unwrap(); let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
// Decrypt contents of locker // Client sends credential_finalization_bytes to server
let plaintext = decrypt(&client_login_finish_result.export_key, &locker.contents);
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")) String::from_utf8(plaintext).map_err(|_| String::from("UTF8 error"))
} }
+3
View File
@@ -100,6 +100,9 @@ pub(crate) struct Envelope<D: Hash> {
hmac: GenericArray<u8, <D as Digest>::OutputSize>, 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) struct OpenedEnvelope<D: Hash> {
pub(crate) client_s_sk: Vec<u8>, pub(crate) client_s_sk: Vec<u8>,
pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>, pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>,
+1 -1
View File
@@ -21,7 +21,6 @@ pub trait KeyExchange<D: Hash, G: Group> {
type KE3Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes; type KE3Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
l1_component: Vec<u8>,
info: Vec<u8>, info: Vec<u8>,
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>; ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
@@ -44,6 +43,7 @@ pub trait KeyExchange<D: Hash, G: Group> {
l2_component: Vec<u8>, l2_component: Vec<u8>,
ke2_message: Self::KE2Message, ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State, ke1_state: &Self::KE1State,
serialized_credential_request: &[u8],
server_s_pk: Key, server_s_pk: Key,
client_s_sk: Key, client_s_sk: Key,
id_u: Vec<u8>, id_u: Vec<u8>,
+19 -28
View File
@@ -28,7 +28,6 @@ use rand_core::{CryptoRng, RngCore};
use std::convert::TryFrom; use std::convert::TryFrom;
const KEY_LEN: usize = 32; const KEY_LEN: usize = 32;
pub(crate) const NONCE_LEN: usize = 32;
pub(crate) type NonceLen = U32; pub(crate) type NonceLen = U32;
static STR_3DH: &[u8] = b"3DH keys"; static STR_3DH: &[u8] = b"3DH keys";
@@ -51,15 +50,14 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>; type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
alpha_bytes: Vec<u8>,
info: Vec<u8>, info: Vec<u8>,
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng); let client_e_kp = KeyPair::<G>::generate_random(rng);
let client_nonce: GenericArray<u8, NonceLen> = { 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); rng.fill_bytes(&mut client_nonce_bytes);
client_nonce_bytes.into() GenericArray::clone_from_slice(&client_nonce_bytes)
}; };
let ke1_message = KE1Message { let ke1_message = KE1Message {
@@ -68,14 +66,10 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
client_e_pk: client_e_kp.public().clone(), 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(( Ok((
KE1State { KE1State {
client_e_sk: client_e_kp.private().clone(), client_e_sk: client_e_kp.private().clone(),
client_nonce, client_nonce,
serialized_credential_request,
}, },
ke1_message, ke1_message,
)) ))
@@ -95,9 +89,9 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> { ) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyPair::<G>::generate_random(rng); let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce: GenericArray<u8, NonceLen> = { 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); 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>( let (session_secret, km2, ke2, km3) = derive_3dh_keys::<D, G>(
@@ -169,6 +163,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
l2_component: Vec<u8>, l2_component: Vec<u8>,
ke2_message: Self::KE2Message, ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State, ke1_state: &Self::KE1State,
serialized_credential_request: &[u8],
server_s_pk: Key, server_s_pk: Key,
client_s_sk: Key, client_s_sk: Key,
id_u: Vec<u8>, id_u: Vec<u8>,
@@ -190,7 +185,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
)?; )?;
let transcript: Vec<u8> = [ let transcript: Vec<u8> = [
&ke1_state.serialized_credential_request[..], &serialized_credential_request,
&l2_component[..], &l2_component[..],
&ke2_message.to_bytes_without_mac(), &ke2_message.to_bytes_without_mac(),
] ]
@@ -258,7 +253,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
} }
fn ke2_message_size() -> usize { 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()
} }
} }
@@ -267,7 +262,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
pub struct KE1State { pub struct KE1State {
client_e_sk: Key, client_e_sk: Key,
client_nonce: GenericArray<u8, NonceLen>, client_nonce: GenericArray<u8, NonceLen>,
serialized_credential_request: Vec<u8>,
} }
/// The first key exchange message /// The first key exchange message
@@ -282,26 +276,21 @@ impl TryFrom<&[u8]> for KE1State {
type Error = PakeError; type Error = PakeError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> { 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 { Ok(Self {
client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?, client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?,
client_nonce: GenericArray::clone_from_slice( 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 { impl ToBytes for KE1State {
fn to_bytes(&self) -> Vec<u8> { fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [ let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
&self.client_e_sk.to_arr(),
&self.client_nonce[..],
&self.serialized_credential_request[..],
]
.concat();
output output
} }
} }
@@ -321,15 +310,16 @@ impl TryFrom<&[u8]> for KE1Message {
type Error = PakeError; type Error = PakeError;
fn try_from(ke1_message_bytes: &[u8]) -> Result<Self, Self::Error> { fn try_from(ke1_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let nonce_len = NonceLen::to_usize();
let checked_nonce = 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")?; let checked_client_e_pk = check_slice_size(&remainder, KEY_LEN, "ke1_message client_e_pk")?;
Ok(Self { Ok(Self {
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..NONCE_LEN]), client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
info, info,
client_e_pk: Key::from_bytes(&checked_client_e_pk)?, client_e_pk: Key::from_bytes(&checked_client_e_pk)?,
}) })
@@ -401,9 +391,10 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
type Error = PakeError; type Error = PakeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> { 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( let checked_server_e_pk = check_slice_size_atleast(
&checked_nonce[NONCE_LEN..], &checked_nonce[nonce_len..],
KEY_LEN, KEY_LEN,
"ke2_message server_e_pk", "ke2_message server_e_pk",
)?; )?;
@@ -411,7 +402,7 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
let checked_mac = check_slice_size(&remainder, HashLen::to_usize(), "ke1_message mac")?; let checked_mac = check_slice_size(&remainder, HashLen::to_usize(), "ke1_message mac")?;
Ok(Self { 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])?, server_e_pk: Key::from_bytes(&checked_server_e_pk[..KEY_LEN])?,
e_info, e_info,
mac: GenericArray::clone_from_slice(&checked_mac), mac: GenericArray::clone_from_slice(&checked_mac),
-1
View File
@@ -37,7 +37,6 @@ impl GroupWithMapToCurve for RistrettoPoint {
// Implements the hash_to_ristretto255() function from // Implements the hash_to_ristretto255() function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt // 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> { 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 = let uniform_bytes =
expand_message_xmd::<H>(msg, dst, <H as Digest>::OutputSize::to_usize())?; expand_message_xmd::<H>(msg, dst, <H as Digest>::OutputSize::to_usize())?;
Ok(<Self as Group>::hash_to_curve( Ok(<Self as Group>::hash_to_curve(
+18 -6
View File
@@ -259,12 +259,24 @@ pub struct CredentialResponse<CS: CipherSuite> {
impl<CS: CipherSuite> CredentialResponse<CS> { impl<CS: CipherSuite> CredentialResponse<CS> {
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
let mut credential_response: Vec<u8> = Vec::new(); [
credential_response.extend_from_slice(&self.beta.to_arr()); Self::serialize_without_ke(&self.beta, &self.server_s_pk, &self.envelope),
credential_response.extend_from_slice(&serialize(&self.server_s_pk.to_arr().to_vec(), 2)); self.ke2_message.to_bytes(),
credential_response.extend_from_slice(&self.envelope.to_bytes()); ]
credential_response.extend_from_slice(&self.ke2_message.to_bytes()); .concat()
credential_response }
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 /// Deserialization from bytes
+20 -17
View File
@@ -408,6 +408,7 @@ pub struct ClientLogin<CS: CipherSuite> {
/// token containing the client's password and the blinding factor /// token containing the client's password and the blinding factor
token: oprf::Token<CS::Group>, token: oprf::Token<CS::Group>,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State, ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State,
serialized_credential_request: Vec<u8>,
} }
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> { impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
@@ -427,7 +428,8 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]); let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?; let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
let (ke1_state_bytes, password) = tokenize(&checked_slice[scalar_len..], 2)?; let (serialized_credential_request, remainder) = tokenize(&checked_slice[scalar_len..], 2)?;
let (ke1_state_bytes, password) = tokenize(&remainder, 2)?;
let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State::try_from( let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State::try_from(
&ke1_state_bytes[..], &ke1_state_bytes[..],
@@ -438,6 +440,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
blind: blinding_factor, blind: blinding_factor,
}, },
ke1_state, ke1_state,
serialized_credential_request,
}) })
} }
} }
@@ -447,6 +450,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
pub fn to_bytes(&self) -> Vec<u8> { pub fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [ let output: Vec<u8> = [
&CS::Group::scalar_as_bytes(&self.token.blind)[..], &CS::Group::scalar_as_bytes(&self.token.blind)[..],
&serialize(&self.serialized_credential_request, 2),
&serialize(&self.ke1_state.to_bytes(), 2), &serialize(&self.ke1_state.to_bytes(), 2),
&self.token.data, &self.token.data,
] ]
@@ -536,14 +540,18 @@ impl<CS: CipherSuite> ClientLogin<CS> {
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, rng)?; let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, rng)?;
let (ke1_state, ke1_message) = let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(info, rng)?;
CS::KeyExchange::generate_ke1(alpha.to_arr().to_vec(), info, rng)?;
let l1 = CredentialRequest { alpha, ke1_message }; let credential_request = CredentialRequest { alpha, ke1_message };
let serialized_credential_request = credential_request.serialize();
Ok(ClientLoginStartResult { Ok(ClientLoginStartResult {
message: l1, message: credential_request,
state: Self { token, ke1_state }, state: Self {
token,
ke1_state,
serialized_credential_request,
},
}) })
} }
@@ -627,6 +635,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
l2_bytes, l2_bytes,
l2.ke2_message, l2.ke2_message,
&self.ke1_state, &self.ke1_state,
&self.serialized_credential_request,
l2.server_s_pk.clone(), l2.server_s_pk.clone(),
client_s_sk, client_s_sk,
id_u, id_u,
@@ -772,21 +781,15 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let l1_bytes = &l1.to_bytes(); let l1_bytes = &l1.to_bytes();
let beta = oprf::evaluate(l1.alpha, &password_file.oprf_key); let beta = oprf::evaluate(l1.alpha, &password_file.oprf_key);
let server_s_pk = KeyPair::<CS::Group>::public_from_private(&server_s_sk); let server_s_pk = KeyPair::<CS::Group>::public_from_private(&server_s_sk);
// TODO: must match serialization of credential response, could be done more cleanly let credential_response_component =
let l2_component: Vec<u8> = [ CredentialResponse::<CS>::serialize_without_ke(&beta, &server_s_pk, &envelope);
&beta.to_arr()[..],
&serialize(&server_s_pk.to_arr()[..], 2),
&envelope.to_bytes()[..],
]
.concat();
let (plain_info, ke2_state, ke2_message) = CS::KeyExchange::generate_ke2( let (plain_info, ke2_state, ke2_message) = CS::KeyExchange::generate_ke2(
rng, rng,
l1_bytes.to_vec(), l1_bytes.to_vec(),
l2_component, credential_response_component,
l1.ke1_message, l1.ke1_message,
client_s_pk, client_s_pk,
server_s_sk.clone(), server_s_sk.clone(),
@@ -795,7 +798,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
e_info, e_info,
)?; )?;
let l2 = CredentialResponse { let credential_response = CredentialResponse {
beta, beta,
server_s_pk, server_s_pk,
envelope, envelope,
@@ -804,7 +807,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
Ok(ServerLoginStartResult { Ok(ServerLoginStartResult {
plain_info, plain_info,
message: l2, message: credential_response,
state: Self { state: Self {
_cs: PhantomData, _cs: PhantomData,
ke2_state, ke2_state,
-1
View File
@@ -22,7 +22,6 @@ pub(crate) fn i2osp(input: usize, length: usize) -> Vec<u8> {
// Corresponds to the OS2IP() function from RFC8017 // Corresponds to the OS2IP() function from RFC8017
pub(crate) fn os2ip(input: &[u8]) -> Result<usize, PakeError> { pub(crate) fn os2ip(input: &[u8]) -> Result<usize, PakeError> {
if input.len() > std::mem::size_of::<usize>() { if input.len() > std::mem::size_of::<usize>() {
// TODO:: check RFC compliance in refusing this
return Err(PakeError::SerializationError); return Err(PakeError::SerializationError);
} }
+18 -10
View File
@@ -9,7 +9,7 @@ use crate::{
group::Group, group::Group,
key_exchange::{ key_exchange::{
traits::{KeyExchange, ToBytes}, traits::{KeyExchange, ToBytes},
tripledh::{TripleDH, NONCE_LEN}, tripledh::{NonceLen, TripleDH},
}, },
opaque::*, opaque::*,
serialization::{i2osp, os2ip, serialize}, serialization::{i2osp, os2ip, serialize},
@@ -17,6 +17,7 @@ use crate::{
}; };
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use generic_bytes::SizedBytes; use generic_bytes::SizedBytes;
use proptest::{collection::vec, prelude::*}; use proptest::{collection::vec, prelude::*};
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
@@ -79,7 +80,7 @@ fn server_registration_roundtrip() {
// Construct a mock envelope // Construct a mock envelope
let mut mock_envelope_bytes = Vec::new(); let mut mock_envelope_bytes = Vec::new();
mock_envelope_bytes.extend_from_slice(&[0; 1]); // mode = 0 mock_envelope_bytes.extend_from_slice(&[0; 1]); // mode = 0
mock_envelope_bytes.extend_from_slice(&[0; NONCE_LEN]); // empty nonce mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::to_usize()]); // empty nonce
mock_envelope_bytes.extend_from_slice(&[0, 0]); // empty ciphertext mock_envelope_bytes.extend_from_slice(&[0, 0]); // empty ciphertext
mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac
@@ -168,7 +169,7 @@ fn login_first_message_roundtrip() {
let alpha_bytes = alpha.to_arr().to_vec(); let alpha_bytes = alpha.to_arr().to_vec();
let client_e_kp = Default::generate_random_keypair(&mut rng); let client_e_kp = Default::generate_random_keypair(&mut rng);
let mut client_nonce = [0u8; NONCE_LEN]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let mut info = [0u8; MAX_INFO_LENGTH]; let mut info = [0u8; MAX_INFO_LENGTH];
@@ -218,7 +219,7 @@ fn login_second_message_roundtrip() {
let server_e_kp = Default::generate_random_keypair(&mut rng); let server_e_kp = Default::generate_random_keypair(&mut rng);
let mut mac = [0u8; MAC_SIZE]; let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac); rng.fill_bytes(&mut mac);
let mut server_nonce = [0u8; NONCE_LEN]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let mut e_info = [0u8; MAX_INFO_LENGTH]; let mut e_info = [0u8; MAX_INFO_LENGTH];
@@ -264,13 +265,20 @@ fn client_login_roundtrip() {
let sc = <RistrettoPoint as Group>::random_scalar(&mut rng); let sc = <RistrettoPoint as Group>::random_scalar(&mut rng);
let client_e_kp = Default::generate_random_keypair(&mut rng); let client_e_kp = Default::generate_random_keypair(&mut rng);
let mut client_nonce = [0u8; NONCE_LEN]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let l1_data = [&sc.to_bytes()[..], &client_nonce, client_e_kp.public()].concat(); let serialized_credential_request = b"serialized credential_request".to_vec();
let l1_data = [client_e_kp.private().to_arr().to_vec(), client_nonce].concat();
// serialization order: scalar, password, ke1_state // serialization order: scalar, credential_request, ke1_state, password
let bytes: Vec<u8> = [&sc.as_bytes()[..], &serialize(&l1_data, 2), &pw[..]].concat(); let bytes: Vec<u8> = [
&sc.as_bytes()[..],
&serialize(&serialized_credential_request, 2),
&serialize(&l1_data, 2),
&pw[..],
]
.concat();
let reg = ClientLogin::<Default>::try_from(&bytes[..]).unwrap(); let reg = ClientLogin::<Default>::try_from(&bytes[..]).unwrap();
let reg_bytes = reg.to_bytes(); let reg_bytes = reg.to_bytes();
assert_eq!(reg_bytes, bytes); assert_eq!(reg_bytes, bytes);
@@ -281,7 +289,7 @@ fn ke1_message_roundtrip() {
let mut rng = OsRng; let mut rng = OsRng;
let client_e_kp = Default::generate_random_keypair(&mut rng); let client_e_kp = Default::generate_random_keypair(&mut rng);
let mut client_nonce = [0u8; NONCE_LEN]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let mut info = [0u8; MAX_INFO_LENGTH]; let mut info = [0u8; MAX_INFO_LENGTH];
@@ -307,7 +315,7 @@ fn ke2_message_roundtrip() {
let server_e_kp = Default::generate_random_keypair(&mut rng); let server_e_kp = Default::generate_random_keypair(&mut rng);
let mut mac = [0u8; MAC_SIZE]; let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac); rng.fill_bytes(&mut mac);
let mut server_nonce = [0u8; NONCE_LEN]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let mut e_info = [0u8; MAX_INFO_LENGTH]; let mut e_info = [0u8; MAX_INFO_LENGTH];
rng.fill_bytes(&mut e_info); rng.fill_bytes(&mut e_info);
+15 -15
View File
@@ -7,7 +7,7 @@ use crate::{
ciphersuite::CipherSuite, ciphersuite::CipherSuite,
errors::*, errors::*,
group::Group, group::Group,
key_exchange::tripledh::{TripleDH, NONCE_LEN}, key_exchange::tripledh::{NonceLen, TripleDH},
keypair::Key, keypair::Key,
opaque::*, opaque::*,
slow_hash::NoOpHash, slow_hash::NoOpHash,
@@ -15,6 +15,7 @@ use crate::{
*, *,
}; };
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use generic_bytes::SizedBytes; use generic_bytes::SizedBytes;
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
use serde_json::Value; use serde_json::Value;
@@ -92,7 +93,7 @@ static TEST_VECTOR: &str = r#"
"credential_response": "2e9bf80e5c101ee2d391ff2aec06af97af17f443fc93a5cc98189681cb943d700020b67d948b98eecfb516a626fab94170e490ccd90da05fce94a016c4d5d6b7136a015dba53e994e82b6d76353341102791cdfb1c2b460e1f913c5741a9c8cf2017c90022f11b89b207eb0735f2bdb2a9968bc9d7c1c962fd0274b4fc1fff2466c769d3710586a3a21ac8ecf13aafe2dc5460eb2f52d8f760396a17beeaf1ef3d1e4e50dd906555832fc51fc474554cb9ff063f6685f9043efc08fd6816666833d68d3b0eb6d913cc5ceb7687d80293005e7f9452ce104d1bac6598dd2265f184cacd558d6b3142103d44b03f3886fe3e419b1b96521fa5a706bb1a4e003907daa381960d664a0006a0f4de671e13c37b24e29138ff9d3df3d55c3b822fedee8dcefd643a1deab7cda5aa1c20f31a0c2a1823c8de3e793f50b257833788b75609fb6c5c4a9cad225f3a981c4ff7b2", "credential_response": "2e9bf80e5c101ee2d391ff2aec06af97af17f443fc93a5cc98189681cb943d700020b67d948b98eecfb516a626fab94170e490ccd90da05fce94a016c4d5d6b7136a015dba53e994e82b6d76353341102791cdfb1c2b460e1f913c5741a9c8cf2017c90022f11b89b207eb0735f2bdb2a9968bc9d7c1c962fd0274b4fc1fff2466c769d3710586a3a21ac8ecf13aafe2dc5460eb2f52d8f760396a17beeaf1ef3d1e4e50dd906555832fc51fc474554cb9ff063f6685f9043efc08fd6816666833d68d3b0eb6d913cc5ceb7687d80293005e7f9452ce104d1bac6598dd2265f184cacd558d6b3142103d44b03f3886fe3e419b1b96521fa5a706bb1a4e003907daa381960d664a0006a0f4de671e13c37b24e29138ff9d3df3d55c3b822fedee8dcefd643a1deab7cda5aa1c20f31a0c2a1823c8de3e793f50b257833788b75609fb6c5c4a9cad225f3a981c4ff7b2",
"credential_finalization": "0640114ec21fbad6da11c61dfa77a48026646a2a1bd091ef5c9dc46b45f9e53c72f5d714e026d808b6de8911947eef437e9f6a6e969e1ea13f2f4880adc7c879", "credential_finalization": "0640114ec21fbad6da11c61dfa77a48026646a2a1bd091ef5c9dc46b45f9e53c72f5d714e026d808b6de8911947eef437e9f6a6e969e1ea13f2f4880adc7c879",
"client_registration_state": "a70b37951d84ed312a0a8e025b71eafdd362b4e7db872881762d506a271b6e0770617373776f7264", "client_registration_state": "a70b37951d84ed312a0a8e025b71eafdd362b4e7db872881762d506a271b6e0770617373776f7264",
"client_login_state": "a70b37951d84ed312a0a8e025b71eafdd362b4e7db872881762d506a271b6e0700a728fb3aadbc2716844d8de562d35e61914da734dcea0fa6ebd79603ef9eff4e01488244dbf75f86ebaa30307773d3e0eabccb91be349abef8a69c458dbf236e4cecf819ef83351e4c75e016ef845b62a12c14f397328960574935a42e04e0fa42488244dbf75f86ebaa30307773d3e0eabccb91be349abef8a69c458dbf236e4c0005696e666f3108973113b27d6f2291aaaeb92f34a8438d279a48b1b412bddbf2785be660e47970617373776f7264", "client_login_state": "a70b37951d84ed312a0a8e025b71eafdd362b4e7db872881762d506a271b6e070067ecf819ef83351e4c75e016ef845b62a12c14f397328960574935a42e04e0fa42488244dbf75f86ebaa30307773d3e0eabccb91be349abef8a69c458dbf236e4c0005696e666f3108973113b27d6f2291aaaeb92f34a8438d279a48b1b412bddbf2785be660e479004028fb3aadbc2716844d8de562d35e61914da734dcea0fa6ebd79603ef9eff4e01488244dbf75f86ebaa30307773d3e0eabccb91be349abef8a69c458dbf236e4c70617373776f7264",
"server_registration_state": "1eaf3cdd64da67e17e3364182f00ed0c323bfb44b20a1b7e0025a0334ab6d40d", "server_registration_state": "1eaf3cdd64da67e17e3364182f00ed0c323bfb44b20a1b7e0025a0334ab6d40d",
"server_login_state": "4952acf61167e6d0e799572827724fdf8c80bda8c62ac28deb68d125661dade2cc1136e634d5fb93f1de4d170ae3e2f5885e333ac3b72d630ab56618c2247fa4d5968eefd69cd270ed14fbcb39627fea9a93cd18175e1b5cfceb06f705bbcb5ecddcd0602aacd7cb358f3273b1099895210bf8baa37d2fab5ed8c092fe9e5739bc6c50cf08ca7d4f4107d48057507480e0c9534b4f04d77cd5156cbd112565d8a2d5beaa473003dcf8dd82d4046648a701c72afeed0f627fcb782627dfaed3ca", "server_login_state": "4952acf61167e6d0e799572827724fdf8c80bda8c62ac28deb68d125661dade2cc1136e634d5fb93f1de4d170ae3e2f5885e333ac3b72d630ab56618c2247fa4d5968eefd69cd270ed14fbcb39627fea9a93cd18175e1b5cfceb06f705bbcb5ecddcd0602aacd7cb358f3273b1099895210bf8baa37d2fab5ed8c092fe9e5739bc6c50cf08ca7d4f4107d48057507480e0c9534b4f04d77cd5156cbd112565d8a2d5beaa473003dcf8dd82d4046648a701c72afeed0f627fcb782627dfaed3ca",
"password_file": "1eaf3cdd64da67e17e3364182f00ed0c323bfb44b20a1b7e0025a0334ab6d40dba8c7e239ed64ea1e6068b52cbd5a34ba6f55d635d20a4593884dc271865ea63015dba53e994e82b6d76353341102791cdfb1c2b460e1f913c5741a9c8cf2017c90022f11b89b207eb0735f2bdb2a9968bc9d7c1c962fd0274b4fc1fff2466c769d3710586a3a21ac8ecf13aafe2dc5460eb2f52d8f760396a17beeaf1ef3d1e4e50dd906555832fc51fc474554cb9ff063f6685f9043efc08fd6816666833d68d3b0eb6d9", "password_file": "1eaf3cdd64da67e17e3364182f00ed0c323bfb44b20a1b7e0025a0334ab6d40dba8c7e239ed64ea1e6068b52cbd5a34ba6f55d635d20a4593884dc271865ea63015dba53e994e82b6d76353341102791cdfb1c2b460e1f913c5741a9c8cf2017c90022f11b89b207eb0735f2bdb2a9968bc9d7c1c962fd0274b4fc1fff2466c769d3710586a3a21ac8ecf13aafe2dc5460eb2f52d8f760396a17beeaf1ef3d1e4e50dd906555832fc51fc474554cb9ff063f6685f9043efc08fd6816666833d68d3b0eb6d9",
@@ -274,9 +275,9 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
rng.fill_bytes(&mut oprf_key_raw); rng.fill_bytes(&mut oprf_key_raw);
let mut envelope_nonce = [0u8; 32]; let mut envelope_nonce = [0u8; 32];
rng.fill_bytes(&mut envelope_nonce); rng.fill_bytes(&mut envelope_nonce);
let mut client_nonce = [0u8; NONCE_LEN]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let mut server_nonce = [0u8; NONCE_LEN]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let blinding_factor = CS::Group::random_scalar(&mut rng); let blinding_factor = CS::Group::random_scalar(&mut rng);
@@ -451,8 +452,8 @@ fn test_registration_response() -> Result<(), ProtocolError> {
let server_registration_start_result = let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start( ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut oprf_key_rng, &mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(), RegistrationRequest::deserialize(&parameters.registration_request[..])?,
&Key::try_from(&parameters.server_s_pk[..]).unwrap(), &Key::try_from(&parameters.server_s_pk[..])?,
)?; )?;
assert_eq!( assert_eq!(
hex::encode(parameters.registration_response), hex::encode(parameters.registration_response),
@@ -477,7 +478,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
)? )?
.finish( .finish(
&mut finish_registration_rng, &mut finish_registration_rng,
RegistrationResponse::deserialize(&parameters.registration_response[..]).unwrap(), RegistrationResponse::deserialize(&parameters.registration_response[..])?,
ClientRegistrationFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), ClientRegistrationFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s),
)?; )?;
@@ -500,8 +501,9 @@ fn test_password_file() -> Result<(), ProtocolError> {
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::try_from( let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
&parameters.server_registration_state[..], &parameters.server_registration_state[..],
)?; )?;
let password_file = server_registration let password_file = server_registration.finish(RegistrationUpload::deserialize(
.finish(RegistrationUpload::deserialize(&parameters.registration_upload[..]).unwrap())?; &parameters.registration_upload[..],
)?)?;
assert_eq!( assert_eq!(
hex::encode(parameters.password_file), hex::encode(parameters.password_file),
@@ -545,12 +547,11 @@ fn test_credential_response() -> Result<(), ProtocolError> {
CycleRng::new([parameters.server_e_sk, parameters.server_nonce].concat()); CycleRng::new([parameters.server_e_sk, parameters.server_nonce].concat());
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start( let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_e_sk_and_nonce_rng, &mut server_e_sk_and_nonce_rng,
ServerRegistration::try_from(&parameters.password_file[..]).unwrap(), ServerRegistration::try_from(&parameters.password_file[..])?,
&Key::try_from(&parameters.server_s_sk[..]).unwrap(), &Key::try_from(&parameters.server_s_sk[..])?,
CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize( CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_request[..], &parameters.credential_request[..],
) )?,
.unwrap(),
ServerLoginStartParameters::WithInfoAndIdentifiers( ServerLoginStartParameters::WithInfoAndIdentifiers(
parameters.einfo2.to_vec(), parameters.einfo2.to_vec(),
parameters.id_u, parameters.id_u,
@@ -577,8 +578,7 @@ fn test_credential_finalization() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let client_login_finish_result = let client_login_finish_result =
ClientLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.client_login_state[..]) ClientLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.client_login_state[..])?
.unwrap()
.finish( .finish(
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize( CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_response[..], &parameters.credential_response[..],