Turning KeyPair into a struct (#119)

This commit is contained in:
Kevin Lewi
2021-01-25 13:20:36 -08:00
committed by GitHub
parent e421aaad07
commit 6dc1c8b212
18 changed files with 420 additions and 1283 deletions
+3 -59
View File
@@ -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::<EdwardsPoint>(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::<EdwardsPoint>(alpha, &salt);
c.bench_function("unblind_and_finalize with Edwards", move |b| {
b.iter(|| {
let _res = unblind_and_finalize_shim::<EdwardsPoint, Sha256>(&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);
+7 -8
View File
@@ -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<u8> {
// 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<curve25519_dalek::ristretto::RistrettoPoint>,
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<curve25519_dalek::ristretto::RistrettoPoint>,
password: String,
locker: &Locker,
) -> Result<String, String> {
@@ -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<Locker> = vec![];
+7 -9
View File
@@ -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<curve25519_dalek::ristretto::RistrettoPoint>,
password: String,
) -> Vec<u8> {
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<curve25519_dalek::ristretto::RistrettoPoint>,
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::<String, Vec<u8>>::new();
+5 -9
View File
@@ -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<Self::Hash, Self::KeyFormat>;
type KeyExchange: KeyExchange<Self::Hash, Self::Group>;
/// 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<Self::Hash>;
/// Generating a random key pair given a cryptographic rng
fn generate_random_keypair<R: RngCore + CryptoRng>(
rng: &mut R,
) -> Result<Self::KeyFormat, InternalPakeError> {
Self::KeyFormat::generate_random(rng)
fn generate_random_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> KeyPair<Self::Group> {
KeyPair::<Self::Group>::generate_random(rng)
}
}
-350
View File
@@ -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,
]);
-177
View File
@@ -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<u8> = (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<u8> = (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<u8> = (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<&'static str>> {
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[..]);
}
}
}
+25 -104
View File
@@ -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 <Self as Group>::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<u8>;
/// Return a scalar from its fixed-length bytes representation
@@ -57,6 +58,12 @@ pub trait Group: Sized + for<'a> Mul<&'a <Self as Group>::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<u8, Self::UniformBytesLen>) -> 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<u8, Self::ScalarLen>) -> 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<R: RngCore + CryptoRng>(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<u8, Self::ScalarLen> {
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<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalPakeError> {
let mut bits = [0u8; 32];
bits.copy_from_slice(scalar_bits);
Ok(Scalar::from_bytes_mod_order(bits))
}
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Scalar::random(rng)
}
fn scalar_as_bytes(scalar: &Self::Scalar) -> &GenericArray<u8, Self::ScalarLen> {
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<u8, Self::ElemLen>,
) -> Result<Self, InternalPakeError> {
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<u8, Self::ElemLen> {
let c = self.compress();
*GenericArray::from_slice(c.as_bytes())
}
type UniformBytesLen = U32;
fn hash_to_curve(uniform_bytes: &GenericArray<u8, Self::UniformBytesLen>) -> 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<EdwardsPoint> {
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<u8, Self::ScalarLen>) -> Self {
let arr: [u8; 32] = scalar.as_slice().try_into().expect("Wrong length");
self * Scalar::from_bits(arr)
}
}
+7 -6
View File
@@ -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<D: Hash, KeyFormat: KeyPair> {
pub trait KeyExchange<D: Hash, G: Group> {
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<D: Hash, KeyFormat: KeyPair> {
l1_bytes: Vec<u8>,
l2_bytes: Vec<u8>,
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<u8>,
id_s: Vec<u8>,
e_info: Vec<u8>,
@@ -43,8 +44,8 @@ pub trait KeyExchange<D: Hash, KeyFormat: KeyPair> {
l2_component: Vec<u8>,
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<u8>,
id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError>;
+53 -50
View File
@@ -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<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
type KE1State = KE1State<<D as FixedOutput>::OutputSize, KeyFormat>;
impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE1State = KE1State<<D as FixedOutput>::OutputSize>;
type KE2State = KE2State<<D as FixedOutput>::OutputSize>;
type KE1Message = KE1Message<KeyFormat>;
type KE2Message = KE2Message<<D as FixedOutput>::OutputSize, KeyFormat>;
type KE1Message = KE1Message;
type KE2Message = KE2Message<<D as FixedOutput>::OutputSize>;
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>(
@@ -55,7 +55,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyFormat::generate_random(rng)?;
let client_e_kp = KeyPair::<G>::generate_random(rng);
let client_nonce: GenericArray<u8, NonceLen> = {
let mut client_nonce_bytes = [0u8; NONCE_LEN];
rng.fill_bytes(&mut client_nonce_bytes);
@@ -89,20 +89,20 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
l1_bytes: Vec<u8>,
l2_bytes: Vec<u8>,
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<u8>,
id_s: Vec<u8>,
e_info: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyFormat::generate_random(rng)?;
let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce: GenericArray<u8, NonceLen> = {
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::<KeyFormat, D>(
let (session_secret, km2, ke2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
pk1: ke1_message.client_e_pk.clone(),
sk1: server_e_kp.private().clone(),
@@ -176,12 +176,12 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
l2_component: Vec<u8>,
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<u8>,
id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> {
let (session_secret, km2, ke2, km3) = derive_3dh_keys::<KeyFormat, D>(
let (session_secret, km2, ke2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(),
sk1: ke1_state.client_e_sk.clone(),
@@ -265,7 +265,7 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
}
fn ke1_state_size() -> usize {
KE1_STATE_LEN
NONCE_LEN + KEY_LEN + <<D as FixedOutput>::OutputSize as Unsigned>::to_usize()
}
fn ke2_message_size() -> usize {
@@ -275,21 +275,21 @@ impl<D: Hash, KeyFormat: KeyPair> KeyExchange<D, KeyFormat> for TripleDH {
/// The client state produced after the first key exchange message
#[derive(PartialEq, Eq)]
pub struct KE1State<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> {
client_e_sk: KeyFormat::Repr,
pub struct KE1State<HashLen: ArrayLength<u8>> {
client_e_sk: Key,
client_nonce: GenericArray<u8, NonceLen>,
hashed_l1: GenericArray<u8, HashLen>,
}
/// The first key exchange message
#[derive(PartialEq, Eq)]
pub struct KE1Message<KeyFormat: KeyPair> {
pub struct KE1Message {
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
pub(crate) info: Vec<u8>,
pub(crate) client_e_pk: KeyFormat::Repr,
pub(crate) client_e_pk: Key,
}
impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State<HashLen, KeyFormat> {
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE1State<HashLen> {
type Error = PakeError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
@@ -300,7 +300,7 @@ impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State<H
)?;
Ok(Self {
client_e_sk: KeyFormat::Repr::from_bytes(&checked_bytes[..KEY_LEN])?,
client_e_sk: Key::from_bytes(&checked_bytes[..KEY_LEN])?,
client_nonce: GenericArray::clone_from_slice(
&checked_bytes[KEY_LEN..KEY_LEN + NONCE_LEN],
),
@@ -309,7 +309,7 @@ impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> TryFrom<&[u8]> for KE1State<H
}
}
impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> ToBytes for KE1State<HashLen, KeyFormat> {
impl<HashLen: ArrayLength<u8>> ToBytes for KE1State<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [
&self.client_e_sk.to_arr(),
@@ -321,7 +321,7 @@ impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> ToBytes for KE1State<HashLen,
}
}
impl<KeyFormat: KeyPair> ToBytes for KE1Message<KeyFormat> {
impl ToBytes for KE1Message {
fn to_bytes(&self) -> Vec<u8> {
[
&self.client_nonce[..],
@@ -332,7 +332,7 @@ impl<KeyFormat: KeyPair> ToBytes for KE1Message<KeyFormat> {
}
}
impl<KeyFormat: KeyPair> TryFrom<&[u8]> for KE1Message<KeyFormat> {
impl TryFrom<&[u8]> for KE1Message {
type Error = PakeError;
fn try_from(ke1_message_bytes: &[u8]) -> Result<Self, Self::Error> {
@@ -346,7 +346,7 @@ impl<KeyFormat: KeyPair> TryFrom<&[u8]> for KE1Message<KeyFormat> {
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<HashLen: ArrayLength<u8>> {
}
/// The second key exchange message
pub struct KE2Message<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> {
pub struct KE2Message<HashLen: ArrayLength<u8>> {
server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: KeyFormat::Repr,
server_e_pk: Key,
e_info: Vec<u8>,
mac: GenericArray<u8, HashLen>,
}
@@ -380,23 +380,28 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2State<HashLen> {
type Error = PakeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
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<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> ToBytes for KE2Message<HashLen, KeyFormat> {
impl<HashLen: ArrayLength<u8>> ToBytes for KE2Message<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[&self.to_bytes_without_mac(), &self.mac[..]].concat()
}
}
impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> KE2Message<HashLen, KeyFormat> {
impl<HashLen: ArrayLength<u8>> KE2Message<HashLen> {
fn to_bytes_without_mac(&self) -> Vec<u8> {
[
&self.server_nonce[..],
@@ -407,9 +412,7 @@ impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> KE2Message<HashLen, KeyFormat
}
}
impl<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> TryFrom<&[u8]>
for KE2Message<HashLen, KeyFormat>
{
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
type Error = PakeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
@@ -424,7 +427,7 @@ impl<HashLen: ArrayLength<u8>, 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<HashLen: ArrayLength<u8>, KeyFormat: KeyPair> TryFrom<&[u8]>
}
// The triple of public and private components used in the 3DH computation
struct TripleDHComponents<KeyFormat: KeyPair> {
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<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE3Message<HashLen> {
type Error = PakeError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
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<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE3Message<HashLen> {
// 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<KeyFormat: KeyPair, D: Hash>(
dh: TripleDHComponents<KeyFormat>,
fn derive_3dh_keys<D: Hash, G: Group>(
dh: TripleDHComponents,
client_nonce: &GenericArray<u8, NonceLen>,
server_nonce: &GenericArray<u8, NonceLen>,
id_u: &[u8],
id_s: &[u8],
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
let ikm: Vec<u8> = [
&KeyFormat::diffie_hellman(dh.pk1, dh.sk1)[..],
&KeyFormat::diffie_hellman(dh.pk2, dh.sk2)[..],
&KeyFormat::diffie_hellman(dh.pk3, dh.sk3)[..],
&KeyPair::<G>::diffie_hellman(dh.pk1, dh.sk1)?[..],
&KeyPair::<G>::diffie_hellman(dh.pk2, dh.sk2)?[..],
&KeyPair::<G>::diffie_hellman(dh.pk3, dh.sk3)?[..],
]
.concat();
+62 -110
View File
@@ -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<T> 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<G> {
pk: Key,
sk: Key,
_g: PhantomData<G>,
}
impl<G: Group> KeyPair<G> {
/// 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<Self, InternalPakeError>;
pub fn new(public: Key, private: Key) -> Result<Self, InternalPakeError> {
Ok(Self {
pk: public,
sk: private,
_g: PhantomData,
})
}
/// Generating a random key pair given a cryptographic rng
fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, InternalPakeError>;
pub(crate) fn generate_random<R: RngCore + CryptoRng>(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::<u8, G::ScalarLen>::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<Self::Repr, InternalPakeError>;
pub(crate) fn check_public_key(key: Key) -> Result<Key, InternalPakeError> {
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<u8>;
pub(crate) fn diffie_hellman(pk: Key, sk: Key) -> Result<Vec<u8>, InternalPakeError> {
let pk_data = GenericArray::<u8, G::ElemLen>::from_slice(&pk.0[..]);
let point = G::from_element_slice(&pk_data)?;
let secret_data = GenericArray::<u8, G::ScalarLen>::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<Self, InternalPakeError> {
let sk = Self::Repr::from_arr(GenericArray::from_slice(&input))?;
pub fn from_private_key_slice(input: &[u8]) -> Result<Self, InternalPakeError> {
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<G: Group + Debug> KeyPair<G> {
/// Test-only strategy returning a proptest Strategy based on
/// generate_random
fn uniform_keypair_strategy() -> BoxedStrategy<Self> {
@@ -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<KP> 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<R: RngCore + CryptoRng>(rng: &mut R) -> (Vec<u8>, Vec<u8>) {
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<Self, InternalPakeError> {
Ok(X25519KeyPair {
pk: public,
sk: private,
})
}
fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, InternalPakeError> {
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<Self::Repr, InternalPakeError> {
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<u8> {
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::<RistrettoPoint>::uniform_keypair_strategy()) {
let pk = kp.public();
prop_assert!(X25519KeyPair::check_public_key(pk.clone()).is_ok());
prop_assert!(KeyPair::<RistrettoPoint>::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::<RistrettoPoint>::uniform_keypair_strategy()) {
let pk = kp.public();
let sk = kp.private();
prop_assert_eq!(&X25519KeyPair::public_from_private(sk), pk);
prop_assert_eq!(&KeyPair::<RistrettoPoint>::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::<RistrettoPoint>::uniform_keypair_strategy(),
kp2 in KeyPair::<RistrettoPoint>::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::<RistrettoPoint>::diffie_hellman(kp1.public().clone(), kp2.private().clone())?;
let dh2 = KeyPair::<RistrettoPoint>::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::<RistrettoPoint>::uniform_keypair_strategy()) {
let sk_bytes = kp.private().to_vec();
let kp2 = X25519KeyPair::from_private_key_slice(&sk_bytes)?;
let kp2 = KeyPair::<RistrettoPoint>::from_private_key_slice(&sk_bytes)?;
let kp2_private_bytes = kp2.private().to_vec();
prop_assert_eq!(sk_bytes, kp2_private_bytes);
+27 -58
View File
@@ -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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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::<Default>::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;
+4 -13
View File
@@ -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<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError> {
let uniform_bytes = expand_message_xmd::<H>(msg, dst, 64)?;
// FIXME use generic_array and turn this into a compile-time error if size mismatch
let uniform_bytes =
expand_message_xmd::<H>(msg, dst, <H as Digest>::OutputSize::to_usize())?;
Ok(<Self as Group>::hash_to_curve(
&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<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError> {
let (hashed_input, _) = Hkdf::<H>::extract(Some(dst), msg);
Ok(<Self as Group>::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;
+23 -30
View File
@@ -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<KeyFormat: KeyPair, D: Hash> {
pub struct RegistrationUpload<D: Hash, G: Group> {
/// The "envelope" generated by the user, containing sealed
/// cryptographic identifiers
pub(crate) envelope: Envelope<D>,
/// The user's public key
pub(crate) client_s_pk: KeyFormat::Repr,
pub(crate) client_s_pk: Key,
pub(crate) _g: PhantomData<G>,
}
impl<KeyFormat, D> TryFrom<&[u8]> for RegistrationUpload<KeyFormat, D>
where
KeyFormat: KeyPair,
D: Hash,
{
impl<D: Hash, G: Group> TryFrom<&[u8]> for RegistrationUpload<D, G> {
type Error = ProtocolError;
fn try_from(third_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
let key_len = <Key as SizedBytes>::Len::to_usize();
let envelope_size = key_len + Envelope::<D>::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::<G>::check_public_key(unchecked_client_s_pk)?;
Ok(Self {
envelope: Envelope::<D>::from_bytes(&checked_bytes[..envelope_size])?,
client_s_pk,
_g: PhantomData,
})
}
}
impl<KeyFormat, D> RegistrationUpload<KeyFormat, D>
where
KeyFormat: KeyPair,
D: Hash,
{
impl<D: Hash, G: Group> RegistrationUpload<D, G> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut message: Vec<u8> = 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::<G>::check_public_key(Key::from_bytes(&client_s_pk)?)?,
_g: PhantomData,
})
}
}
@@ -216,7 +212,7 @@ where
pub struct CredentialRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: CS::Group,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialRequest<CS> {
@@ -250,9 +246,7 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
let alpha = <CS::Group as Group>::from_element_slice(arr)?;
let ke1_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1Message::try_from(
&ke1m[..],
)?;
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message::try_from(&ke1m[..])?;
Ok(Self { alpha, ke1_message })
}
@@ -263,10 +257,10 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
pub struct CredentialResponse<CS: CipherSuite> {
/// the server's oprf output
pub(crate) beta: CS::Group,
pub(crate) server_s_pk: <CS::KeyFormat as KeyPair>::Repr,
pub(crate) server_s_pk: Key,
/// the user's sealed information,
pub(crate) envelope: Envelope<CS::Hash>,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message,
}
impl<CS: CipherSuite> CredentialResponse<CS> {
@@ -308,12 +302,11 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
let (serialized_server_s_pk, remainder) = tokenize(&checked_slice[elem_len..], 2)?;
let sized_server_s_pk = check_slice_size(
&serialized_server_s_pk[..],
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize(),
<Key as SizedBytes>::Len::to_usize(),
"server_s_pk in credential_response",
)?;
let unchecked_server_s_pk =
<CS::KeyFormat as KeyPair>::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::<CS::Group>::check_public_key(unchecked_server_s_pk)?;
let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&remainder)?;
@@ -321,7 +314,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
let checked_remainder =
check_slice_size_atleast(&remainder, ke2_message_size, "login_second_message_bytes")?;
let ke2_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2Message::try_from(
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message::try_from(
&checked_remainder,
)?;
@@ -337,7 +330,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
/// The answer sent by the client to the server, upon reception of the
/// sealed envelope
pub struct CredentialFinalization<CS: CipherSuite> {
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message,
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialFinalization<CS> {
@@ -345,7 +338,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialFinalization<CS> {
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let ke3_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE3Message::try_from(bytes)?;
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message::try_from(bytes)?;
Ok(Self { ke3_message })
}
}
+55 -92
View File
@@ -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<CS: CipherSuite> ClientRegistration<CS> {
/// 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<CS: CipherSuite> ClientRegistration<CS> {
pub fn start<R: RngCore + CryptoRng>(
blinding_factor_rng: &mut R,
password: &[u8],
#[cfg(test)] postprocess: fn(<CS::Group as Group>::Scalar) -> <CS::Group as Group>::Scalar,
) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(
&password,
blinding_factor_rng,
#[cfg(test)]
postprocess,
)?;
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, blinding_factor_rng)?;
Ok(ClientRegistrationStartResult {
message: RegistrationRequest::<CS::Group> { alpha },
@@ -145,11 +138,12 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
}
/// Contains the fields that are returned by a client registration finish
pub struct ClientRegistrationFinishResult<KeyFormat: KeyPair, D: Hash> {
pub struct ClientRegistrationFinishResult<D: Hash, G: Group> {
/// The registration upload message to be sent to the server
pub message: RegistrationUpload<KeyFormat, D>,
pub message: RegistrationUpload<D, G>,
/// The export key output by client registration
pub export_key: GenericArray<u8, ExportKeySize>,
_g: PhantomData<G>,
}
impl<CS: CipherSuite> ClientRegistration<CS> {
@@ -162,7 +156,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// # 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<CS: CipherSuite> ClientRegistration<CS> {
/// 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::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result =
/// ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
@@ -190,12 +183,12 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
rng: &mut R,
r2: RegistrationResponse<CS::Group>,
params: ClientRegistrationFinishParameters,
) -> Result<ClientRegistrationFinishResult<CS::KeyFormat, CS::Hash>, ProtocolError> {
) -> Result<ClientRegistrationFinishResult<CS::Hash, CS::Group>, 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::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?;
@@ -212,8 +205,10 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
message: RegistrationUpload {
envelope,
client_s_pk: client_static_keypair.public().clone(),
_g: PhantomData,
},
export_key,
_g: PhantomData,
})
}
}
@@ -257,19 +252,11 @@ pub struct ServerRegistrationStartResult<CS: CipherSuite> {
/// The state elements the server holds to record a registration
pub struct ServerRegistration<CS: CipherSuite> {
envelope: Option<Envelope<CS::Hash>>,
client_s_pk: Option<<CS::KeyFormat as KeyPair>::Repr>,
client_s_pk: Option<Key>,
pub(crate) oprf_key: <CS::Group as Group>::Scalar,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ServerRegistration<CS>
where
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len:
std::ops::Add<<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len>,
generic_array::typenum::Sum<
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
>: generic_array::ArrayLength<u8>,
{
impl<CS: CipherSuite> TryFrom<&[u8]> for ServerRegistration<CS> {
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 = <<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
let key_len = <Key as SizedBytes>::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 = <CS::KeyFormat as KeyPair>::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::<CS::Group>::check_public_key(unchecked_client_s_pk)?;
let envelope = Envelope::<CS::Hash>::from_bytes(&checked_bytes[scalar_len + key_len..])?;
@@ -307,15 +293,7 @@ where
}
}
impl<CS: CipherSuite> ServerRegistration<CS>
where
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len:
std::ops::Add<<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len>,
generic_array::typenum::Sum<
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
>: generic_array::ArrayLength<u8>,
{
impl<CS: CipherSuite> ServerRegistration<CS> {
/// byte representation for the server's registration state
pub fn to_bytes(&self) -> Vec<u8> {
let mut output: Vec<u8> = 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::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// # Ok::<(), ProtocolError>(())
@@ -359,7 +336,7 @@ where
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
message: RegistrationRequest<CS::Group>,
server_s_pk: &<CS::KeyFormat as KeyPair>::Repr,
server_s_pk: &Key,
) -> Result<ServerRegistrationStartResult<CS>, 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::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result = ServerRegistration::<Default>::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<CS::KeyFormat, CS::Hash>,
message: RegistrationUpload<CS::Hash, CS::Group>,
) -> Result<Self, ProtocolError> {
Ok(Self {
envelope: Some(message.envelope),
@@ -430,7 +406,7 @@ where
pub struct ClientLogin<CS: CipherSuite> {
/// token containing the client's password and the blinding factor
token: oprf::Token<CS::Group>,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1State,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
@@ -438,7 +414,7 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
let ke1_state_size =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::ke1_state_size();
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::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<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
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 =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE1State::try_from(
&checked_slice[scalar_len..scalar_len + ke1_state_size],
)?;
let ke1_state = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::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<CS: CipherSuite> {
/// The client-side export key
pub export_key: GenericArray<u8, ExportKeySize>,
/// The server's static public key
pub server_s_pk: <CS::KeyFormat as KeyPair>::Repr,
pub server_s_pk: Key,
/// The confidential info sent by the client
pub confidential_info: Vec<u8>,
}
@@ -545,9 +520,8 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// 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<CS: CipherSuite> ClientLogin<CS> {
rng: &mut R,
password: &[u8],
params: ClientLoginStartParameters,
#[cfg(test)] postprocess: fn(<CS::Group as Group>::Scalar) -> <CS::Group as Group>::Scalar,
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
let ClientLoginStartParameters::WithInfo(info) = params;
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(
&password,
rng,
#[cfg(test)]
postprocess,
)?;
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, rng)?;
let (ke1_state, ke1_message) =
CS::KeyExchange::generate_ke1(alpha.to_arr().to_vec(), info, rng)?;
@@ -592,21 +560,20 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// 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::<Default>::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::<Default>::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<CS: CipherSuite> ClientLogin<CS> {
err => PakeError::from(err),
})?;
let client_s_sk =
<CS::KeyFormat as KeyPair>::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::<CS::Group>::public_from_private(&client_s_sk)
.to_arr()
.to_vec(),
server_s_pk_bytes.clone(),
@@ -681,7 +647,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// The state elements the server holds to record a login
pub struct ServerLogin<CS: CipherSuite> {
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State,
_cs: PhantomData<CS>,
}
@@ -690,10 +656,9 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ServerLogin<CS> {
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Ok(Self {
_cs: PhantomData,
ke2_state:
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::KE2State::try_from(
bytes,
)?,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State::try_from(
bytes,
)?,
})
}
}
@@ -750,20 +715,19 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// 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::<Default>::start(&mut client_rng, b"hunter2")?;
/// # let server_registration_start_result = ServerRegistration::<Default>::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<CS: CipherSuite> ServerLogin<CS> {
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
password_file: ServerRegistration<CS>,
server_s_sk: &<CS::KeyFormat as KeyPair>::Repr,
server_s_sk: &Key,
l1: CredentialRequest<CS>,
params: ServerLoginStartParameters,
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
@@ -801,7 +765,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
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::<CS::Group>::public_from_private(server_s_sk)
.to_arr()
.to_vec(),
),
@@ -811,7 +775,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
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::<CS::Group>::public_from_private(&server_s_sk);
let l2_component: Vec<u8> = [
serialize(&beta.to_arr()[..], 2),
@@ -861,20 +825,19 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// 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::<Default>::start(&mut client_rng, b"hunter2")?;
/// # let server_registration_start_result = ServerRegistration::<Default>::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<CS: CipherSuite> ServerLogin<CS> {
&self,
message: CredentialFinalization<CS>,
) -> Result<ServerLoginFinishResult, ProtocolError> {
let shared_secret = <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeyFormat>>::finish_ke(
let shared_secret = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::finish_ke(
message.ke3_message,
&self.ke2_state,
)
+12 -28
View File
@@ -28,16 +28,10 @@ static MODE_BASE: u8 = 0x00;
pub(crate) fn blind<R: RngCore + CryptoRng, G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
#[cfg(test)] postprocess: fn(G::Scalar) -> G::Scalar,
) -> Result<(Token<G>, 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::<H>(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<R: RngCore + CryptoRng, G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<(Token<G>, G), InternalPakeError> {
blind::<R, G, H>(
input,
blinding_factor_rng,
#[cfg(test)]
std::convert::identity,
)
blind::<R, G, H>(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<u8, <RistrettoPoint as Group>::ElemLen> {
fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray<u8, <Sha512 as Digest>::OutputSize> {
let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat();
let point = RistrettoPoint::map_to_curve::<Sha256>(input, &dst).unwrap();
let point = RistrettoPoint::map_to_curve::<Sha512>(input, &dst).unwrap();
let scalar =
RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap();
let res = point * scalar;
finalize::<RistrettoPoint, sha2::Sha256>(&input, &res.to_arr().to_vec(), b"")
finalize::<RistrettoPoint, sha2::Sha512>(&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::<RistrettoPoint>(alpha, &oprf_key);
let res =
finalize::<RistrettoPoint, sha2::Sha256>(&token.data, &unblind(&token, beta), b"");
finalize::<RistrettoPoint, sha2::Sha512>(&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::<RistrettoPoint, sha2::Sha256>(&token.data, &unblind(&token, alpha), b"");
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &unblind(&token, alpha), b"");
let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat();
let point = RistrettoPoint::map_to_curve::<Sha256>(&input, &dst).unwrap();
let res2 = finalize::<RistrettoPoint, sha2::Sha256>(&input, &point.to_arr().to_vec(), b"");
let point = RistrettoPoint::map_to_curve::<Sha512>(&input, &dst).unwrap();
let res2 = finalize::<RistrettoPoint, sha2::Sha512>(&input, &point.to_arr().to_vec(), b"");
assert_eq!(res, res2);
}
+33 -40
View File
@@ -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::<u8>::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::<sha2::Sha256>::seal_raw(
let (envelope, _) = Envelope::<sha2::Sha512>::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::<usize>() - 2..]);
input.extend_from_slice(&pubkey_bytes[..]);
let r3 = RegistrationUpload::<X25519KeyPair, sha2::Sha256>::deserialize(&input[..]).unwrap();
let r3 = RegistrationUpload::<sha2::Sha512, RistrettoPoint>::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::<sha2::Sha256>::seal_raw(
let (envelope, _) = Envelope::<sha2::Sha512>::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<u8> = [&mac[..]].concat();
@@ -276,12 +275,12 @@ fn client_login_roundtrip() {
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::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 = <TripleDH as KeyExchange<
sha2::Sha256,
crate::keypair::X25519KeyPair,
>>::KE1Message::try_from(&ke1m[..])
.unwrap();
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::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 = <TripleDH as KeyExchange<
sha2::Sha256,
crate::keypair::X25519KeyPair,
>>::KE2Message::try_from(&ke2m[..])
.unwrap();
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::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<u8> = [&mac[..]].concat();
let reg = <TripleDH as KeyExchange<
sha2::Sha256,
crate::keypair::X25519KeyPair,
>>::KE3Message::try_from(&ke3m[..])
.unwrap();
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::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::<u8>(), 0..200)) {
#[test]
fn test_nocrash_register_third_message(bytes in vec(any::<u8>(), 0..200)) {
RegistrationUpload::<crate::keypair::X25519KeyPair, sha2::Sha512>::try_from(&bytes[..]).map_or(true, |_| true);
RegistrationUpload::<sha2::Sha512, RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
}
#[test]
+92 -110
View File
@@ -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<CS: CipherSuite>() -> 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).
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len:
std::ops::Add<<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len>,
generic_array::typenum::Sum<
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
<<CS::KeyFormat as KeyPair>::Repr as SizedBytes>::Len,
>: generic_array::ArrayLength<u8>,
{
fn generate_parameters<CS: CipherSuite>() -> 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::<CS>::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::<CS>::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<u8> = 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::<X255193dhNoSlowHash>();
let parameters = generate_parameters::<RistrettoSha5123dhNoSlowHash>();
println!("{}", stringify_test_vectors(&parameters));
}
// For fixing the blinding factor
fn postprocess_blinding_factor<G: Group>(_: G::Scalar) -> G::Scalar {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
G::from_scalar_slice(GenericArray::from_slice(&parameters.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::<X255193dhNoSlowHash>::start(
&mut rng,
&parameters.password,
postprocess_blinding_factor::<<X255193dhNoSlowHash as CipherSuite>::Group>,
)?;
let mut rng = CycleRng::new(parameters.blinding_factor.to_vec());
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut rng, &parameters.password)?;
assert_eq!(
hex::encode(&parameters.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::<X255193dhNoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&Key::try_from(&parameters.server_s_pk[..]).unwrap(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&Key::try_from(&parameters.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<u8> =
[parameters.client_s_sk, parameters.envelope_nonce].concat();
let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce);
let result = ClientRegistration::<X255193dhNoSlowHash>::try_from(
let result = ClientRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
&parameters.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::<X255193dhNoSlowHash>::try_from(
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
&parameters.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::<X255193dhNoSlowHash>::start(
let mut client_login_start_rng = CycleRng::new(client_login_start_rng);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_login_start_rng,
&parameters.password,
ClientLoginStartParameters::WithInfo(parameters.info1),
postprocess_blinding_factor::<<X255193dhNoSlowHash as CipherSuite>::Group>,
)?;
assert_eq!(
hex::encode(&parameters.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::<X255193dhNoSlowHash>::start(
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_e_sk_rng,
ServerRegistration::try_from(&parameters.password_file[..]).unwrap(),
&Key::try_from(&parameters.server_s_sk[..]).unwrap(),
CredentialRequest::<X255193dhNoSlowHash>::deserialize(&parameters.credential_request[..])
.unwrap(),
CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.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::<X255193dhNoSlowHash>::try_from(&parameters.client_login_state[..])
ClientLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.client_login_state[..])
.unwrap()
.finish(
CredentialResponse::<X255193dhNoSlowHash>::deserialize(
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.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::<X255193dhNoSlowHash>::try_from(&parameters.server_login_state[..])?.finish(
CredentialFinalization::try_from(&parameters.key_exchange[..])?,
)?;
ServerLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.server_login_state[..])?
.finish(CredentialFinalization::try_from(
&parameters.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::<X255193dhNoSlowHash>::start(
&mut client_rng,
registration_password,
std::convert::identity,
)?;
let server_registration_start_result = ServerRegistration::<X255193dhNoSlowHash>::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::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
registration_password,
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::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::<X255193dhNoSlowHash>::start(
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
login_password,
ClientLoginStartParameters::default(),
std::convert::identity,
)?;
let server_login_start_result = ServerLogin::<X255193dhNoSlowHash>::start(
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
+5 -30
View File
@@ -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: Group>(_: 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(&parameters.blind[..])).unwrap()
}
fn postprocess_blinding_factor_oprf_ristretto255_sha512_1<G: Group>(_: 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(&parameters.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(
<RistrettoPoint as Group>::Scalar,
) -> <RistrettoPoint as Group>::Scalar = match i {
0 => postprocess_blinding_factor_oprf_ristretto255_sha512_0::<RistrettoPoint>,
1 => postprocess_blinding_factor_oprf_ristretto255_sha512_1::<RistrettoPoint>,
_ => panic!("Need to cover each test vector"),
};
let (token, blinded_element) = oprf::blind::<OsRng, RistrettoPoint, Sha512>(
&parameters.input,
&mut rng,
postprocess_fn,
)?;
let (token, blinded_element) =
oprf::blind::<_, RistrettoPoint, Sha512>(&parameters.input, &mut rng)?;
assert_eq!(
&parameters.blind,