From 6dc1c8b212b78b2b194249c4311dcbfb77c01ed0 Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Mon, 25 Jan 2021 13:20:36 -0800 Subject: [PATCH] Turning KeyPair into a struct (#119) --- benches/oprf.rs | 62 +----- examples/digital_locker.rs | 15 +- examples/simple_login.rs | 16 +- src/ciphersuite.rs | 14 +- src/elligator/field.rs | 350 -------------------------------- src/elligator/mod.rs | 177 ---------------- src/group.rs | 129 +++--------- src/key_exchange/traits.rs | 13 +- src/key_exchange/tripledh.rs | 103 +++++----- src/keypair.rs | 172 ++++++---------- src/lib.rs | 85 +++----- src/map_to_curve.rs | 17 +- src/messages.rs | 53 +++-- src/opaque.rs | 147 +++++--------- src/oprf.rs | 40 ++-- src/serialization/tests.rs | 73 +++---- src/tests/opaque_ke_test.rs | 202 +++++++++--------- src/tests/voprf_test_vectors.rs | 35 +--- 18 files changed, 420 insertions(+), 1283 deletions(-) delete mode 100644 src/elligator/field.rs delete mode 100644 src/elligator/mod.rs diff --git a/benches/oprf.rs b/benches/oprf.rs index b3fbfe7..56e8642 100644 --- a/benches/oprf.rs +++ b/benches/oprf.rs @@ -7,14 +7,14 @@ extern crate criterion; use criterion::Criterion; -use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint}; +use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::arr; use opaque_ke::{ group::Group, oprf::{blind_shim, evaluate_shim, unblind_and_finalize_shim}, }; use rand::{prelude::ThreadRng, thread_rng}; -use sha2::{Sha256, Sha512}; +use sha2::Sha512; fn oprf1(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); @@ -27,17 +27,6 @@ fn oprf1(c: &mut Criterion) { }); } -fn oprf1_edwards(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - c.bench_function("blind with Edwards", move |b| { - b.iter(|| { - blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); - }) - }); -} - fn oprf2(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; @@ -56,24 +45,6 @@ fn oprf2(c: &mut Criterion) { }); } -fn oprf2_edwards(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - let (_, alpha) = blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); - let salt_bytes = arr![ - u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let salt = RistrettoPoint::from_scalar_slice(&salt_bytes).unwrap(); - - c.bench_function("evaluate with Edwards", move |b| { - b.iter(|| { - let _beta = evaluate_shim::(alpha, &salt); - }) - }); -} - fn oprf3(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; @@ -93,32 +64,5 @@ fn oprf3(c: &mut Criterion) { }); } -fn oprf3_edwards(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - let (token, alpha) = blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); - let salt_bytes = arr![ - u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let salt = RistrettoPoint::from_scalar_slice(&salt_bytes).unwrap(); - let beta = evaluate_shim::(alpha, &salt); - - c.bench_function("unblind_and_finalize with Edwards", move |b| { - b.iter(|| { - let _res = unblind_and_finalize_shim::(&token, beta).unwrap(); - }) - }); -} - -criterion_group!( - oprf_benches, - oprf1, - oprf2, - oprf3, - oprf1_edwards, - oprf2_edwards, - oprf3_edwards -); +criterion_group!(oprf_benches, oprf1, oprf2, oprf3); criterion_main!(oprf_benches); diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 9617f31..7deaf4c 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -33,10 +33,10 @@ use std::convert::TryFrom; use std::process::exit; use opaque_ke::{ - ciphersuite::CipherSuite, keypair::KeyPair, ClientLogin, ClientLoginFinishParameters, - ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters, - CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, - RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, + ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, + ClientRegistration, ClientRegistrationFinishParameters, CredentialRequest, CredentialResponse, + RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, + ServerLoginStartParameters, ServerRegistration, }; // The ciphersuite trait allows to specify the underlying primitives @@ -45,7 +45,6 @@ use opaque_ke::{ struct Default; impl CipherSuite for Default { 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; @@ -82,7 +81,7 @@ fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec { // Password-based registration and encryption of client secret message between a client and server fn register_locker( - server_kp: &opaque_ke::keypair::X25519KeyPair, + server_kp: &opaque_ke::keypair::KeyPair, password: String, secret_message: String, ) -> Locker { @@ -135,7 +134,7 @@ fn register_locker( // Open the contents of a locker with a password between a client and server fn open_locker( - server_kp: &opaque_ke::keypair::X25519KeyPair, + server_kp: &opaque_ke::keypair::KeyPair, password: String, locker: &Locker, ) -> Result { @@ -182,7 +181,7 @@ fn open_locker( fn main() { let mut rng = OsRng; - let server_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let server_kp = Default::generate_random_keypair(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_lockers: Vec = vec![]; diff --git a/examples/simple_login.rs b/examples/simple_login.rs index ce1357c..e54555c 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -28,11 +28,10 @@ use std::convert::TryFrom; use std::process::exit; use opaque_ke::{ - ciphersuite::CipherSuite, keypair::KeyPair, ClientLogin, ClientLoginFinishParameters, - ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters, - CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, - RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters, - ServerRegistration, + ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, + ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization, + CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, + RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, }; // The ciphersuite trait allows to specify the underlying primitives @@ -41,7 +40,6 @@ use opaque_ke::{ struct Default; impl CipherSuite for Default { 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; @@ -49,7 +47,7 @@ impl CipherSuite for Default { // Password-based registration between a client and server fn account_registration( - server_kp: &opaque_ke::keypair::X25519KeyPair, + server_kp: &opaque_ke::keypair::KeyPair, password: String, ) -> Vec { let mut client_rng = OsRng; @@ -91,7 +89,7 @@ fn account_registration( // Password-based login between a client and server fn account_login( - server_kp: &opaque_ke::keypair::X25519KeyPair, + server_kp: &opaque_ke::keypair::KeyPair, password: String, password_file_bytes: &[u8], ) -> bool { @@ -144,7 +142,7 @@ fn account_login( fn main() { let mut rng = OsRng; - let server_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let server_kp = Default::generate_random_keypair(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_users = HashMap::>::new(); diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 996f9ce..e5f5024 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -6,7 +6,7 @@ //! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE use crate::{ - errors::InternalPakeError, hash::Hash, key_exchange::traits::KeyExchange, keypair::KeyPair, + hash::Hash, key_exchange::traits::KeyExchange, keypair::KeyPair, map_to_curve::GroupWithMapToCurve, slow_hash::SlowHash, }; @@ -17,7 +17,6 @@ use rand_core::{CryptoRng, RngCore}; /// with an extension trait PasswordToCurve that allows some customization on /// how to hash a password to a curve point. See `group::Group` and /// `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 /// * `Hash`: The main hashing function to use /// * `SlowHash`: A slow hashing function, typically used for password hashing @@ -27,18 +26,15 @@ pub trait CipherSuite { /// how to hash a password to a curve point. See `group::Group` and /// `map_to_curve::GroupWithMapToCurve`. type Group: GroupWithMapToCurve; - /// A keypair type composed of public and private components - type KeyFormat: KeyPair + PartialEq; /// A key exchange protocol - type KeyExchange: KeyExchange; + type KeyExchange: KeyExchange; /// 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; + /// Generating a random key pair given a cryptographic rng - fn generate_random_keypair( - rng: &mut R, - ) -> Result { - Self::KeyFormat::generate_random(rng) + fn generate_random_keypair(rng: &mut R) -> KeyPair { + KeyPair::::generate_random(rng) } } diff --git a/src/elligator/field.rs b/src/elligator/field.rs deleted file mode 100644 index 511d5be..0000000 --- a/src/elligator/field.rs +++ /dev/null @@ -1,350 +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. -#![allow(clippy::let_and_return)] - -//! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(64\\)-bit -//! limbs with \\(128\\)-bit products. - -use core::{ - fmt::Debug, - ops::{Add, AddAssign, Mul, MulAssign, Neg}, -}; - -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq}; - -use zeroize::Zeroize; - -use fiat_crypto::curve25519_64::*; - -/// A `FieldElement51` represents an element of the field -/// \\( \mathbb Z / (2\^{255} - 19)\\). -/// -/// In the 64-bit implementation, a `FieldElement` is represented in -/// radix \\(2\^{51}\\) as five `u64`s; the coefficients are allowed to -/// grow up to \\(2\^{54}\\) between reductions modulo \\(p\\). -/// -/// # Note -/// -/// The `curve25519_dalek::field` module provides a type alias -/// `curve25519_dalek::field::FieldElement` to either `FieldElement51` -/// or `FieldElement2625`. -/// -/// The backend-specific type `FieldElement51` should not be used -/// outside of the `curve25519_dalek::field` module. -#[derive(Copy, Clone)] -pub struct FieldElement51(pub(crate) [u64; 5]); - -impl Debug for FieldElement51 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "FieldElement51({:?})", &self.0[..]) - } -} - -impl Zeroize for FieldElement51 { - fn zeroize(&mut self) { - self.0.zeroize(); - } -} - -impl ConstantTimeEq for FieldElement51 { - /// Test equality between two `FieldElement`s. Since the - /// internal representation is not canonical, the field elements - /// are normalized to wire format before comparison. - fn ct_eq(&self, other: &FieldElement51) -> Choice { - self.to_bytes().ct_eq(&other.to_bytes()) - } -} - -impl<'b> AddAssign<&'b FieldElement51> for FieldElement51 { - fn add_assign(&mut self, _rhs: &'b FieldElement51) { - let input = self.0; - fiat_25519_add(&mut self.0, &input, &_rhs.0); - let input = self.0; - fiat_25519_carry(&mut self.0, &input); - } -} - -impl<'a, 'b> Add<&'b FieldElement51> for &'a FieldElement51 { - type Output = FieldElement51; - fn add(self, _rhs: &'b FieldElement51) -> FieldElement51 { - let mut output = *self; - fiat_25519_add(&mut output.0, &self.0, &_rhs.0); - let input = output.0; - fiat_25519_carry(&mut output.0, &input); - output - } -} - -impl<'b> MulAssign<&'b FieldElement51> for FieldElement51 { - fn mul_assign(&mut self, _rhs: &'b FieldElement51) { - let input = self.0; - fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0); - } -} - -impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 { - type Output = FieldElement51; - fn mul(self, _rhs: &'b FieldElement51) -> FieldElement51 { - let mut output = *self; - fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0); - output - } -} - -impl<'a> Neg for &'a FieldElement51 { - type Output = FieldElement51; - fn neg(self) -> FieldElement51 { - let mut output = *self; - fiat_25519_opp(&mut output.0, &self.0); - let input = output.0; - fiat_25519_carry(&mut output.0, &input); - output - } -} - -impl ConditionallySelectable for FieldElement51 { - fn conditional_select( - a: &FieldElement51, - b: &FieldElement51, - choice: Choice, - ) -> FieldElement51 { - let mut output = [0u64; 5]; - fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0); - FieldElement51(output) - } - - fn conditional_swap(a: &mut FieldElement51, b: &mut FieldElement51, choice: Choice) { - u64::conditional_swap(&mut a.0[0], &mut b.0[0], choice); - u64::conditional_swap(&mut a.0[1], &mut b.0[1], choice); - u64::conditional_swap(&mut a.0[2], &mut b.0[2], choice); - u64::conditional_swap(&mut a.0[3], &mut b.0[3], choice); - u64::conditional_swap(&mut a.0[4], &mut b.0[4], choice); - } - - fn conditional_assign(&mut self, _rhs: &FieldElement51, choice: Choice) { - let mut output = [0u64; 5]; - let choicebit = choice.unwrap_u8() as fiat_25519_u1; - fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], _rhs.0[0]); - fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], _rhs.0[1]); - fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], _rhs.0[2]); - fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], _rhs.0[3]); - fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], _rhs.0[4]); - *self = FieldElement51(output); - } -} - -impl FieldElement51 { - /// Construct zero. - pub fn zero() -> FieldElement51 { - FieldElement51([0, 0, 0, 0, 0]) - } - - /// Construct one. - pub fn one() -> FieldElement51 { - FieldElement51([1, 0, 0, 0, 0]) - } - - pub fn is_negative(&self) -> Choice { - let bytes = self.to_bytes(); - (bytes[0] & 1).into() - } - - /// Raise this field element to the power (p-5)/8 = 2^252 -3. - fn pow_p58(&self) -> FieldElement51 { - // The bits of (p-5)/8 are 101111.....11. - // - // nonzero bits of exponent - let (t19, _) = self.pow22501(); // 249..0 - let t20 = t19.pow2k(2); // 251..2 - let t21 = self * &t20; // 251..2,0 - - t21 - } - - /// Given a nonzero field element, compute its inverse. - /// - /// The inverse is computed as self^(p-2), since - /// x^(p-2)x = x^(p-1) = 1 (mod p). - /// - /// This function returns zero on input zero. - pub fn invert(&self) -> FieldElement51 { - // The bits of p-2 = 2^255 -19 -2 are 11010111111...11. - // - // nonzero bits of exponent - let (t19, t3) = self.pow22501(); // t19: 249..0 ; t3: 3,1,0 - let t20 = t19.pow2k(5); // 254..5 - let t21 = &t20 * &t3; // 254..5,3,1,0 - - t21 - } - - /// Compute (self^(2^250-1), self^11), used as a helper function - /// within invert() and pow22523(). - fn pow22501(&self) -> (FieldElement51, FieldElement51) { - // Instead of managing which temporary variables are used - // for what, we define as many as we need and leave stack - // allocation to the compiler - // - // Each temporary variable t_i is of the form (self)^e_i. - // Squaring t_i corresponds to multiplying e_i by 2, - // so the pow2k function shifts e_i left by k places. - // Multiplying t_i and t_j corresponds to adding e_i + e_j. - // - // Temporary t_i Nonzero bits of e_i - // - let t0 = self.square(); // 1 e_0 = 2^1 - let t1 = t0.square().square(); // 3 e_1 = 2^3 - let t2 = self * &t1; // 3,0 e_2 = 2^3 + 2^0 - let t3 = &t0 * &t2; // 3,1,0 - let t4 = t3.square(); // 4,2,1 - let t5 = &t2 * &t4; // 4,3,2,1,0 - let t6 = t5.pow2k(5); // 9,8,7,6,5 - let t7 = &t6 * &t5; // 9,8,7,6,5,4,3,2,1,0 - let t8 = t7.pow2k(10); // 19..10 - let t9 = &t8 * &t7; // 19..0 - let t10 = t9.pow2k(20); // 39..20 - let t11 = &t10 * &t9; // 39..0 - let t12 = t11.pow2k(10); // 49..10 - let t13 = &t12 * &t7; // 49..0 - let t14 = t13.pow2k(50); // 99..50 - let t15 = &t14 * &t13; // 99..0 - let t16 = t15.pow2k(100); // 199..100 - let t17 = &t16 * &t15; // 199..0 - let t18 = t17.pow2k(50); // 249..50 - let t19 = &t18 * &t13; // 249..0 - - (t19, t3) - } - - /// Load a `FieldElement51` from the low 255 bits of a 256-bit - /// input. - /// - /// # Warning - /// - /// This function does not check that the input used the canonical - /// representative. It masks the high bit, but it will happily - /// decode 2^255 - 18 to 1. Applications that require a canonical - /// encoding of every field element should decode, re-encode to - /// the canonical encoding, and check that the input was - /// canonical. - /// - pub fn from_bytes(bytes: &[u8; 32]) -> FieldElement51 { - let mut temp = [0u8; 32]; - temp.copy_from_slice(bytes); - temp[31] &= 127u8; - let mut output = [0u64; 5]; - fiat_25519_from_bytes(&mut output, &temp); - FieldElement51(output) - } - - /// Serialize this `FieldElement51` to a 32-byte array. The - /// encoding is canonical. - pub fn to_bytes(&self) -> [u8; 32] { - let mut bytes = [0u8; 32]; - fiat_25519_to_bytes(&mut bytes, &self.0); - bytes - } - - /// Given `k > 0`, return `self^(2^k)`. - pub fn pow2k(&self, mut k: u32) -> FieldElement51 { - let mut output = *self; - loop { - let input = output.0; - fiat_25519_carry_square(&mut output.0, &input); - k -= 1; - if k == 0 { - return output; - } - } - } - - /// Given `FieldElements` `u` and `v`, compute either `sqrt(u/v)` - /// or `sqrt(i*u/v)` in constant time. - /// - /// This function always returns the nonnegative square root. - /// - /// # Return - /// - /// - `(Choice(1), +sqrt(u/v)) ` if `v` is nonzero and `u/v` is square; - /// - `(Choice(1), zero) ` if `u` is zero; - /// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero; - /// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square). - /// - pub fn sqrt_ratio_i(u: &FieldElement51, v: &FieldElement51) -> (Choice, FieldElement51) { - // Using the same trick as in ed25519 decoding, we merge the - // inversion, the square root, and the square test as follows. - // - // To compute sqrt(α), we can compute β = α^((p+3)/8). - // Then β^2 = ±α, so multiplying β by sqrt(-1) if necessary - // gives sqrt(α). - // - // To compute 1/sqrt(α), we observe that - // 1/β = α^(p-1 - (p+3)/8) = α^((7p-11)/8) - // = α^3 * (α^7)^((p-5)/8). - // - // We can therefore compute sqrt(u/v) = sqrt(u)/sqrt(v) - // by first computing - // r = u^((p+3)/8) v^(p-1-(p+3)/8) - // = u u^((p-5)/8) v^3 (v^7)^((p-5)/8) - // = (uv^3) (uv^7)^((p-5)/8). - // - // If v is nonzero and u/v is square, then r^2 = ±u/v, - // so vr^2 = ±u. - // If vr^2 = u, then sqrt(u/v) = r. - // If vr^2 = -u, then sqrt(u/v) = r*sqrt(-1). - // - // If v is zero, r is also zero. - - let v3 = &v.square() * v; - let v7 = &v3.square() * v; - let mut r = &(u * &v3) * &(u * &v7).pow_p58(); - let check = v * &r.square(); - - let i = &SQRT_M1; - - let correct_sign_sqrt = check.ct_eq(u); - let flipped_sign_sqrt = check.ct_eq(&(-u)); - let flipped_sign_sqrt_i = check.ct_eq(&(&(-u) * i)); - - let r_prime = &SQRT_M1 * &r; - r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i); - - // Choose the nonnegative square root. - let r_is_negative = r.is_negative(); - r.conditional_negate(r_is_negative); - - let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt; - - (was_nonzero_square, r) - } - - /// Returns the square of this field element. - pub fn square(&self) -> FieldElement51 { - let mut output = *self; - fiat_25519_carry_square(&mut output.0, &self.0); - output - } - - /// Returns 2 times the square of this field element. - pub fn square2(&self) -> FieldElement51 { - let mut output = *self; - let mut temp = *self; - // Void vs return type, measure cost of copying self - fiat_25519_carry_square(&mut temp.0, &self.0); - fiat_25519_add(&mut output.0, &temp.0, &temp.0); - let input = output.0; - fiat_25519_carry(&mut output.0, &input); - output - } -} - -/// Precomputed value of one of the square roots of -1 (mod p) -pub(crate) const SQRT_M1: FieldElement51 = FieldElement51([ - 1718705420411056, - 234908883556509, - 2233514472574048, - 2117202627021982, - 765476049583133, -]); diff --git a/src/elligator/mod.rs b/src/elligator/mod.rs deleted file mode 100644 index 164020d..0000000 --- a/src/elligator/mod.rs +++ /dev/null @@ -1,177 +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. -#![allow(non_snake_case)] - -mod field; - -use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint}; -use field::FieldElement51; -use sha2::Digest; -use subtle::{ConditionallyNegatable, ConditionallySelectable}; - -const MONT_A: FieldElement51 = FieldElement51([486662, 0, 0, 0, 0]); - -fn elligator_signal(r_0: &FieldElement51) -> MontgomeryPoint { - let minus_a = -&MONT_A; /* A = 486662 */ - let one = FieldElement51::one(); - let d_1 = &one + &r_0.square2(); /* 2r^2 */ - - let d = &minus_a * &(d_1.invert()); /* A/(1+2r^2) */ - - let d_sq = &d.square(); - let au = &MONT_A * &d; - - let inner = &(d_sq + &au) + &one; - let eps = &d * &inner; /* eps = d^3 + Ad^2 + d */ - - let (eps_is_sq, _eps) = FieldElement51::sqrt_ratio_i(&eps, &one); - - let zero = FieldElement51::zero(); - let Atemp = FieldElement51::conditional_select(&MONT_A, &zero, eps_is_sq); /* 0, or A if nonsquare*/ - let mut u = &d + &Atemp; /* d, or d+A if nonsquare */ - u.conditional_negate(!eps_is_sq); /* d, or -d-A if nonsquare */ - - MontgomeryPoint(u.to_bytes()) -} - -pub fn hash_to_point(bytes: &[u8]) -> EdwardsPoint { - let mut hash = sha2::Sha512::new(); - hash.update(bytes); - let h = hash.finalize(); - let mut res = [0u8; 32]; - res.copy_from_slice(&h[..32]); - - let sign_bit = (res[31] & 0x80) >> 7; - - let fe = FieldElement51::from_bytes(&res); - - let M1 = elligator_signal(&fe); - let E1_opt = M1.to_edwards(sign_bit); - - E1_opt - .expect("Montgomery conversion to Edwards point in Elligator failed") - .mul_by_cofactor() -} - -#[cfg(test)] -mod tests { - use super::*; - use std::convert::TryInto; - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Signal tests from // - // https://github.com/signalapp/libsignal-protocol-c/blob/master/src/curve25519/ed25519/tests/internal_fast_tests.c#L222-L282 // - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [ - 0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac, - 0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95, - 0xf4, 0x6e, - ]; - - #[test] - fn elligator_correct() { - let bytes: Vec = (0u8..32u8).collect(); - let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken"); - - let fe = FieldElement51::from_bytes(&bits_in); - let eg = elligator_signal(&fe); - assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT); - } - - #[test] - fn elligator_zero_zero() { - let zero = [0u8; 32]; - let fe = FieldElement51::from_bytes(&zero); - let eg = elligator_signal(&fe); - assert_eq!(eg.to_bytes(), zero); - } - - const HASHTOPOINT_CORRECT_OUTPUT1: [u8; 32] = [ - 0xce, 0x89, 0x9f, 0xb2, 0x8f, 0xf7, 0x20, 0x91, 0x5e, 0x14, 0xf5, 0xb7, 0x99, 0x08, 0xab, - 0x17, 0xaa, 0x2e, 0xe2, 0x45, 0xb4, 0xfc, 0x2b, 0xf6, 0x06, 0x36, 0x29, 0x40, 0xed, 0x7d, - 0xe7, 0xed, - ]; - - const HASHTOPOINT_CORRECT_OUTPUT2: [u8; 32] = [ - 0xa0, 0x35, 0xbb, 0xa9, 0x4d, 0x30, 0x55, 0x33, 0x0d, 0xce, 0xc2, 0x7f, 0x83, 0xde, 0x79, - 0xd0, 0x89, 0x67, 0x72, 0x4c, 0x07, 0x8d, 0x68, 0x9d, 0x61, 0x52, 0x1d, 0xf9, 0x2c, 0x5c, - 0xba, 0x77, - ]; - - #[test] - fn test_hash_to_point_1() { - let bits: Vec = (0u8..32u8).collect(); - let hashed = hash_to_point(&bits); - assert_eq!(hashed.compress().to_bytes(), HASHTOPOINT_CORRECT_OUTPUT1); - } - - #[test] - fn test_hash_to_point_2() { - let bits: Vec = (0u8..32u8).map(|u| u + 1).collect(); - let hashed = hash_to_point(&bits); - assert_eq!(hashed.compress().to_bytes(), HASHTOPOINT_CORRECT_OUTPUT2); - } - - ///////////////////////////////////////// - // Additional test vectors from Signal // - ///////////////////////////////////////// - - fn test_vectors() -> Vec> { - vec![ - vec![ - "214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f", - "c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052", - ], - vec![ - "2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6", - "d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f", - ], - vec![ - "84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9", - "93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453", - ], - vec![ - "c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267", - "43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537", - ], - vec![ - "26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76", - "b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835", - ], - vec![ - "1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2", - "da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c", - ], - vec![ - "48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c", - "ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966", - ], - vec![ - "a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d", - "fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0", - ], - vec![ - "80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095", - "57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47", - ], - vec![ - "f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617", - "785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0", - ], - ] - } - - #[test] - fn additional_signal_test_vectors() { - for vector in test_vectors().iter() { - let input = hex::decode(vector[0]).unwrap(); - let output = hex::decode(vector[1]).unwrap(); - - let point = hash_to_point(&input); - assert_eq!(point.compress().to_bytes(), output[..]); - } - } -} diff --git a/src/group.rs b/src/group.rs index f01aabf..3a7af8f 100644 --- a/src/group.rs +++ b/src/group.rs @@ -6,10 +6,10 @@ //! Defines the Group trait to specify the underlying prime order group used in //! OPAQUE's OPRF -use crate::{elligator, errors::InternalPakeError}; +use crate::errors::InternalPakeError; use curve25519_dalek::{ - edwards::{CompressedEdwardsY, EdwardsPoint}, + constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, scalar::Scalar, }; @@ -17,6 +17,7 @@ use generic_array::{ typenum::{U32, U64}, ArrayLength, GenericArray, }; +use std::convert::TryInto; use rand_core::{CryptoRng, RngCore}; use std::ops::Mul; @@ -26,7 +27,7 @@ use zeroize::Zeroize; /// subgroup is noted additively — as in the draft RFC — in this trait. pub trait Group: Sized + for<'a> Mul<&'a ::Scalar, Output = Self> { /// The type of base field scalars - type Scalar: Zeroize; + type Scalar: Zeroize + Clone; /// The byte length necessary to represent scalars type ScalarLen: ArrayLength; /// Return a scalar from its fixed-length bytes representation @@ -57,6 +58,12 @@ pub trait Group: Sized + for<'a> Mul<&'a ::Scalar, Output = Self> /// Hashes a slice of pseudo-random bytes of the correct length to a curve point fn hash_to_curve(uniform_bytes: &GenericArray) -> Self; + + /// Get the base point for the group + fn base_point() -> Self; + + /// Multiply the point by a scalar, represented as a slice + fn mult_by_slice(&self, scalar: &GenericArray) -> Self; } /// The implementation of such a subgroup for Ristretto @@ -71,7 +78,16 @@ impl Group for RistrettoPoint { Ok(Scalar::from_bytes_mod_order(bits)) } fn random_scalar(rng: &mut R) -> Self::Scalar { - Scalar::random(rng) + #[cfg(not(test))] + return Scalar::random(rng); + + // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng + #[cfg(test)] + { + let mut scalar_bytes = [0u8; 32]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order(scalar_bytes) + } } fn scalar_as_bytes(scalar: &Self::Scalar) -> &GenericArray { GenericArray::from_slice(scalar.as_bytes()) @@ -105,108 +121,13 @@ impl Group for RistrettoPoint { }; RistrettoPoint::from_uniform_bytes(&bits) } -} -/// The implementation of such a subgroup for points on the large Curve25519-subgroup -impl Group for EdwardsPoint { - type Scalar = Scalar; - type ScalarLen = U32; - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result { - let mut bits = [0u8; 32]; - bits.copy_from_slice(scalar_bits); - Ok(Scalar::from_bytes_mod_order(bits)) - } - fn random_scalar(rng: &mut R) -> Self::Scalar { - Scalar::random(rng) - } - fn scalar_as_bytes(scalar: &Self::Scalar) -> &GenericArray { - GenericArray::from_slice(scalar.as_bytes()) - } - fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar { - scalar.invert() + fn base_point() -> Self { + RISTRETTO_BASEPOINT_POINT } - // The byte length necessary to represent group elements - type ElemLen = U32; - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result { - let point = CompressedEdwardsY::from_slice(element_bits) - .decompress() - .ok_or(InternalPakeError::PointError)?; - - if point.is_small_order() { - return Err(InternalPakeError::SubGroupError); - } - Ok(point) - } - // serialization of a group element - fn to_arr(&self) -> GenericArray { - let c = self.compress(); - *GenericArray::from_slice(c.as_bytes()) - } - - type UniformBytesLen = U32; - fn hash_to_curve(uniform_bytes: &GenericArray) -> Self { - elligator::hash_to_point(uniform_bytes) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use anyhow::{anyhow, Result}; - use std::convert::TryInto; - - const EIGHT_TORSION: [[u8; 32]; 8] = [ - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ], - [ - 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, - 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 128, - ], - [ - 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, - 172, 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 5, - ], - [ - 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, - ], - [ - 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, - 172, 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 133, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ], - [ - 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, - 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 250, - ], - ]; - - fn deserialize_point(pt: &[u8]) -> Result { - let bytes: [u8; 32] = (&pt[..32]).try_into()?; - curve25519_dalek::edwards::CompressedEdwardsY(bytes) - .decompress() - .ok_or_else(|| anyhow!("Point decompression failed!")) - } - - #[test] - fn test_small_subgroup_edwards() { - for pt in &EIGHT_TORSION[..] { - assert!(deserialize_point(&pt[..]).is_ok()); - assert!(EdwardsPoint::from_element_slice(GenericArray::from_slice(&pt[..])).is_err()); - } + fn mult_by_slice(&self, scalar: &GenericArray) -> Self { + let arr: [u8; 32] = scalar.as_slice().try_into().expect("Wrong length"); + self * Scalar::from_bits(arr) } } diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index b88660a..26b0ac9 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -5,14 +5,15 @@ use crate::{ errors::{PakeError, ProtocolError}, + group::Group, hash::Hash, - keypair::KeyPair, + keypair::Key, }; use rand_core::{CryptoRng, RngCore}; use std::convert::TryFrom; -pub trait KeyExchange { +pub trait KeyExchange { type KE1State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes; type KE2State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes; type KE1Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes; @@ -31,8 +32,8 @@ pub trait KeyExchange { l1_bytes: Vec, l2_bytes: Vec, ke1_message: Self::KE1Message, - client_s_pk: KeyFormat::Repr, - server_s_sk: KeyFormat::Repr, + client_s_pk: Key, + server_s_sk: Key, id_u: Vec, id_s: Vec, e_info: Vec, @@ -43,8 +44,8 @@ pub trait KeyExchange { l2_component: Vec, ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, - server_s_pk: KeyFormat::Repr, - client_s_sk: KeyFormat::Repr, + server_s_pk: Key, + client_s_sk: Key, id_u: Vec, id_s: Vec, ) -> Result<(Vec, Vec, Self::KE3Message), ProtocolError>; diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index 183de32..d434f1e 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -9,9 +9,10 @@ use crate::{ utils::{check_slice_size, check_slice_size_atleast}, InternalPakeError, PakeError, ProtocolError, }, + group::Group, hash::Hash, key_exchange::traits::{KeyExchange, ToBytes}, - keypair::{KeyPair, SizedBytesExt}, + keypair::{Key, KeyPair, SizedBytesExt}, serialization::{serialize, tokenize}, }; use digest::{Digest, FixedOutput}; @@ -29,7 +30,6 @@ use std::convert::TryFrom; const KEY_LEN: usize = 32; pub(crate) const NONCE_LEN: usize = 32; pub(crate) type NonceLen = U32; -const KE1_STATE_LEN: usize = KEY_LEN + KEY_LEN + NONCE_LEN; static STR_3DH: &[u8] = b"3DH keys"; static STR_CLIENT_MAC: &[u8] = b"client mac"; @@ -43,11 +43,11 @@ static STR_OPAQUE: &[u8] = b"OPAQUE "; /// The Triple Diffie-Hellman key exchange implementation pub struct TripleDH; -impl KeyExchange for TripleDH { - type KE1State = KE1State<::OutputSize, KeyFormat>; +impl KeyExchange for TripleDH { + type KE1State = KE1State<::OutputSize>; type KE2State = KE2State<::OutputSize>; - type KE1Message = KE1Message; - type KE2Message = KE2Message<::OutputSize, KeyFormat>; + type KE1Message = KE1Message; + type KE2Message = KE2Message<::OutputSize>; type KE3Message = KE3Message<::OutputSize>; fn generate_ke1( @@ -55,7 +55,7 @@ impl KeyExchange for TripleDH { info: Vec, rng: &mut R, ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { - let client_e_kp = KeyFormat::generate_random(rng)?; + let client_e_kp = KeyPair::::generate_random(rng); let client_nonce: GenericArray = { let mut client_nonce_bytes = [0u8; NONCE_LEN]; rng.fill_bytes(&mut client_nonce_bytes); @@ -89,20 +89,20 @@ impl KeyExchange for TripleDH { l1_bytes: Vec, l2_bytes: Vec, ke1_message: Self::KE1Message, - client_s_pk: KeyFormat::Repr, - server_s_sk: KeyFormat::Repr, + client_s_pk: Key, + server_s_sk: Key, id_u: Vec, id_s: Vec, e_info: Vec, ) -> Result<(Vec, Self::KE2State, Self::KE2Message), ProtocolError> { - let server_e_kp = KeyFormat::generate_random(rng)?; + let server_e_kp = KeyPair::::generate_random(rng); let server_nonce: GenericArray = { let mut server_nonce_bytes = [0u8; NONCE_LEN]; rng.fill_bytes(&mut server_nonce_bytes); server_nonce_bytes.into() }; - let (session_secret, km2, ke2, km3) = derive_3dh_keys::( + let (session_secret, km2, ke2, km3) = derive_3dh_keys::( TripleDHComponents { pk1: ke1_message.client_e_pk.clone(), sk1: server_e_kp.private().clone(), @@ -176,12 +176,12 @@ impl KeyExchange for TripleDH { l2_component: Vec, ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, - server_s_pk: KeyFormat::Repr, - client_s_sk: KeyFormat::Repr, + server_s_pk: Key, + client_s_sk: Key, id_u: Vec, id_s: Vec, ) -> Result<(Vec, Vec, Self::KE3Message), ProtocolError> { - let (session_secret, km2, ke2, km3) = derive_3dh_keys::( + let (session_secret, km2, ke2, km3) = derive_3dh_keys::( TripleDHComponents { pk1: ke2_message.server_e_pk.clone(), sk1: ke1_state.client_e_sk.clone(), @@ -265,7 +265,7 @@ impl KeyExchange for TripleDH { } fn ke1_state_size() -> usize { - KE1_STATE_LEN + NONCE_LEN + KEY_LEN + <::OutputSize as Unsigned>::to_usize() } fn ke2_message_size() -> usize { @@ -275,21 +275,21 @@ impl KeyExchange for TripleDH { /// The client state produced after the first key exchange message #[derive(PartialEq, Eq)] -pub struct KE1State, KeyFormat: KeyPair> { - client_e_sk: KeyFormat::Repr, +pub struct KE1State> { + client_e_sk: Key, client_nonce: GenericArray, hashed_l1: GenericArray, } /// The first key exchange message #[derive(PartialEq, Eq)] -pub struct KE1Message { +pub struct KE1Message { pub(crate) client_nonce: GenericArray, pub(crate) info: Vec, - pub(crate) client_e_pk: KeyFormat::Repr, + pub(crate) client_e_pk: Key, } -impl, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State { +impl> TryFrom<&[u8]> for KE1State { type Error = PakeError; fn try_from(bytes: &[u8]) -> Result { @@ -300,7 +300,7 @@ impl, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State, KeyFormat: KeyPair> ToBytes for KE1State { +impl> ToBytes for KE1State { fn to_bytes(&self) -> Vec { let output: Vec = [ &self.client_e_sk.to_arr(), @@ -321,7 +321,7 @@ impl, KeyFormat: KeyPair> ToBytes for KE1State ToBytes for KE1Message { +impl ToBytes for KE1Message { fn to_bytes(&self) -> Vec { [ &self.client_nonce[..], @@ -332,7 +332,7 @@ impl ToBytes for KE1Message { } } -impl TryFrom<&[u8]> for KE1Message { +impl TryFrom<&[u8]> for KE1Message { type Error = PakeError; fn try_from(ke1_message_bytes: &[u8]) -> Result { @@ -346,7 +346,7 @@ impl TryFrom<&[u8]> for KE1Message { Ok(Self { client_nonce: GenericArray::clone_from_slice(&checked_nonce[..NONCE_LEN]), info, - client_e_pk: KeyFormat::Repr::from_bytes(&checked_client_e_pk)?, + client_e_pk: Key::from_bytes(&checked_client_e_pk)?, }) } } @@ -358,9 +358,9 @@ pub struct KE2State> { } /// The second key exchange message -pub struct KE2Message, KeyFormat: KeyPair> { +pub struct KE2Message> { server_nonce: GenericArray, - server_e_pk: KeyFormat::Repr, + server_e_pk: Key, e_info: Vec, mac: GenericArray, } @@ -380,23 +380,28 @@ impl> TryFrom<&[u8]> for KE2State { type Error = PakeError; fn try_from(input: &[u8]) -> Result { - let checked_bytes = check_slice_size(input, 3 * KEY_LEN, "ke2_state")?; + let hash_len = HashLen::to_usize(); + let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?; Ok(Self { - km3: GenericArray::clone_from_slice(&checked_bytes[..KEY_LEN]), - hashed_transcript: GenericArray::clone_from_slice(&checked_bytes[KEY_LEN..2 * KEY_LEN]), - session_secret: GenericArray::clone_from_slice(&checked_bytes[2 * KEY_LEN..]), + km3: GenericArray::clone_from_slice(&checked_bytes[..hash_len]), + hashed_transcript: GenericArray::clone_from_slice( + &checked_bytes[hash_len..2 * hash_len], + ), + session_secret: GenericArray::clone_from_slice( + &checked_bytes[2 * hash_len..3 * hash_len], + ), }) } } -impl, KeyFormat: KeyPair> ToBytes for KE2Message { +impl> ToBytes for KE2Message { fn to_bytes(&self) -> Vec { [&self.to_bytes_without_mac(), &self.mac[..]].concat() } } -impl, KeyFormat: KeyPair> KE2Message { +impl> KE2Message { fn to_bytes_without_mac(&self) -> Vec { [ &self.server_nonce[..], @@ -407,9 +412,7 @@ impl, KeyFormat: KeyPair> KE2Message, KeyFormat: KeyPair> TryFrom<&[u8]> - for KE2Message -{ +impl> TryFrom<&[u8]> for KE2Message { type Error = PakeError; fn try_from(input: &[u8]) -> Result { @@ -424,7 +427,7 @@ impl, KeyFormat: KeyPair> TryFrom<&[u8]> Ok(Self { server_nonce: GenericArray::clone_from_slice(&checked_nonce[..NONCE_LEN]), - server_e_pk: KeyFormat::Repr::from_bytes(&checked_server_e_pk[..KEY_LEN])?, + server_e_pk: Key::from_bytes(&checked_server_e_pk[..KEY_LEN])?, e_info, mac: GenericArray::clone_from_slice(&checked_mac), }) @@ -432,13 +435,13 @@ impl, KeyFormat: KeyPair> TryFrom<&[u8]> } // The triple of public and private components used in the 3DH computation -struct TripleDHComponents { - pk1: KeyFormat::Repr, - sk1: KeyFormat::Repr, - pk2: KeyFormat::Repr, - sk2: KeyFormat::Repr, - pk3: KeyFormat::Repr, - sk3: KeyFormat::Repr, +struct TripleDHComponents { + pk1: Key, + sk1: Key, + pk2: Key, + sk2: Key, + pk3: Key, + sk3: Key, } // Consists of a shared secret, followed by two mac keys and an encryption key: (session_secret, km2, ke2, km3) @@ -464,7 +467,7 @@ impl> TryFrom<&[u8]> for KE3Message { type Error = PakeError; fn try_from(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(&bytes, KEY_LEN, "ke3_message")?; + let checked_bytes = check_slice_size(&bytes, HashLen::to_usize(), "ke3_message")?; Ok(Self { mac: GenericArray::clone_from_slice(&checked_bytes), @@ -476,17 +479,17 @@ impl> TryFrom<&[u8]> for KE3Message { // 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( - dh: TripleDHComponents, +fn derive_3dh_keys( + dh: TripleDHComponents, client_nonce: &GenericArray, server_nonce: &GenericArray, id_u: &[u8], id_s: &[u8], ) -> Result, ProtocolError> { let ikm: Vec = [ - &KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..], - &KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..], - &KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..], + &KeyPair::::diffie_hellman(dh.pk1, dh.sk1)?[..], + &KeyPair::::diffie_hellman(dh.pk2, dh.sk2)?[..], + &KeyPair::::diffie_hellman(dh.pk3, dh.sk3)?[..], ] .concat(); diff --git a/src/keypair.rs b/src/keypair.rs index d1ef36b..9c9b24c 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -6,17 +6,17 @@ //! Contains the keypair types that must be supplied for the OPAQUE API use crate::errors::InternalPakeError; +use crate::group::Group; use generic_array::{typenum::U32, GenericArray}; use generic_bytes::{SizedBytes, TryFromSizedBytesError}; -use generic_bytes_derive::{SizedBytes, TryFromForSizedBytes}; +use generic_bytes_derive::TryFromForSizedBytes; #[cfg(test)] use proptest::prelude::*; #[cfg(test)] use rand::{rngs::StdRng, SeedableRng}; use rand_core::{CryptoRng, RngCore}; -use std::convert::TryInto; use std::fmt::Debug; -use x25519_dalek::{PublicKey, StaticSecret}; +use std::marker::PhantomData; use std::ops::Deref; @@ -31,46 +31,79 @@ pub(crate) trait SizedBytesExt: SizedBytes { impl SizedBytesExt for T where T: SizedBytes {} /// A Keypair trait with public-private verification -pub trait KeyPair: Sized + Clone { - /// The single key representation must have a specific byte size itself - type Repr: SizedBytes + Clone; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct KeyPair { + pk: Key, + sk: Key, + _g: PhantomData, +} +impl KeyPair { /// The public key component - fn public(&self) -> &Self::Repr; + pub fn public(&self) -> &Key { + &self.pk + } /// The private key component - fn private(&self) -> &Self::Repr; + pub fn private(&self) -> &Key { + &self.sk + } /// A constructor that receives public and private key independently as /// bytes - fn new(public: Self::Repr, private: Self::Repr) -> Result; + pub fn new(public: Key, private: Key) -> Result { + Ok(Self { + pk: public, + sk: private, + _g: PhantomData, + }) + } /// Generating a random key pair given a cryptographic rng - fn generate_random(rng: &mut R) -> Result; + pub(crate) fn generate_random(rng: &mut R) -> Self { + let sk = G::random_scalar(rng); + let sk_bytes = G::scalar_as_bytes(&sk); + let pk = G::base_point().mult_by_slice(&sk_bytes); + Self { + pk: Key(pk.to_arr().to_vec()), + sk: Key(sk_bytes.to_vec()), + _g: PhantomData, + } + } /// Obtaining a public key from secret bytes. At all times, we should have /// &public_from_private(self.private()) == self.public() - fn public_from_private(secret: &Self::Repr) -> Self::Repr; + pub(crate) fn public_from_private(bytes: &Key) -> Key { + let bytes_data = GenericArray::::from_slice(&bytes.0[..]); + Key(G::base_point().mult_by_slice(&bytes_data).to_arr().to_vec()) + } /// Check whether a public key is valid. This is meant to be applied on /// material provided through the network which fits the key /// representation (i.e. can be mapped to a curve point), but presents /// some risk - e.g. small subgroup check - fn check_public_key(key: Self::Repr) -> Result; + pub(crate) fn check_public_key(key: Key) -> Result { + G::from_element_slice(GenericArray::from_slice(&key.0)).map(|_| key) + } /// Computes the diffie hellman function on a public key and private key - fn diffie_hellman(pk: Self::Repr, sk: Self::Repr) -> Vec; + pub(crate) fn diffie_hellman(pk: Key, sk: Key) -> Result, InternalPakeError> { + let pk_data = GenericArray::::from_slice(&pk.0[..]); + let point = G::from_element_slice(&pk_data)?; + let secret_data = GenericArray::::from_slice(&sk.0[..]); + Ok(G::mult_by_slice(&point, &secret_data).to_arr().to_vec()) + } /// Obtains a KeyPair from a slice representing the private key - fn from_private_key_slice(input: &[u8]) -> Result { - let sk = Self::Repr::from_arr(GenericArray::from_slice(&input))?; + pub fn from_private_key_slice(input: &[u8]) -> Result { + let sk = Key::from_arr(GenericArray::from_slice(&input))?; let pk = Self::public_from_private(&sk); Self::new(pk, sk) } } #[cfg(test)] -trait KeyPairExt: KeyPair + Debug { +impl KeyPair { /// Test-only strategy returning a proptest Strategy based on /// generate_random fn uniform_keypair_strategy() -> BoxedStrategy { @@ -79,17 +112,13 @@ trait KeyPairExt: KeyPair + Debug { any::<[u8; 32]>() .prop_filter_map("valid random keypair", |seed| { let mut rng = StdRng::from_seed(seed); - Self::generate_random(&mut rng).ok() + Some(Self::generate_random(&mut rng)) }) .no_shrink() .boxed() } } -// blanket implementation -#[cfg(test)] -impl KeyPairExt for KP where KP: KeyPair + Debug {} - /// A minimalist key type built around [u8;32] #[derive(Debug, PartialEq, Eq, Clone, TryFromForSizedBytes)] #[ErrorType = "::generic_bytes::TryFromSizedBytesError"] @@ -116,117 +145,40 @@ impl SizedBytes for Key { } } -/// A representation of an X25519 keypair according to RFC7748 -#[derive(Clone, Debug, PartialEq, Eq, SizedBytes, TryFromForSizedBytes)] -#[ErrorType = "::generic_bytes::TryFromSizedBytesError"] -pub struct X25519KeyPair { - pk: Key, - sk: Key, -} - -impl X25519KeyPair { - fn gen(rng: &mut R) -> (Vec, Vec) { - let sk = StaticSecret::new(rng); - let pk = PublicKey::from(&sk); - (pk.as_bytes().to_vec(), sk.to_bytes().to_vec()) - } -} - -impl KeyPair for X25519KeyPair { - type Repr = Key; - - fn public(&self) -> &Self::Repr { - &self.pk - } - - fn private(&self) -> &Self::Repr { - &self.sk - } - - fn new(public: Self::Repr, private: Self::Repr) -> Result { - Ok(X25519KeyPair { - pk: public, - sk: private, - }) - } - - fn generate_random(rng: &mut R) -> Result { - let (public, private) = X25519KeyPair::gen(rng); - Ok(X25519KeyPair { - pk: Key(public), - sk: Key(private), - }) - } - - fn public_from_private(secret: &Self::Repr) -> Self::Repr { - let secret_data: [u8; 32] = (&secret.0[..]) - .try_into() - .expect("Keypair::Repr invariant broken"); - let base_data = ::x25519_dalek::X25519_BASEPOINT_BYTES; - Key(::x25519_dalek::x25519(secret_data, base_data).to_vec()) - } - - fn check_public_key(key: Self::Repr) -> Result { - let key_bytes: [u8; 32] = - (&key[..]) - .try_into() - .map_err(|_| InternalPakeError::SizeError { - name: "key", - len: 32, - actual_len: key.len(), - })?; - let point = ::curve25519_dalek::montgomery::MontgomeryPoint(key_bytes) - .to_edwards(1) - .ok_or(InternalPakeError::PointError)?; - if !point.is_torsion_free() { - Err(InternalPakeError::SubGroupError) - } else { - Ok(key) - } - } - - fn diffie_hellman(pk: Self::Repr, sk: Self::Repr) -> Vec { - let mut pk_data = [0; 32]; - pk_data.copy_from_slice(&pk.0[..]); - let mut sk_data = [0; 32]; - sk_data.copy_from_slice(&sk.0[..]); - ::x25519_dalek::x25519(sk_data, pk_data).to_vec() - } -} - #[cfg(test)] mod tests { use super::*; + use curve25519_dalek::ristretto::RistrettoPoint; proptest! { #[test] - fn test_x25519_check(kp in X25519KeyPair::uniform_keypair_strategy()) { + fn test_ristretto_check(kp in KeyPair::::uniform_keypair_strategy()) { let pk = kp.public(); - prop_assert!(X25519KeyPair::check_public_key(pk.clone()).is_ok()); + prop_assert!(KeyPair::::check_public_key(pk.clone()).is_ok()); } #[test] - fn test_x25519_pub_from_priv(kp in X25519KeyPair::uniform_keypair_strategy()) { + fn test_ristretto_pub_from_priv(kp in KeyPair::::uniform_keypair_strategy()) { let pk = kp.public(); let sk = kp.private(); - prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk); + prop_assert_eq!(&KeyPair::::public_from_private(sk), pk); } #[test] - fn test_x25519_dh(kp1 in X25519KeyPair::uniform_keypair_strategy(), - kp2 in X25519KeyPair::uniform_keypair_strategy()) { + fn test_ristretto_dh(kp1 in KeyPair::::uniform_keypair_strategy(), + kp2 in KeyPair::::uniform_keypair_strategy()) { - let dh1 = X25519KeyPair::diffie_hellman(kp1.public().clone(), kp2.private().clone()); - let dh2 = X25519KeyPair::diffie_hellman(kp2.public().clone(), kp1.private().clone()); + let dh1 = KeyPair::::diffie_hellman(kp1.public().clone(), kp2.private().clone())?; + let dh2 = KeyPair::::diffie_hellman(kp2.public().clone(), kp1.private().clone())?; - prop_assert_eq!(dh1,dh2); + prop_assert_eq!(dh1, dh2); } #[test] - fn test_private_key_slice(kp in X25519KeyPair::uniform_keypair_strategy()) { + fn test_private_key_slice(kp in KeyPair::::uniform_keypair_strategy()) { let sk_bytes = kp.private().to_vec(); - let kp2 = X25519KeyPair::from_private_key_slice(&sk_bytes)?; + let kp2 = KeyPair::::from_private_key_slice(&sk_bytes)?; let kp2_private_bytes = kp2.private().to_vec(); prop_assert_eq!(sk_bytes, kp2_private_bytes); diff --git a/src/lib.rs b/src/lib.rs index 28ea40f..9b7c8db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ //! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives //! 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, //! * a hashing function, //! * a slow hashing function, and @@ -25,9 +24,8 @@ //! struct Default; //! impl CipherSuite for Default { //! 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 Hash = sha2::Sha512; //! type SlowHash = opaque_ke::slow_hash::NoOpHash; //! } //! ``` @@ -41,20 +39,18 @@ //! ## Setup //! To set up the protocol, the server begins by generating a static keypair: //! ``` -//! # use opaque_ke::keypair::{KeyPair, X25519KeyPair}; //! # use opaque_ke::errors::ProtocolError; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use rand_core::{OsRng, RngCore}; //! let mut rng = OsRng; -//! let server_kp = Default::generate_random_keypair(&mut rng)?; +//! let server_kp = Default::generate_random_keypair(&mut rng); //! # Ok::<(), ProtocolError>(()) //! ``` //! The server must persist this keypair for the registration and login steps, where the public component will be @@ -74,16 +70,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ServerRegistration, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use opaque_ke::ClientRegistration; @@ -106,16 +100,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -126,7 +118,7 @@ //! # )?; //! use opaque_ke::ServerRegistration; //! let mut server_rng = OsRng; -//! let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! let server_kp = Default::generate_random_keypair(&mut server_rng); //! let server_registration_start_result = ServerRegistration::::start( //! &mut server_rng, //! client_registration_start_result.message, @@ -145,16 +137,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -164,7 +154,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! let client_registration_finish_result = client_registration_start_result.state.finish( //! &mut client_rng, @@ -185,16 +175,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -204,7 +192,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; //! let password_file = server_registration_start_result.state.finish( @@ -228,16 +216,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ServerRegistration, ServerLogin, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -263,16 +249,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -282,7 +266,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; //! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes(); @@ -314,16 +298,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -333,7 +315,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; //! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes(); @@ -363,16 +345,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -382,7 +362,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; //! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes(); @@ -441,16 +421,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -461,7 +439,7 @@ //! # )?; //! # let mut server_rng = OsRng; //! // During setup, server generates its static keypair -//! let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! //! // During setup or registration, the server transmits its static public key to the client @@ -520,16 +498,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -539,7 +515,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! // During registration... //! let client_registration_finish_result = client_registration_start_result.state.finish( @@ -587,16 +563,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -606,7 +580,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! let client_registration_finish_result = client_registration_start_result.state.finish( //! &mut client_rng, @@ -624,16 +598,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -643,7 +615,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?; //! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes(); @@ -674,16 +646,14 @@ //! # use opaque_ke::{ //! # errors::ProtocolError, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, -//! # keypair::{KeyPair, X25519KeyPair}, //! # slow_hash::NoOpHash, //! # }; //! # use opaque_ke::ciphersuite::CipherSuite; //! # struct Default; //! # impl CipherSuite for Default { //! # 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 Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! # use rand_core::{OsRng, RngCore}; @@ -693,7 +663,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?; +//! # let server_kp = Default::generate_random_keypair(&mut server_rng); //! # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?; //! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes(); @@ -759,7 +729,6 @@ pub mod ciphersuite; mod envelope; pub mod hash; -mod elligator; pub mod group; pub mod map_to_curve; diff --git a/src/map_to_curve.rs b/src/map_to_curve.rs index 3b28751..0e301ac 100644 --- a/src/map_to_curve.rs +++ b/src/map_to_curve.rs @@ -10,11 +10,10 @@ use crate::errors::InternalPakeError; use crate::group::Group; use crate::hash::Hash; use crate::serialization::i2osp; -use curve25519_dalek::{edwards::EdwardsPoint, ristretto::RistrettoPoint}; +use curve25519_dalek::ristretto::RistrettoPoint; use digest::{BlockInput, Digest}; use generic_array::typenum::Unsigned; use generic_array::GenericArray; -use hkdf::Hkdf; /// A subtrait of Group specifying how to hash a password into a point pub trait GroupWithMapToCurve: Group { @@ -38,23 +37,15 @@ 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(msg: &[u8], dst: &[u8]) -> Result { - let uniform_bytes = expand_message_xmd::(msg, dst, 64)?; + // FIXME use generic_array and turn this into a compile-time error if size mismatch + let uniform_bytes = + expand_message_xmd::(msg, dst, ::OutputSize::to_usize())?; Ok(::hash_to_curve( &GenericArray::clone_from_slice(&uniform_bytes[..]), )) } } -impl GroupWithMapToCurve for EdwardsPoint { - const SUITE_ID: usize = 0x0009; // FIXME, seemingly unsupported by VOPRF RFC? - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { - let (hashed_input, _) = Hkdf::::extract(Some(dst), msg); - Ok(::hash_to_curve( - &GenericArray::clone_from_slice(&hashed_input[..]), - )) - } -} - // Computes ceil(x / y) fn div_ceil(x: usize, y: usize) -> usize { let additive = (x % y != 0) as usize; diff --git a/src/messages.rs b/src/messages.rs index f04b279..109ce9f 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -15,12 +15,13 @@ use crate::{ group::Group, hash::Hash, key_exchange::traits::{KeyExchange, ToBytes}, - keypair::{KeyPair, SizedBytesExt}, + keypair::{Key, KeyPair, SizedBytesExt}, serialization::{serialize, tokenize}, }; use generic_array::{typenum::Unsigned, GenericArray}; use generic_bytes::SizedBytes; use std::convert::TryFrom; +use std::marker::PhantomData; // Messages // ========= @@ -150,44 +151,38 @@ where /// The final message from the client, containing sealed cryptographic /// identifiers -pub struct RegistrationUpload { +pub struct RegistrationUpload { /// The "envelope" generated by the user, containing sealed /// cryptographic identifiers pub(crate) envelope: Envelope, /// The user's public key - pub(crate) client_s_pk: KeyFormat::Repr, + pub(crate) client_s_pk: Key, + pub(crate) _g: PhantomData, } -impl TryFrom<&[u8]> for RegistrationUpload -where - KeyFormat: KeyPair, - D: Hash, -{ +impl TryFrom<&[u8]> for RegistrationUpload { type Error = ProtocolError; fn try_from(third_message_bytes: &[u8]) -> Result { - let key_len = ::Len::to_usize(); + let key_len = ::Len::to_usize(); let envelope_size = key_len + Envelope::::additional_size(); let checked_bytes = check_slice_size( third_message_bytes, envelope_size + key_len, "third_message", )?; - let unchecked_client_s_pk = KeyFormat::Repr::from_bytes(&checked_bytes[envelope_size..])?; - let client_s_pk = KeyFormat::check_public_key(unchecked_client_s_pk)?; + let unchecked_client_s_pk = Key::from_bytes(&checked_bytes[envelope_size..])?; + let client_s_pk = KeyPair::::check_public_key(unchecked_client_s_pk)?; Ok(Self { envelope: Envelope::::from_bytes(&checked_bytes[..envelope_size])?, client_s_pk, + _g: PhantomData, }) } } -impl RegistrationUpload -where - KeyFormat: KeyPair, - D: Hash, -{ +impl RegistrationUpload { /// Serialization into bytes pub fn serialize(&self) -> Vec { let mut message: Vec = Vec::new(); @@ -207,7 +202,8 @@ where Ok(Self { envelope, - client_s_pk: KeyFormat::check_public_key(KeyFormat::Repr::from_bytes(&client_s_pk)?)?, + client_s_pk: KeyPair::::check_public_key(Key::from_bytes(&client_s_pk)?)?, + _g: PhantomData, }) } } @@ -216,7 +212,7 @@ where pub struct CredentialRequest { /// blinded password information pub(crate) alpha: CS::Group, - pub(crate) ke1_message: >::KE1Message, + pub(crate) ke1_message: >::KE1Message, } impl TryFrom<&[u8]> for CredentialRequest { @@ -250,9 +246,7 @@ impl CredentialRequest { let alpha = ::from_element_slice(arr)?; let ke1_message = - >::KE1Message::try_from( - &ke1m[..], - )?; + >::KE1Message::try_from(&ke1m[..])?; Ok(Self { alpha, ke1_message }) } @@ -263,10 +257,10 @@ impl CredentialRequest { pub struct CredentialResponse { /// the server's oprf output pub(crate) beta: CS::Group, - pub(crate) server_s_pk: ::Repr, + pub(crate) server_s_pk: Key, /// the user's sealed information, pub(crate) envelope: Envelope, - pub(crate) ke2_message: >::KE2Message, + pub(crate) ke2_message: >::KE2Message, } impl CredentialResponse { @@ -308,12 +302,11 @@ impl TryFrom<&[u8]> for CredentialResponse { let (serialized_server_s_pk, remainder) = tokenize(&checked_slice[elem_len..], 2)?; let sized_server_s_pk = check_slice_size( &serialized_server_s_pk[..], - <::Repr as SizedBytes>::Len::to_usize(), + ::Len::to_usize(), "server_s_pk in credential_response", )?; - let unchecked_server_s_pk = - ::Repr::from_bytes(&sized_server_s_pk[..])?; - let server_s_pk = CS::KeyFormat::check_public_key(unchecked_server_s_pk)?; + let unchecked_server_s_pk = Key::from_bytes(&sized_server_s_pk[..])?; + let server_s_pk = KeyPair::::check_public_key(unchecked_server_s_pk)?; let (envelope, remainder) = Envelope::::deserialize(&remainder)?; @@ -321,7 +314,7 @@ impl TryFrom<&[u8]> for CredentialResponse { let checked_remainder = check_slice_size_atleast(&remainder, ke2_message_size, "login_second_message_bytes")?; let ke2_message = - >::KE2Message::try_from( + >::KE2Message::try_from( &checked_remainder, )?; @@ -337,7 +330,7 @@ impl TryFrom<&[u8]> for CredentialResponse { /// The answer sent by the client to the server, upon reception of the /// sealed envelope pub struct CredentialFinalization { - pub(crate) ke3_message: >::KE3Message, + pub(crate) ke3_message: >::KE3Message, } impl TryFrom<&[u8]> for CredentialFinalization { @@ -345,7 +338,7 @@ impl TryFrom<&[u8]> for CredentialFinalization { fn try_from(bytes: &[u8]) -> Result { let ke3_message = - >::KE3Message::try_from(bytes)?; + >::KE3Message::try_from(bytes)?; Ok(Self { ke3_message }) } } diff --git a/src/opaque.rs b/src/opaque.rs index 8275d10..de34493 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -12,7 +12,7 @@ use crate::{ group::Group, hash::Hash, key_exchange::traits::{KeyExchange, ToBytes}, - keypair::{KeyPair, SizedBytesExt}, + keypair::{Key, KeyPair, SizedBytesExt}, map_to_curve::GroupWithMapToCurve, oprf, serialization::serialize, @@ -116,9 +116,8 @@ impl ClientRegistration { /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -128,14 +127,8 @@ impl ClientRegistration { pub fn start( blinding_factor_rng: &mut R, password: &[u8], - #[cfg(test)] postprocess: fn(::Scalar) -> ::Scalar, ) -> Result, ProtocolError> { - let (token, alpha) = oprf::blind::( - &password, - blinding_factor_rng, - #[cfg(test)] - postprocess, - )?; + let (token, alpha) = oprf::blind::(&password, blinding_factor_rng)?; Ok(ClientRegistrationStartResult { message: RegistrationRequest:: { alpha }, @@ -145,11 +138,12 @@ impl ClientRegistration { } /// Contains the fields that are returned by a client registration finish -pub struct ClientRegistrationFinishResult { +pub struct ClientRegistrationFinishResult { /// The registration upload message to be sent to the server - pub message: RegistrationUpload, + pub message: RegistrationUpload, /// The export key output by client registration pub export_key: GenericArray, + _g: PhantomData, } impl ClientRegistration { @@ -162,7 +156,7 @@ impl ClientRegistration { /// # Example /// /// ``` - /// use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, keypair::X25519KeyPair}; + /// use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::KeyPair; /// use rand_core::{OsRng, RngCore}; @@ -170,14 +164,13 @@ impl ClientRegistration { /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// let mut server_rng = OsRng; - /// let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// let server_kp = Default::generate_random_keypair(&mut server_rng); /// let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; /// let server_registration_start_result = /// ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; @@ -190,12 +183,12 @@ impl ClientRegistration { rng: &mut R, r2: RegistrationResponse, params: ClientRegistrationFinishParameters, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> { let optional_ids = match params { ClientRegistrationFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)), ClientRegistrationFinishParameters::Default => None, }; - let client_static_keypair = CS::generate_random_keypair(rng)?; + let client_static_keypair = CS::generate_random_keypair(rng); let password_derived_key = get_password_derived_key::(&self.token, r2.beta)?; @@ -212,8 +205,10 @@ impl ClientRegistration { message: RegistrationUpload { envelope, client_s_pk: client_static_keypair.public().clone(), + _g: PhantomData, }, export_key, + _g: PhantomData, }) } } @@ -257,19 +252,11 @@ pub struct ServerRegistrationStartResult { /// The state elements the server holds to record a registration pub struct ServerRegistration { envelope: Option>, - client_s_pk: Option<::Repr>, + client_s_pk: Option, pub(crate) oprf_key: ::Scalar, } -impl TryFrom<&[u8]> for ServerRegistration -where - <::Repr as SizedBytes>::Len: - std::ops::Add<<::Repr as SizedBytes>::Len>, - generic_array::typenum::Sum< - <::Repr as SizedBytes>::Len, - <::Repr as SizedBytes>::Len, - >: generic_array::ArrayLength, -{ +impl TryFrom<&[u8]> for ServerRegistration { type Error = ProtocolError; /// The format of a serialized ServerRegistration object: @@ -285,17 +272,16 @@ where } // Need to do this check manually because envelope is variable-size - let key_len = <::Repr as SizedBytes>::Len::to_usize(); + let key_len = ::Len::to_usize(); let checked_bytes = check_slice_size_atleast(&input, scalar_len + key_len, "server_registration_bytes")?; let oprf_key_bytes = GenericArray::from_slice(&checked_bytes[..scalar_len]); let oprf_key = CS::Group::from_scalar_slice(oprf_key_bytes)?; - let unchecked_client_s_pk = ::Repr::from_bytes( - &checked_bytes[scalar_len..scalar_len + key_len], - )?; - let client_s_pk = CS::KeyFormat::check_public_key(unchecked_client_s_pk)?; + let unchecked_client_s_pk = + Key::from_bytes(&checked_bytes[scalar_len..scalar_len + key_len])?; + let client_s_pk = KeyPair::::check_public_key(unchecked_client_s_pk)?; let envelope = Envelope::::from_bytes(&checked_bytes[scalar_len + key_len..])?; @@ -307,15 +293,7 @@ where } } -impl ServerRegistration -where - <::Repr as SizedBytes>::Len: - std::ops::Add<<::Repr as SizedBytes>::Len>, - generic_array::typenum::Sum< - <::Repr as SizedBytes>::Len, - <::Repr as SizedBytes>::Len, - >: generic_array::ArrayLength, -{ +impl ServerRegistration { /// byte representation for the server's registration state pub fn to_bytes(&self) -> Vec { let mut output: Vec = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec(); @@ -337,21 +315,20 @@ where /// # Example /// /// ``` - /// use opaque_ke::{*, keypair::{KeyPair, X25519KeyPair}}; + /// use opaque_ke::*; /// # use opaque_ke::errors::ProtocolError; /// use rand_core::{OsRng, RngCore}; /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// let mut server_rng = OsRng; - /// let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// let server_kp = Default::generate_random_keypair(&mut server_rng); /// let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; /// let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; /// # Ok::<(), ProtocolError>(()) @@ -359,7 +336,7 @@ where pub fn start( rng: &mut R, message: RegistrationRequest, - server_s_pk: &::Repr, + server_s_pk: &Key, ) -> Result, ProtocolError> { // RFC: generate oprf_key (salt) and v_u = g^oprf_key let oprf_key = CS::Group::random_scalar(rng); @@ -389,21 +366,20 @@ where /// # Example /// /// ``` - /// use opaque_ke::{*, keypair::{KeyPair, X25519KeyPair}}; + /// use opaque_ke::{*, keypair::KeyPair}; /// # use opaque_ke::errors::ProtocolError; /// use rand_core::{OsRng, RngCore}; /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// let mut server_rng = OsRng; - /// let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// let server_kp = Default::generate_random_keypair(&mut server_rng); /// let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; /// let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; /// let mut client_rng = OsRng; @@ -413,7 +389,7 @@ where /// ``` pub fn finish( self, - message: RegistrationUpload, + message: RegistrationUpload, ) -> Result { Ok(Self { envelope: Some(message.envelope), @@ -430,7 +406,7 @@ where pub struct ClientLogin { /// token containing the client's password and the blinding factor token: oprf::Token, - ke1_state: >::KE1State, + ke1_state: >::KE1State, } impl TryFrom<&[u8]> for ClientLogin { @@ -438,7 +414,7 @@ impl TryFrom<&[u8]> for ClientLogin { fn try_from(input: &[u8]) -> Result { let scalar_len = ::ScalarLen::to_usize(); let ke1_state_size = - >::ke1_state_size(); + >::ke1_state_size(); let min_expected_len = scalar_len + ke1_state_size; let checked_slice = (if input.len() <= min_expected_len { @@ -453,10 +429,9 @@ impl TryFrom<&[u8]> for ClientLogin { let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]); let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?; - let ke1_state = - >::KE1State::try_from( - &checked_slice[scalar_len..scalar_len + ke1_state_size], - )?; + let ke1_state = >::KE1State::try_from( + &checked_slice[scalar_len..scalar_len + ke1_state_size], + )?; let password = input[scalar_len + ke1_state_size..].to_vec(); Ok(Self { token: oprf::Token { @@ -524,7 +499,7 @@ pub struct ClientLoginFinishResult { /// The client-side export key pub export_key: GenericArray, /// The server's static public key - pub server_s_pk: ::Repr, + pub server_s_pk: Key, /// The confidential info sent by the client pub confidential_info: Vec, } @@ -545,9 +520,8 @@ impl ClientLogin { /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; @@ -558,16 +532,10 @@ impl ClientLogin { rng: &mut R, password: &[u8], params: ClientLoginStartParameters, - #[cfg(test)] postprocess: fn(::Scalar) -> ::Scalar, ) -> Result, ProtocolError> { let ClientLoginStartParameters::WithInfo(info) = params; - let (token, alpha) = oprf::blind::( - &password, - rng, - #[cfg(test)] - postprocess, - )?; + let (token, alpha) = oprf::blind::(&password, rng)?; let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(alpha.to_arr().to_vec(), info, rng)?; @@ -592,21 +560,20 @@ impl ClientLogin { /// use opaque_ke::{ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters}; /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; - /// # use opaque_ke::keypair::{X25519KeyPair, KeyPair}; + /// # use opaque_ke::keypair::KeyPair; /// use rand_core::{OsRng, RngCore}; /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// # let mut server_rng = OsRng; /// # let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; - /// # let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// # let server_kp = Default::generate_random_keypair(&mut server_rng); /// # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; /// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; /// # let p_file = server_registration_start_result.state.finish(client_registration_finish_result.message)?; @@ -639,12 +606,11 @@ impl ClientLogin { err => PakeError::from(err), })?; - let client_s_sk = - ::Repr::from_bytes(&opened_envelope.client_s_sk)?; + let client_s_sk = Key::from_bytes(&opened_envelope.client_s_sk)?; let (id_u, id_s) = match optional_ids { None => ( - CS::KeyFormat::public_from_private(&client_s_sk) + KeyPair::::public_from_private(&client_s_sk) .to_arr() .to_vec(), server_s_pk_bytes.clone(), @@ -681,7 +647,7 @@ impl ClientLogin { /// The state elements the server holds to record a login pub struct ServerLogin { - ke2_state: >::KE2State, + ke2_state: >::KE2State, _cs: PhantomData, } @@ -690,10 +656,9 @@ impl TryFrom<&[u8]> for ServerLogin { fn try_from(bytes: &[u8]) -> Result { Ok(Self { _cs: PhantomData, - ke2_state: - >::KE2State::try_from( - bytes, - )?, + ke2_state: >::KE2State::try_from( + bytes, + )?, }) } } @@ -750,20 +715,19 @@ impl ServerLogin { /// use opaque_ke::{ClientLogin, ClientLoginStartParameters, ServerLogin, ServerLoginStartParameters}; /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; - /// # use opaque_ke::keypair::{KeyPair, X25519KeyPair}; + /// # use opaque_ke::keypair::KeyPair; /// use rand_core::{OsRng, RngCore}; /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// let mut server_rng = OsRng; - /// let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// let server_kp = Default::generate_random_keypair(&mut server_rng); /// # let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; /// # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; /// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; @@ -775,7 +739,7 @@ impl ServerLogin { pub fn start( rng: &mut R, password_file: ServerRegistration, - server_s_sk: &::Repr, + server_s_sk: &Key, l1: CredentialRequest, params: ServerLoginStartParameters, ) -> Result, ProtocolError> { @@ -801,7 +765,7 @@ impl ServerLogin { let (id_u, id_s) = match optional_ids { None => ( client_s_pk.to_arr().to_vec(), - CS::KeyFormat::public_from_private(server_s_sk) + KeyPair::::public_from_private(server_s_sk) .to_arr() .to_vec(), ), @@ -811,7 +775,7 @@ impl ServerLogin { let l1_bytes = &l1.to_bytes(); let beta = oprf::evaluate(l1.alpha, &password_file.oprf_key); - let server_s_pk = CS::KeyFormat::public_from_private(&server_s_sk); + let server_s_pk = KeyPair::::public_from_private(&server_s_sk); let l2_component: Vec = [ serialize(&beta.to_arr()[..], 2), @@ -861,20 +825,19 @@ impl ServerLogin { /// use opaque_ke::{ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ServerLogin, ServerLoginStartParameters}; /// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; - /// # use opaque_ke::keypair::{KeyPair, X25519KeyPair}; + /// # use opaque_ke::keypair::KeyPair; /// use rand_core::{OsRng, RngCore}; /// use opaque_ke::ciphersuite::CipherSuite; /// struct Default; /// impl CipherSuite for Default { /// 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 Hash = sha2::Sha512; /// type SlowHash = opaque_ke::slow_hash::NoOpHash; /// } /// let mut client_rng = OsRng; /// let mut server_rng = OsRng; - /// let server_kp = Default::generate_random_keypair(&mut server_rng)?; + /// let server_kp = Default::generate_random_keypair(&mut server_rng); /// # let client_registration_start_result = ClientRegistration::::start(&mut client_rng, b"hunter2")?; /// # let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; /// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; @@ -889,7 +852,7 @@ impl ServerLogin { &self, message: CredentialFinalization, ) -> Result { - let shared_secret = >::finish_ke( + let shared_secret = >::finish_ke( message.ke3_message, &self.ke2_state, ) diff --git a/src/oprf.rs b/src/oprf.rs index 0cac7a2..7027277 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -28,16 +28,10 @@ static MODE_BASE: u8 = 0x00; pub(crate) fn blind( input: &[u8], blinding_factor_rng: &mut R, - #[cfg(test)] postprocess: fn(G::Scalar) -> G::Scalar, ) -> Result<(Token, G), InternalPakeError> { + let blind = G::random_scalar(blinding_factor_rng); let dst = [STR_VOPRF, &G::get_context_string(MODE_BASE)].concat(); let mapped_point = G::map_to_curve::(input, &dst)?; - let blinding_factor = G::random_scalar(blinding_factor_rng); - #[cfg(test)] - let blind = postprocess(blinding_factor); - #[cfg(not(test))] - let blind = blinding_factor; - let blind_token = mapped_point * &blind; Ok(( Token { @@ -88,12 +82,7 @@ pub fn blind_shim( input: &[u8], blinding_factor_rng: &mut R, ) -> Result<(Token, G), InternalPakeError> { - blind::( - input, - blinding_factor_rng, - #[cfg(test)] - std::convert::identity, - ) + blind::(input, blinding_factor_rng) } #[cfg(feature = "bench")] @@ -129,27 +118,23 @@ mod tests { use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::{arr, GenericArray}; use rand_core::OsRng; - use sha2::Sha256; + use sha2::Sha512; - fn prf( - input: &[u8], - oprf_key: &[u8; 32], - ) -> GenericArray::ElemLen> { + fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray::OutputSize> { let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat(); - let point = RistrettoPoint::map_to_curve::(input, &dst).unwrap(); + let point = RistrettoPoint::map_to_curve::(input, &dst).unwrap(); let scalar = RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); let res = point * scalar; - finalize::(&input, &res.to_arr().to_vec(), b"") + finalize::(&input, &res.to_arr().to_vec(), b"") } #[test] fn oprf_retrieval() -> Result<(), InternalPakeError> { let input = b"hunter2"; let mut rng = OsRng; - let (token, alpha) = - blind::<_, RistrettoPoint, Sha256>(&input[..], &mut rng, std::convert::identity)?; + let (token, alpha) = blind::<_, RistrettoPoint, Sha512>(&input[..], &mut rng)?; let oprf_key_bytes = arr![ u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -157,7 +142,7 @@ mod tests { let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes)?; let beta = evaluate::(alpha, &oprf_key); let res = - finalize::(&token.data, &unblind(&token, beta), b""); + finalize::(&token.data, &unblind(&token, beta), b""); let res2 = prf(&input[..], &oprf_key.as_bytes()); assert_eq!(res, res2); Ok(()) @@ -168,14 +153,13 @@ mod tests { let mut rng = OsRng; let mut input = vec![0u8; 64]; rng.fill_bytes(&mut input); - let (token, alpha) = - blind::<_, RistrettoPoint, Sha256>(&input, &mut rng, std::convert::identity).unwrap(); + let (token, alpha) = blind::<_, RistrettoPoint, sha2::Sha512>(&input, &mut rng).unwrap(); let res = - finalize::(&token.data, &unblind(&token, alpha), b""); + finalize::(&token.data, &unblind(&token, alpha), b""); let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat(); - let point = RistrettoPoint::map_to_curve::(&input, &dst).unwrap(); - let res2 = finalize::(&input, &point.to_arr().to_vec(), b""); + let point = RistrettoPoint::map_to_curve::(&input, &dst).unwrap(); + let res2 = finalize::(&input, &point.to_arr().to_vec(), b""); assert_eq!(res, res2); } diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index 7132398..83cb7b2 100644 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -11,7 +11,6 @@ use crate::{ traits::{KeyExchange, ToBytes}, tripledh::{TripleDH, NONCE_LEN}, }, - keypair::{KeyPair, X25519KeyPair}, opaque::*, serialization::{i2osp, os2ip, serialize}, *, @@ -22,19 +21,19 @@ use generic_bytes::SizedBytes; use proptest::{collection::vec, prelude::*}; use rand_core::{OsRng, RngCore}; -use sha2::{Digest, Sha256}; +use sha2::{Digest, Sha512}; 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 Hash = sha2::Sha512; type SlowHash = crate::slow_hash::NoOpHash; } const MAX_INFO_LENGTH: usize = 10; +const MAC_SIZE: usize = 64; // Because of SHA512 fn random_ristretto_point() -> RistrettoPoint { let mut rng = OsRng; @@ -83,11 +82,11 @@ fn server_registration_roundtrip() { mock_envelope_bytes.extend_from_slice(&[0; NONCE_LEN]); // empty nonce mock_envelope_bytes.extend_from_slice(&[0, 0]); // empty ciphertext mock_envelope_bytes.extend_from_slice(&[0, 0]); // empty auth_data - // length-32 hmac - mock_envelope_bytes.extend_from_slice(&[0, 32]); - mock_envelope_bytes.extend_from_slice(&[0; 32]); + // length-MAC_SIZE hmac + mock_envelope_bytes.extend_from_slice(&[0, MAC_SIZE as u8]); + mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); - let mock_client_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let mock_client_kp = Default::generate_random_keypair(&mut rng); // serialization order: oprf_key, public key, envelope let mut bytes = Vec::::new(); bytes.extend_from_slice(oprf_key.as_bytes()); @@ -119,7 +118,7 @@ fn register_second_message_roundtrip() { let pt = random_ristretto_point(); let beta_bytes = pt.to_arr(); let mut rng = OsRng; - let skp = Default::generate_random_keypair(&mut rng).unwrap(); + let skp = Default::generate_random_keypair(&mut rng); let pubkey_bytes = skp.public().to_arr(); let beta_length: usize = beta_bytes.len(); @@ -139,7 +138,7 @@ fn register_second_message_roundtrip() { #[test] fn register_third_message_roundtrip() { let mut rng = OsRng; - let skp = Default::generate_random_keypair(&mut rng).unwrap(); + let skp = Default::generate_random_keypair(&mut rng); let pubkey_bytes = skp.public().to_arr(); let mut key = [0u8; 32]; @@ -148,7 +147,7 @@ fn register_third_message_roundtrip() { let mut msg = [0u8; 32]; rng.fill_bytes(&mut msg); - let (envelope, _) = Envelope::::seal_raw( + let (envelope, _) = Envelope::::seal_raw( &mut rng, &key, &msg, @@ -165,7 +164,7 @@ fn register_third_message_roundtrip() { input.extend_from_slice(&pubkey_length.to_be_bytes()[std::mem::size_of::() - 2..]); input.extend_from_slice(&pubkey_bytes[..]); - let r3 = RegistrationUpload::::deserialize(&input[..]).unwrap(); + let r3 = RegistrationUpload::::deserialize(&input[..]).unwrap(); let r3_bytes = r3.serialize(); assert_eq!(input, r3_bytes); } @@ -176,7 +175,7 @@ fn login_first_message_roundtrip() { let alpha = random_ristretto_point(); let alpha_bytes = alpha.to_arr().to_vec(); - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let client_e_kp = Default::generate_random_keypair(&mut rng); let mut client_nonce = [0u8; NONCE_LEN]; rng.fill_bytes(&mut client_nonce); @@ -208,7 +207,7 @@ fn login_second_message_roundtrip() { let pt_bytes = pt.to_arr().to_vec(); let mut rng = OsRng; - let skp = Default::generate_random_keypair(&mut rng).unwrap(); + let skp = Default::generate_random_keypair(&mut rng); let pubkey_bytes = skp.public().to_arr(); let pubkey_length: usize = pubkey_bytes.len(); @@ -218,7 +217,7 @@ fn login_second_message_roundtrip() { let mut msg = [0u8; 32]; rng.fill_bytes(&mut msg); - let (envelope, _) = Envelope::::seal_raw( + let (envelope, _) = Envelope::::seal_raw( &mut rng, &key, &msg, @@ -227,8 +226,8 @@ fn login_second_message_roundtrip() { ) .unwrap(); - let server_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut mac = [0u8; 32]; + let server_e_kp = Default::generate_random_keypair(&mut rng); + let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let mut server_nonce = [0u8; NONCE_LEN]; rng.fill_bytes(&mut server_nonce); @@ -260,7 +259,7 @@ fn login_second_message_roundtrip() { #[test] fn login_third_message_roundtrip() { let mut rng = OsRng; - let mut mac = [0u8; 32]; + let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let input: Vec = [&mac[..]].concat(); @@ -276,12 +275,12 @@ fn client_login_roundtrip() { let mut rng = OsRng; let sc = ::random_scalar(&mut rng); - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let client_e_kp = Default::generate_random_keypair(&mut rng); 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(); + let mut hasher = Sha512::new(); hasher.update(l1_data); let hashed_l1 = hasher.finalize(); @@ -303,7 +302,7 @@ fn client_login_roundtrip() { fn ke1_message_roundtrip() { let mut rng = OsRng; - let client_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); + let client_e_kp = Default::generate_random_keypair(&mut rng); let mut client_nonce = [0u8; NONCE_LEN]; rng.fill_bytes(&mut client_nonce); @@ -316,11 +315,9 @@ fn ke1_message_roundtrip() { &client_e_kp.public(), ] .concat(); - let reg = >::KE1Message::try_from(&ke1m[..]) - .unwrap(); + let reg = + >::KE1Message::try_from(&ke1m[..]) + .unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke1m); } @@ -329,8 +326,8 @@ fn ke1_message_roundtrip() { fn ke2_message_roundtrip() { let mut rng = OsRng; - let server_e_kp = Default::generate_random_keypair(&mut rng).unwrap(); - let mut mac = [0u8; 32]; + let server_e_kp = Default::generate_random_keypair(&mut rng); + let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let mut server_nonce = [0u8; NONCE_LEN]; rng.fill_bytes(&mut server_nonce); @@ -345,11 +342,9 @@ fn ke2_message_roundtrip() { ] .concat(); - let reg = >::KE2Message::try_from(&ke2m[..]) - .unwrap(); + let reg = + >::KE2Message::try_from(&ke2m[..]) + .unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke2m); } @@ -357,16 +352,14 @@ fn ke2_message_roundtrip() { #[test] fn ke3_message_roundtrip() { let mut rng = OsRng; - let mut mac = [0u8; 32]; + let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let ke3m: Vec = [&mac[..]].concat(); - let reg = >::KE3Message::try_from(&ke3m[..]) - .unwrap(); + let reg = + >::KE3Message::try_from(&ke3m[..]) + .unwrap(); let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke3m); } @@ -390,7 +383,7 @@ fn test_nocrash_register_second_message(bytes in vec(any::(), 0..200)) { #[test] fn test_nocrash_register_third_message(bytes in vec(any::(), 0..200)) { - RegistrationUpload::::try_from(&bytes[..]).map_or(true, |_| true); + RegistrationUpload::::try_from(&bytes[..]).map_or(true, |_| true); } #[test] diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index e8d7b26..d65e213 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -8,14 +8,13 @@ use crate::{ errors::*, group::Group, key_exchange::tripledh::{TripleDH, NONCE_LEN}, - keypair::{Key, KeyPair, X25519KeyPair}, + keypair::Key, opaque::*, slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, }; -use curve25519_dalek::edwards::EdwardsPoint; -use generic_array::GenericArray; +use curve25519_dalek::ristretto::RistrettoPoint; use generic_bytes::SizedBytes; use rand_core::{OsRng, RngCore}; use serde_json::Value; @@ -24,12 +23,11 @@ use std::convert::TryFrom; // Tests // ===== -struct X255193dhNoSlowHash; -impl CipherSuite for X255193dhNoSlowHash { - type Group = EdwardsPoint; - type KeyFormat = X25519KeyPair; +struct RistrettoSha5123dhNoSlowHash; +impl CipherSuite for RistrettoSha5123dhNoSlowHash { + type Group = RistrettoPoint; type KeyExchange = TripleDH; - type Hash = sha2::Sha256; + type Hash = sha2::Sha512; type SlowHash = NoOpHash; } @@ -69,37 +67,37 @@ pub struct TestVectorParameters { static TEST_VECTOR: &str = r#" { - "client_s_pk": "d762053e2da32c990b1edb22408138369282462feeaa68fc0acfd157c2745740", - "client_s_sk": "b06303fb12bde8fb2875ffc052c0c0cdb4bbef7b6a32ec9bb4a00a3a56fb9545", - "client_e_pk": "0375e9aa445b859a02e9ccacd45772758e560f8640ee067319a86374cd93a435", - "client_e_sk": "c835f5854f1651c8551e24aee6ab1b81bf44e4cb906d0ac9fcf1aaa26ef3b872", - "server_s_pk": "b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f33136", - "server_s_sk": "b81e81698a315f4ee817e5c9bd4426db1bf8dd9fec2e6fc82639d08d90509e6f", - "server_e_pk": "6ce63bc56b7b2141680b4fc4c8d3b4d09b903c5a2d657fc79432c586d0e9bd64", - "server_e_sk": "a0f3efd594b41deaba4480cae066c658529b90754f5109f9b0b61d42266f234e", + "client_s_pk": "64b071d0b8eccc39673b16384e86df49f258a12bad24f21f5d5f4a9a8f45561c", + "client_s_sk": "98a23d0757ac5c7aba8b218c5643c3a3029fb815a656de24cfc05df877925809", + "client_e_pk": "3a575317d7a6269bc4e76074ec906d8886b4c135fb6b590972f6fdd6c1a55676", + "client_e_sk": "163b91328785a5e84e21579e941be1dd7bdbc44d4c959f603d7f79417ab6b201", + "server_s_pk": "fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d75", + "server_s_sk": "f7068582fdf2a57f7da63e6f5142abda805a7b508ce171c84f58a4e5e634a406", + "server_e_pk": "8e32a64f670b9d2115eba0006b00667972523bf2f8686933ef2a2819d6b5a13b", + "server_e_sk": "a3a06d81eeb60aeedaee96769149ae5f45184eec66e1b2bdeb04114d27019206", "id_u": "696455", "id_s": "696453", "password": "70617373776f7264", - "blinding_factor": "78b54192e145ff02458385f8540fdf54e94c2a20f0a7f8f98944bc17af795e04", - "oprf_key": "98a9232ccfda91d14ea305e5cfb4e552ab8aa972ac6c79befe15ccf9f4f2970a", - "envelope_nonce": "46c395bdc879bfebae229024f0004721448e713643a341146310d50c84cef011", - "client_nonce": "197b1147bb214fafde9f5ce6f5e903d69be0ca006c51ac8f949ce7ab73a828ed", - "server_nonce": "73a56c702168d9d534429fd45c37390ed6f55b6ea79f2025e4584a04d35aa229", + "blinding_factor": "3497d8c6728a3dc0873e363dcd60acd95f7897d15d33cdf1d49c8b856f061900", + "oprf_key": "c728d1ea06fb94f577ef63d50d1645e9fea14575987971e4a64e05dda367c703", + "envelope_nonce": "d7d138a3a4cb796662814391f5221e05b66438a92e5f57cc9624e6227da34eab", + "client_nonce": "ad87ae4609f1f56aeb1de0f4760120fa75ceff39751dffc35d25ff3f2896bb60", + "server_nonce": "d7a8cd7cd2c4f0f7650390f6f2d4858ae99ac79f2769a9e15583d570b11c434c", "info1": "696e666f31", "einfo2": "65696e666f32", - "registration_request": "0020ed5ca18ae23e622694611af62744f21c70f68d495ce36ae17784f03d225b573c", - "registration_response": "002078b592b789e7239481637419438333cbdcd3dd909534ac28e5473683d023719d0020b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f33136", - "registration_upload": "0146c395bdc879bfebae229024f0004721448e713643a341146310d50c84cef0110022f6906b64d3d45c4d77dd8c002b841749f716efc9e3e2cff762004b4878e75c0a7abe002c0020b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f33136000369645500036964530020e4444a287fc0bc8921d0c6423d36948fa176f68671196592cb947984b082bacd0020d762053e2da32c990b1edb22408138369282462feeaa68fc0acfd157c2745740", - "credential_request": "0020ed5ca18ae23e622694611af62744f21c70f68d495ce36ae17784f03d225b573c197b1147bb214fafde9f5ce6f5e903d69be0ca006c51ac8f949ce7ab73a828ed0005696e666f310375e9aa445b859a02e9ccacd45772758e560f8640ee067319a86374cd93a435", - "credential_response": "002078b592b789e7239481637419438333cbdcd3dd909534ac28e5473683d023719d0020b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f331360146c395bdc879bfebae229024f0004721448e713643a341146310d50c84cef0110022f6906b64d3d45c4d77dd8c002b841749f716efc9e3e2cff762004b4878e75c0a7abe002c0020b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f33136000369645500036964530020e4444a287fc0bc8921d0c6423d36948fa176f68671196592cb947984b082bacda0f3efd594b41deaba4480cae066c658529b90754f5109f9b0b61d42266f234e6ce63bc56b7b2141680b4fc4c8d3b4d09b903c5a2d657fc79432c586d0e9bd6400061e51f6093a7669db4e4a493137440cada18dcff631462e349de1d043a14a7a1de77b85638fbd", - "key_exchange": "11d1a3f15b490c059bfa06f7d0fae9cde9ff6af80270f3327161a362ec84570e", - "client_registration_state": "78b54192e145ff02458385f8540fdf54e94c2a20f0a7f8f98944bc17af795e0470617373776f7264", - "client_login_state": "78b54192e145ff02458385f8540fdf54e94c2a20f0a7f8f98944bc17af795e04c835f5854f1651c8551e24aee6ab1b81bf44e4cb906d0ac9fcf1aaa26ef3b872197b1147bb214fafde9f5ce6f5e903d69be0ca006c51ac8f949ce7ab73a828ed3a139e3e250129a1d3e7633052f853006a501e24b1d3c6ba6403aeb9cca7eda170617373776f7264", - "server_registration_state": "98a9232ccfda91d14ea305e5cfb4e552ab8aa972ac6c79befe15ccf9f4f2970a", - "server_login_state": "8be81bab99a1ce566adb4f33d14b012a8583f0ecf8b467bad1930a6adbf7178dcc8daa2de4be476dde1d8193f9fe7786601d7db0af7302c19501b516ad5e53329706a48a68a6bbf3dea894447a53b423fc5b5e561c9c3fa9b1b278d6790bbb74", - "password_file": "98a9232ccfda91d14ea305e5cfb4e552ab8aa972ac6c79befe15ccf9f4f2970ad762053e2da32c990b1edb22408138369282462feeaa68fc0acfd157c27457400146c395bdc879bfebae229024f0004721448e713643a341146310d50c84cef0110022f6906b64d3d45c4d77dd8c002b841749f716efc9e3e2cff762004b4878e75c0a7abe002c0020b7d6d756fb2b3972125245f53c042c53c8b3bf5e9d2b576809548c5510f33136000369645500036964530020e4444a287fc0bc8921d0c6423d36948fa176f68671196592cb947984b082bacd", - "export_key": "3cdf9ad930b46fad7855faab02a7f2e28282cc73f82fdd411f1a7f6c300c8ab1", - "shared_secret": "9706a48a68a6bbf3dea894447a53b423fc5b5e561c9c3fa9b1b278d6790bbb74" + "registration_request": "002096738fbc9883a85a8067763abe33efadce0adb2bc64857d3cd0a6cc92e9ce325", + "registration_response": "0020a86baffac435f6239cfe5f61d823e7458ac211dce1d3c364375e9d1f40843c750020fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d75", + "registration_upload": "01d7d138a3a4cb796662814391f5221e05b66438a92e5f57cc9624e6227da34eab002229979df679067f8baecbbe063ee97e2b5be88c2b99163d595b32a74d640138771d70002c0020fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d750003696455000369645300400b6119d0de405d85e0e6b1748eb66194b1768ea71fd47c103f8e1fcbc837add2b282889469921798d626a02b6dd763ee9cd028f333386a19c13a3d4d4c6a3312002064b071d0b8eccc39673b16384e86df49f258a12bad24f21f5d5f4a9a8f45561c", + "credential_request": "002096738fbc9883a85a8067763abe33efadce0adb2bc64857d3cd0a6cc92e9ce325ad87ae4609f1f56aeb1de0f4760120fa75ceff39751dffc35d25ff3f2896bb600005696e666f313a575317d7a6269bc4e76074ec906d8886b4c135fb6b590972f6fdd6c1a55676", + "credential_response": "0020a86baffac435f6239cfe5f61d823e7458ac211dce1d3c364375e9d1f40843c750020fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d7501d7d138a3a4cb796662814391f5221e05b66438a92e5f57cc9624e6227da34eab002229979df679067f8baecbbe063ee97e2b5be88c2b99163d595b32a74d640138771d70002c0020fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d750003696455000369645300400b6119d0de405d85e0e6b1748eb66194b1768ea71fd47c103f8e1fcbc837add2b282889469921798d626a02b6dd763ee9cd028f333386a19c13a3d4d4c6a3312a3a06d81eeb60aeedaee96769149ae5f45184eec66e1b2bdeb04114d270192068e32a64f670b9d2115eba0006b00667972523bf2f8686933ef2a2819d6b5a13b00066c7c22ba8dbae814beb02d1b89659379ca61697cd3212169d387b61f5659b77f6993833a1542a692cb5ad259244a1d53a51243fb5366a5bd8e4ed7eb2f50caa9a568fc8267e9", + "key_exchange": "3f9d3754920514328529bf4e78d4c2d0f411dfb871a4ef83c87d3462119b836381bdc651096cb62ca96de5f4b5a9acaa87b180d2acefebd2f3feb5785adcd038", + "client_registration_state": "3497d8c6728a3dc0873e363dcd60acd95f7897d15d33cdf1d49c8b856f06190070617373776f7264", + "client_login_state": "3497d8c6728a3dc0873e363dcd60acd95f7897d15d33cdf1d49c8b856f061900163b91328785a5e84e21579e941be1dd7bdbc44d4c959f603d7f79417ab6b201ad87ae4609f1f56aeb1de0f4760120fa75ceff39751dffc35d25ff3f2896bb607962b421c96efcfdb465365eea8fe9914e6ac5c0b216e2b07c7fb3b17fa0d21ee8c37373fee8dfec6a5eae559f21c8c4e1505ed974ce8baff3529304cb4d90b570617373776f7264", + "server_registration_state": "c728d1ea06fb94f577ef63d50d1645e9fea14575987971e4a64e05dda367c703", + "server_login_state": "644fb6d36a91e0197f5d82d17f04354f9e24794988923160534d99c80970b66fe54a3d8b36d5a4d21eaf0fa7bb4717ed1741be3f7d24b96d27aeb7f4b59a72bf80d0d969a2398ea5a5232d3257c60516dcc0de65b66fc5a07b8e2f58523eb0079ac09798a23cee0e31454594d25e40f34b10b92195fea6f90e197c0fbc8658bb3435bb6179370029f03d2dbd5efa2d0f8b8f4f69ca42ef70245686f70c2d7f326d0a8bc596145099e6a668d23ce1a93f9a3f83bfdddde15c78f467a639f124dd", + "password_file": "c728d1ea06fb94f577ef63d50d1645e9fea14575987971e4a64e05dda367c70364b071d0b8eccc39673b16384e86df49f258a12bad24f21f5d5f4a9a8f45561c01d7d138a3a4cb796662814391f5221e05b66438a92e5f57cc9624e6227da34eab002229979df679067f8baecbbe063ee97e2b5be88c2b99163d595b32a74d640138771d70002c0020fc0d7d59e0fabe7c876c01c8d5408fc60dc5fdec7b89acdc5af2bc7c0de26d750003696455000369645300400b6119d0de405d85e0e6b1748eb66194b1768ea71fd47c103f8e1fcbc837add2b282889469921798d626a02b6dd763ee9cd028f333386a19c13a3d4d4c6a3312", + "export_key": "005ba69bed794d3e24ac750ed575165a0235e103d1f8d7ca1b9aa0cfc2c3d9b6", + "shared_secret": "3435bb6179370029f03d2dbd5efa2d0f8b8f4f69ca42ef70245686f70c2d7f326d0a8bc596145099e6a668d23ce1a93f9a3f83bfdddde15c78f467a639f124dd" } "#; @@ -255,30 +253,17 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String { s } -fn generate_parameters() -> TestVectorParameters -where - // Unsightly constraints due to the (required) use of the SizedBytes - // instance for KP in ServerRegistration::start. See also the impl - // Tryfrom<&[u8]> for ServerRegistration (those are the same constraints). - <::Repr as SizedBytes>::Len: - std::ops::Add<<::Repr as SizedBytes>::Len>, - generic_array::typenum::Sum< - <::Repr as SizedBytes>::Len, - <::Repr as SizedBytes>::Len, - >: generic_array::ArrayLength, -{ +fn generate_parameters() -> TestVectorParameters { let mut rng = OsRng; // Inputs - let server_s_kp = CS::generate_random_keypair(&mut rng).unwrap(); - let server_e_kp = CS::generate_random_keypair(&mut rng).unwrap(); - let client_s_kp = CS::generate_random_keypair(&mut rng).unwrap(); - let client_e_kp = CS::generate_random_keypair(&mut rng).unwrap(); + let server_s_kp = CS::generate_random_keypair(&mut rng); + let server_e_kp = CS::generate_random_keypair(&mut rng); + let client_s_kp = CS::generate_random_keypair(&mut rng); + let client_e_kp = CS::generate_random_keypair(&mut rng); let id_u = b"idU"; let id_s = b"idS"; let password = b"password"; - let mut blinding_factor_raw = [0u8; 64]; - rng.fill_bytes(&mut blinding_factor_raw); let mut oprf_key_raw = [0u8; 32]; rng.fill_bytes(&mut oprf_key_raw); let mut envelope_nonce = [0u8; 32]; @@ -288,22 +273,26 @@ where let mut server_nonce = [0u8; NONCE_LEN]; rng.fill_bytes(&mut server_nonce); + let blinding_factor = CS::Group::random_scalar(&mut rng); + let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone(); + let info1 = b"info1"; let einfo2 = b"einfo2"; - let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_raw.to_vec()); - let client_registration_start_result = ClientRegistration::::start( - &mut blinding_factor_registration_rng, - password, - std::convert::identity, - ) - .unwrap(); + let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec()); + let client_registration_start_result = + ClientRegistration::::start(&mut blinding_factor_registration_rng, password).unwrap(); + let blinding_factor_bytes_returned = + CS::Group::scalar_as_bytes(&client_registration_start_result.state.token.blind).clone(); + assert_eq!( + hex::encode(&blinding_factor_bytes), + hex::encode(&blinding_factor_bytes_returned) + ); + let registration_request_bytes = client_registration_start_result .message .serialize() .to_vec(); - let blinding_factor_bytes = - CS::Group::scalar_as_bytes(&client_registration_start_result.state.token.blind).clone(); let client_registration_state = client_registration_start_result.state.to_bytes().to_vec(); let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec()); @@ -346,7 +335,7 @@ where let password_file_bytes = password_file.to_bytes(); let mut client_login_start: Vec = Vec::new(); - client_login_start.extend_from_slice(&blinding_factor_raw); + client_login_start.extend_from_slice(&blinding_factor_bytes); client_login_start.extend_from_slice(&client_e_kp.private().to_arr()); client_login_start.extend_from_slice(&client_nonce); @@ -355,7 +344,6 @@ where &mut client_login_start_rng, password, ClientLoginStartParameters::WithInfo(info1.to_vec()), - std::convert::identity, ) .unwrap(); let credential_request_bytes = client_login_start_result.message.serialize().to_vec(); @@ -423,25 +411,16 @@ where #[test] fn generate_test_vectors() { - let parameters = generate_parameters::(); + let parameters = generate_parameters::(); println!("{}", stringify_test_vectors(¶meters)); } -// For fixing the blinding factor -fn postprocess_blinding_factor(_: G::Scalar) -> G::Scalar { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - G::from_scalar_slice(GenericArray::from_slice(¶meters.blinding_factor[..])).unwrap() -} - #[test] fn test_registration_request() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let mut rng = OsRng; - let client_registration_start_result = ClientRegistration::::start( - &mut rng, - ¶meters.password, - postprocess_blinding_factor::<::Group>, - )?; + let mut rng = CycleRng::new(parameters.blinding_factor.to_vec()); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.registration_request), hex::encode(client_registration_start_result.message.serialize()) @@ -457,11 +436,12 @@ fn test_registration_request() -> Result<(), ProtocolError> { fn test_registration_response() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut oprf_key_rng = CycleRng::new(parameters.oprf_key); - let server_registration_start_result = ServerRegistration::::start( - &mut oprf_key_rng, - RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(), - &Key::try_from(¶meters.server_s_pk[..]).unwrap(), - )?; + let server_registration_start_result = + ServerRegistration::::start( + &mut oprf_key_rng, + RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(), + &Key::try_from(¶meters.server_s_pk[..]).unwrap(), + )?; assert_eq!( hex::encode(parameters.registration_response), hex::encode(server_registration_start_result.message.serialize()) @@ -480,7 +460,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> { let client_s_sk_and_nonce: Vec = [parameters.client_s_sk, parameters.envelope_nonce].concat(); let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); - let result = ClientRegistration::::try_from( + let result = ClientRegistration::::try_from( ¶meters.client_registration_state[..], )? .finish( @@ -505,7 +485,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> { fn test_password_file() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let server_registration = ServerRegistration::::try_from( + let server_registration = ServerRegistration::::try_from( ¶meters.server_registration_state[..], )?; let password_file = server_registration @@ -522,18 +502,17 @@ fn test_password_file() -> Result<(), ProtocolError> { fn test_credential_request() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let client_login_start = [ - vec![0u8; 64], // FIXME: don't hardcode this + let client_login_start_rng = [ + parameters.blinding_factor, parameters.client_e_sk, parameters.client_nonce, ] .concat(); - let mut client_login_start_rng = CycleRng::new(client_login_start); - let client_login_start_result = ClientLogin::::start( + let mut client_login_start_rng = CycleRng::new(client_login_start_rng); + let client_login_start_result = ClientLogin::::start( &mut client_login_start_rng, ¶meters.password, ClientLoginStartParameters::WithInfo(parameters.info1), - postprocess_blinding_factor::<::Group>, )?; assert_eq!( hex::encode(¶meters.credential_request), @@ -551,12 +530,14 @@ fn test_credential_response() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let mut server_e_sk_rng = CycleRng::new(parameters.server_e_sk); - let server_login_start_result = ServerLogin::::start( + let server_login_start_result = ServerLogin::::start( &mut server_e_sk_rng, ServerRegistration::try_from(¶meters.password_file[..]).unwrap(), &Key::try_from(¶meters.server_s_sk[..]).unwrap(), - CredentialRequest::::deserialize(¶meters.credential_request[..]) - .unwrap(), + CredentialRequest::::deserialize( + ¶meters.credential_request[..], + ) + .unwrap(), ServerLoginStartParameters::WithInfoAndIdentifiers( parameters.einfo2.to_vec(), parameters.id_u, @@ -583,10 +564,10 @@ fn test_key_exchange() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let client_login_finish_result = - ClientLogin::::try_from(¶meters.client_login_state[..]) + ClientLogin::::try_from(¶meters.client_login_state[..]) .unwrap() .finish( - CredentialResponse::::deserialize( + CredentialResponse::::deserialize( ¶meters.credential_response[..], )?, ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), @@ -621,9 +602,10 @@ fn test_server_login_finish() -> Result<(), ProtocolError> { let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let server_login_result = - ServerLogin::::try_from(¶meters.server_login_state[..])?.finish( - CredentialFinalization::try_from(¶meters.key_exchange[..])?, - )?; + ServerLogin::::try_from(¶meters.server_login_state[..])? + .finish(CredentialFinalization::try_from( + ¶meters.key_exchange[..], + )?)?; assert_eq!( hex::encode(parameters.shared_secret), @@ -639,17 +621,18 @@ fn test_complete_flow( ) -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_kp = X255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?; - let client_registration_start_result = ClientRegistration::::start( - &mut client_rng, - registration_password, - std::convert::identity, - )?; - let server_registration_start_result = ServerRegistration::::start( - &mut server_rng, - client_registration_start_result.message, - server_kp.public(), - )?; + let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start( + &mut client_rng, + registration_password, + )?; + let server_registration_start_result = + ServerRegistration::::start( + &mut server_rng, + client_registration_start_result.message, + server_kp.public(), + )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, @@ -658,13 +641,12 @@ fn test_complete_flow( let p_file = server_registration_start_result .state .finish(client_registration_finish_result.message)?; - let client_login_start_result = ClientLogin::::start( + let client_login_start_result = ClientLogin::::start( &mut client_rng, login_password, ClientLoginStartParameters::default(), - std::convert::identity, )?; - let server_login_start_result = ServerLogin::::start( + let server_login_start_result = ServerLogin::::start( &mut server_rng, p_file, &server_kp.private(), diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 6fe1b32..56f634b 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -3,10 +3,10 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +use crate::tests::mock_rng::CycleRng; use crate::{errors::*, group::Group, oprf}; use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::GenericArray; -use rand_core::OsRng; use serde_json::Value; use sha2::Sha512; @@ -69,40 +69,15 @@ fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters { } } -// For fixing the blinding factor - -fn postprocess_blinding_factor_oprf_ristretto255_sha512_0(_: G::Scalar) -> G::Scalar { - let parameters = - populate_test_vectors(&serde_json::from_str(OPRF_RISTRETTO255_SHA512[0]).unwrap()); - G::from_scalar_slice(GenericArray::from_slice(¶meters.blind[..])).unwrap() -} - -fn postprocess_blinding_factor_oprf_ristretto255_sha512_1(_: G::Scalar) -> G::Scalar { - let parameters = - populate_test_vectors(&serde_json::from_str(OPRF_RISTRETTO255_SHA512[1]).unwrap()); - G::from_scalar_slice(GenericArray::from_slice(¶meters.blind[..])).unwrap() -} - // Tests input -> blind, blinded_element #[test] fn test_blind() -> Result<(), PakeError> { - for (i, tv) in OPRF_RISTRETTO255_SHA512.iter().enumerate() { + for tv in OPRF_RISTRETTO255_SHA512 { let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); - let mut rng = OsRng; + let mut rng = CycleRng::new(parameters.blind.to_vec()); - let postprocess_fn: fn( - ::Scalar, - ) -> ::Scalar = match i { - 0 => postprocess_blinding_factor_oprf_ristretto255_sha512_0::, - 1 => postprocess_blinding_factor_oprf_ristretto255_sha512_1::, - _ => panic!("Need to cover each test vector"), - }; - - let (token, blinded_element) = oprf::blind::( - ¶meters.input, - &mut rng, - postprocess_fn, - )?; + let (token, blinded_element) = + oprf::blind::<_, RistrettoPoint, Sha512>(¶meters.input, &mut rng)?; assert_eq!( ¶meters.blind,