Turning KeyPair into a struct (#119)
This commit is contained in:
+3
-59
@@ -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);
|
||||
|
||||
@@ -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![];
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(¶meters));
|
||||
}
|
||||
|
||||
// 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(¶meters.blinding_factor[..])).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registration_request() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
let mut rng = OsRng;
|
||||
let client_registration_start_result = ClientRegistration::<X255193dhNoSlowHash>::start(
|
||||
&mut rng,
|
||||
¶meters.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, ¶meters.password)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.registration_request),
|
||||
hex::encode(client_registration_start_result.message.serialize())
|
||||
@@ -457,11 +436,12 @@ fn test_registration_request() -> Result<(), ProtocolError> {
|
||||
fn test_registration_response() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
let mut oprf_key_rng = CycleRng::new(parameters.oprf_key);
|
||||
let server_registration_start_result = ServerRegistration::<X255193dhNoSlowHash>::start(
|
||||
&mut oprf_key_rng,
|
||||
RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..]).unwrap(),
|
||||
)?;
|
||||
let server_registration_start_result =
|
||||
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
|
||||
&mut oprf_key_rng,
|
||||
RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..]).unwrap(),
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(parameters.registration_response),
|
||||
hex::encode(server_registration_start_result.message.serialize())
|
||||
@@ -480,7 +460,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
|
||||
let client_s_sk_and_nonce: Vec<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(
|
||||
¶meters.client_registration_state[..],
|
||||
)?
|
||||
.finish(
|
||||
@@ -505,7 +485,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
|
||||
fn test_password_file() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let server_registration = ServerRegistration::<X255193dhNoSlowHash>::try_from(
|
||||
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
|
||||
¶meters.server_registration_state[..],
|
||||
)?;
|
||||
let password_file = server_registration
|
||||
@@ -522,18 +502,17 @@ fn test_password_file() -> Result<(), ProtocolError> {
|
||||
fn test_credential_request() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let client_login_start = [
|
||||
vec![0u8; 64], // FIXME: don't hardcode this
|
||||
let client_login_start_rng = [
|
||||
parameters.blinding_factor,
|
||||
parameters.client_e_sk,
|
||||
parameters.client_nonce,
|
||||
]
|
||||
.concat();
|
||||
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
||||
let client_login_start_result = ClientLogin::<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,
|
||||
¶meters.password,
|
||||
ClientLoginStartParameters::WithInfo(parameters.info1),
|
||||
postprocess_blinding_factor::<<X255193dhNoSlowHash as CipherSuite>::Group>,
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.credential_request),
|
||||
@@ -551,12 +530,14 @@ fn test_credential_response() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let mut server_e_sk_rng = CycleRng::new(parameters.server_e_sk);
|
||||
let server_login_start_result = ServerLogin::<X255193dhNoSlowHash>::start(
|
||||
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
|
||||
&mut server_e_sk_rng,
|
||||
ServerRegistration::try_from(¶meters.password_file[..]).unwrap(),
|
||||
&Key::try_from(¶meters.server_s_sk[..]).unwrap(),
|
||||
CredentialRequest::<X255193dhNoSlowHash>::deserialize(¶meters.credential_request[..])
|
||||
.unwrap(),
|
||||
CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize(
|
||||
¶meters.credential_request[..],
|
||||
)
|
||||
.unwrap(),
|
||||
ServerLoginStartParameters::WithInfoAndIdentifiers(
|
||||
parameters.einfo2.to_vec(),
|
||||
parameters.id_u,
|
||||
@@ -583,10 +564,10 @@ fn test_key_exchange() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let client_login_finish_result =
|
||||
ClientLogin::<X255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
ClientLogin::<RistrettoSha5123dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
.unwrap()
|
||||
.finish(
|
||||
CredentialResponse::<X255193dhNoSlowHash>::deserialize(
|
||||
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
|
||||
¶meters.credential_response[..],
|
||||
)?,
|
||||
ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s),
|
||||
@@ -621,9 +602,10 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let server_login_result =
|
||||
ServerLogin::<X255193dhNoSlowHash>::try_from(¶meters.server_login_state[..])?.finish(
|
||||
CredentialFinalization::try_from(¶meters.key_exchange[..])?,
|
||||
)?;
|
||||
ServerLogin::<RistrettoSha5123dhNoSlowHash>::try_from(¶meters.server_login_state[..])?
|
||||
.finish(CredentialFinalization::try_from(
|
||||
¶meters.key_exchange[..],
|
||||
)?)?;
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(parameters.shared_secret),
|
||||
@@ -639,17 +621,18 @@ fn test_complete_flow(
|
||||
) -> Result<(), ProtocolError> {
|
||||
let mut client_rng = OsRng;
|
||||
let mut server_rng = OsRng;
|
||||
let server_kp = X255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?;
|
||||
let client_registration_start_result = ClientRegistration::<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(),
|
||||
|
||||
@@ -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(¶meters.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(¶meters.blind[..])).unwrap()
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
#[test]
|
||||
fn test_blind() -> Result<(), PakeError> {
|
||||
for (i, tv) in OPRF_RISTRETTO255_SHA512.iter().enumerate() {
|
||||
for tv in OPRF_RISTRETTO255_SHA512 {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
|
||||
let mut rng = OsRng;
|
||||
let mut rng = CycleRng::new(parameters.blind.to_vec());
|
||||
|
||||
let postprocess_fn: fn(
|
||||
<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>(
|
||||
¶meters.input,
|
||||
&mut rng,
|
||||
postprocess_fn,
|
||||
)?;
|
||||
let (token, blinded_element) =
|
||||
oprf::blind::<_, RistrettoPoint, Sha512>(¶meters.input, &mut rng)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind,
|
||||
|
||||
Reference in New Issue
Block a user