Using voprf as a dependency (#248)

* Using voprf as a dependency

* Adding back x25519 and KeGroup

* Addressing comments
This commit is contained in:
Kevin Lewi
2021-10-25 02:54:32 -07:00
committed by GitHub
parent f1f4184400
commit 29b2ebef1b
34 changed files with 1638 additions and 2452 deletions
+8 -1
View File
@@ -163,6 +163,13 @@ jobs:
benches:
name: cargo bench compilation
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
backend_feature:
- u64_backend
- u32_backend
- p256,u64_backend
steps:
- name: Checkout sources
uses: actions/checkout@v2
@@ -178,7 +185,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: bench
args: --features "bench" --no-run
args: --no-default-features --features bench --features ${{ matrix.backend_feature }} --no-run
clippy:
name: cargo clippy
+7 -10
View File
@@ -14,12 +14,12 @@ resolver = "2"
[features]
default = ["u64_backend", "serialize"]
slow-hash = ["argon2"]
p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"]
p256 = ["p256_", "voprf/p256"]
bench = []
u64_backend = ["curve25519-dalek/u64_backend"]
u32_backend = ["curve25519-dalek/u32_backend"]
std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "num-bigint/std", "num-integer/std", "num-traits/std"]
serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde"]
u64_backend = ["curve25519-dalek/u64_backend", "voprf/ristretto255_u64"]
u32_backend = ["curve25519-dalek/u32_backend", "voprf/ristretto255_u32"]
std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "voprf/std"]
serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde", "voprf/serde"]
[dependencies]
argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true }
@@ -32,14 +32,11 @@ generic-array = "0.14"
getrandom = { version = "0.2", optional = true }
hkdf = "0.11"
hmac = "0.11"
num-bigint = { version = "0.4", default-features = false, optional = true }
num-integer = { version = "0.1", default-features = false, optional = true }
num-traits = { version = "0.2", default-features = false, optional = true }
once_cell = { version = "1", default-features = false, optional = true }
p256_ = { package = "p256", version = "0.9", default-features = false, features = ["arithmetic", "zeroize"], optional = true }
rand = { version = "0.8", default-features = false }
serde = { version = "1", default-features = false, features = ["alloc", "derive"], optional = true }
subtle = { version = "2.3", default-features = false }
voprf = { version = "0.2", default-features = false, features = ["danger"] }
zeroize = { version = "1", features = ["zeroize_derive"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
@@ -61,6 +58,6 @@ regex = "1"
rustyline = "8"
[[bench]]
name = "oprf"
name = "opaque"
harness = false
required-features = ["bench"]
+345
View File
@@ -0,0 +1,345 @@
// 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.
#[macro_use]
extern crate criterion;
use criterion::Criterion;
use opaque_ke::*;
use rand::rngs::OsRng;
#[cfg(all(not(feature = "p256"), feature = "u64_backend"))]
static SUFFIX: &str = "u64_backend";
#[cfg(all(not(feature = "p256"), feature = "u32_backend"))]
static SUFFIX: &str = "u32_backend";
#[cfg(feature = "p256")]
static SUFFIX: &str = "p256";
struct Default;
#[cfg(not(feature = "p256"))]
impl CipherSuite for Default {
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
type Hash = sha2::Sha512;
type SlowHash = opaque_ke::slow_hash::NoOpHash;
}
#[cfg(feature = "p256")]
impl CipherSuite for Default {
type OprfGroup = p256_::ProjectivePoint;
type KeGroup = p256_::ProjectivePoint;
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
type Hash = sha2::Sha256;
type SlowHash = opaque_ke::slow_hash::NoOpHash;
}
fn server_setup(c: &mut Criterion) {
let mut rng = OsRng;
c.bench_function(&format!("server setup ({})", SUFFIX), move |b| {
b.iter(|| {
ServerSetup::<Default>::new(&mut rng).unwrap();
})
});
}
fn client_registration_start(c: &mut Criterion) {
let mut rng = OsRng;
let password = b"password";
c.bench_function(
&format!("client registration start ({})", SUFFIX),
move |b| {
b.iter(|| {
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
})
},
);
}
fn server_registration_start(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
c.bench_function(
&format!("server registration start ({})", SUFFIX),
move |b| {
b.iter(|| {
ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
})
},
);
}
fn client_registration_finish(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
let server_registration_start_result = ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
c.bench_function(
&format!("client registration finish ({})", SUFFIX),
move |b| {
b.iter(|| {
client_registration_start_result
.clone()
.state
.finish(
&mut rng,
server_registration_start_result.message.clone(),
ClientRegistrationFinishParameters::default(),
)
.unwrap();
})
},
);
}
fn server_registration_finish(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
let server_registration_start_result = ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
let client_registration_finish_result = client_registration_start_result
.clone()
.state
.finish(
&mut rng,
server_registration_start_result.message.clone(),
ClientRegistrationFinishParameters::default(),
)
.unwrap();
c.bench_function(
&format!("server registration finish ({})", SUFFIX),
move |b| {
b.iter(|| {
ServerRegistration::finish(client_registration_finish_result.clone().message);
})
},
);
}
fn client_login_start(c: &mut Criterion) {
let mut rng = OsRng;
let password = b"password";
c.bench_function(&format!("client login start ({})", SUFFIX), move |b| {
b.iter(|| {
ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
})
});
}
fn server_login_start_real(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
let server_registration_start_result = ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
let client_registration_finish_result = client_registration_start_result
.clone()
.state
.finish(
&mut rng,
server_registration_start_result.message.clone(),
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
c.bench_function(
&format!("server login start (real) ({})", SUFFIX),
move |b| {
b.iter(|| {
ServerLogin::start(
&mut rng,
&server_setup,
Some(password_file.clone()),
client_login_start_result.clone().message,
&username[..],
ServerLoginStartParameters::default(),
)
.unwrap();
})
},
);
}
fn server_login_start_fake(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_login_start_result = ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
c.bench_function(
&format!("server login start (fake) ({})", SUFFIX),
move |b| {
b.iter(|| {
ServerLogin::start(
&mut rng,
&server_setup,
None,
client_login_start_result.clone().message,
&username[..],
ServerLoginStartParameters::default(),
)
.unwrap();
})
},
);
}
fn client_login_finish(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
let server_registration_start_result = ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
let client_registration_finish_result = client_registration_start_result
.clone()
.state
.finish(
&mut rng,
server_registration_start_result.message.clone(),
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
let server_login_start = ServerLogin::start(
&mut rng,
&server_setup,
Some(password_file.clone()),
client_login_start_result.clone().message,
&username[..],
ServerLoginStartParameters::default(),
)
.unwrap();
c.bench_function(&format!("client login finish ({})", SUFFIX), move |b| {
b.iter(|| {
client_login_start_result
.clone()
.state
.finish(
server_login_start.clone().message,
ClientLoginFinishParameters::default(),
)
.unwrap();
})
});
}
fn server_login_finish(c: &mut Criterion) {
let mut rng = OsRng;
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
let server_registration_start_result = ServerRegistration::<Default>::start(
&server_setup,
client_registration_start_result.message.clone(),
&username[..],
)
.unwrap();
let client_registration_finish_result = client_registration_start_result
.clone()
.state
.finish(
&mut rng,
server_registration_start_result.message.clone(),
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
let server_login_start_result = ServerLogin::start(
&mut rng,
&server_setup,
Some(password_file.clone()),
client_login_start_result.clone().message,
&username[..],
ServerLoginStartParameters::default(),
)
.unwrap();
let client_login_finish_result = client_login_start_result
.clone()
.state
.finish(
server_login_start_result.clone().message,
ClientLoginFinishParameters::default(),
)
.unwrap();
c.bench_function(&format!("server login finish ({})", SUFFIX), move |b| {
b.iter(|| {
server_login_start_result
.clone()
.state
.finish(client_login_finish_result.clone().message)
.unwrap();
})
});
}
criterion_group!(
opaque_benches,
server_setup,
client_registration_start,
server_registration_start,
client_registration_finish,
server_registration_finish,
client_login_start,
server_login_start_real,
server_login_start_fake,
client_login_finish,
server_login_finish,
);
criterion_main!(opaque_benches);
-68
View File
@@ -1,68 +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.
#[macro_use]
extern crate criterion;
use criterion::Criterion;
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::arr;
use opaque_ke::{
group::Group,
oprf::{blind_shim, evaluate_shim, finalize_shim},
};
use rand::{prelude::ThreadRng, thread_rng};
use sha2::Sha512;
fn oprf1(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let input = b"hunter2";
c.bench_function("blind with Ristretto", move |b| {
b.iter(|| {
blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap();
})
});
}
fn oprf2(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let input = b"hunter2";
let (_, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&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 Ristretto", move |b| {
b.iter(|| {
let _beta = evaluate_shim::<RistrettoPoint>(alpha, &salt);
})
});
}
fn oprf3(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let input = b"hunter2";
let (token, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&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::<RistrettoPoint>(alpha, &salt);
c.bench_function("finalize with Ristretto", move |b| {
b.iter(|| {
let _res = finalize_shim::<RistrettoPoint, Sha512>(&token, beta).unwrap();
})
});
}
criterion_group!(oprf_benches, oprf1, oprf2, oprf3);
criterion_main!(oprf_benches);
+17 -8
View File
@@ -90,7 +90,10 @@ fn register_locker(
let mut client_rng = OsRng;
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
let registration_request_bytes = client_registration_start_result.message.serialize();
let registration_request_bytes = client_registration_start_result
.message
.serialize()
.unwrap();
// Client sends registration_request_bytes to server
let server_registration_start_result = ServerRegistration::<Default>::start(
@@ -99,7 +102,10 @@ fn register_locker(
&locker_id.to_be_bytes(),
)
.unwrap();
let registration_response_bytes = server_registration_start_result.message.serialize();
let registration_response_bytes = server_registration_start_result
.message
.serialize()
.unwrap();
// Server sends registration_response_bytes to client
@@ -111,7 +117,10 @@ fn register_locker(
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let message_bytes = client_finish_registration_result.message.serialize();
let message_bytes = client_finish_registration_result
.message
.serialize()
.unwrap();
// Client encrypts secret message using export key
let ciphertext = encrypt(
@@ -127,7 +136,7 @@ fn register_locker(
Locker {
contents: ciphertext,
password_file: password_file.serialize(),
password_file: password_file.serialize().unwrap(),
}
}
@@ -141,7 +150,7 @@ fn open_locker(
let mut client_rng = OsRng;
let client_login_start_result =
ClientLogin::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
let credential_request_bytes = client_login_start_result.message.serialize().unwrap();
// Client sends credential_request_bytes to server
@@ -157,7 +166,7 @@ fn open_locker(
ServerLoginStartParameters::default(),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize();
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
// Server sends credential_response_bytes to client
@@ -171,7 +180,7 @@ fn open_locker(
return Err(String::from("Incorrect password, please try again."));
}
let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
// Client sends credential_finalization_bytes to server
@@ -197,7 +206,7 @@ fn open_locker(
fn main() {
let mut rng = OsRng;
let server_setup = ServerSetup::<Default>::new(&mut rng);
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let mut rl = Editor::<()>::new();
let mut registered_lockers: Vec<Locker> = vec![];
+17 -8
View File
@@ -53,7 +53,10 @@ fn account_registration(
let mut client_rng = OsRng;
let client_registration_start_result =
ClientRegistration::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
let registration_request_bytes = client_registration_start_result.message.serialize();
let registration_request_bytes = client_registration_start_result
.message
.serialize()
.unwrap();
// Client sends registration_request_bytes to server
@@ -63,7 +66,10 @@ fn account_registration(
username.as_bytes(),
)
.unwrap();
let registration_response_bytes = server_registration_start_result.message.serialize();
let registration_response_bytes = server_registration_start_result
.message
.serialize()
.unwrap();
// Server sends registration_response_bytes to client
@@ -75,14 +81,17 @@ fn account_registration(
ClientRegistrationFinishParameters::default(),
)
.unwrap();
let message_bytes = client_finish_registration_result.message.serialize();
let message_bytes = client_finish_registration_result
.message
.serialize()
.unwrap();
// Client sends message_bytes to server
let password_file = ServerRegistration::finish(
RegistrationUpload::<Default>::deserialize(&message_bytes[..]).unwrap(),
);
password_file.serialize()
password_file.serialize().unwrap()
}
// Password-based login between a client and server
@@ -95,7 +104,7 @@ fn account_login(
let mut client_rng = OsRng;
let client_login_start_result =
ClientLogin::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
let credential_request_bytes = client_login_start_result.message.serialize().unwrap();
// Client sends credential_request_bytes to server
@@ -110,7 +119,7 @@ fn account_login(
ServerLoginStartParameters::default(),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize();
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
// Server sends credential_response_bytes to client
@@ -124,7 +133,7 @@ fn account_login(
return false;
}
let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
// Client sends credential_finalization_bytes to server
@@ -138,7 +147,7 @@ fn account_login(
fn main() {
let mut rng = OsRng;
let server_setup = ServerSetup::<Default>::new(&mut rng);
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
let mut rl = Editor::<()>::new();
let mut registered_users = HashMap::<String, Vec<u8>>::new();
+5 -3
View File
@@ -5,7 +5,9 @@
//! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE
use crate::{group::Group, hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash};
use crate::key_exchange::group::KeGroup;
use crate::{hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash};
use voprf::group::Group as OprfGroup;
/// Configures the underlying primitives used in OPAQUE
/// * `OprfGroup`: a finite cyclic group along with a point representation, along
@@ -19,9 +21,9 @@ pub trait CipherSuite {
/// A finite cyclic group along with a point representation along with
/// an extension trait PasswordToCurve that allows some customization on
/// how to hash a password to a curve point. See `group::Group`.
type OprfGroup: Group;
type OprfGroup: OprfGroup;
/// A `Group` used for the `KeyExchange`.
type KeGroup: Group;
type KeGroup: KeGroup;
/// A key exchange protocol
type KeyExchange: KeyExchange<Self::Hash, Self::KeGroup>;
/// The main hash function use (for HKDF computations and hashing transcripts)
+44 -38
View File
@@ -6,8 +6,8 @@
use crate::{
ciphersuite::CipherSuite,
errors::{utils::check_slice_size, InternalError, ProtocolError},
group::Group,
hash::Hash,
key_exchange::group::KeGroup,
keypair::{KeyPair, PublicKey},
opaque::{bytestrings_from_identifiers, Identifiers},
};
@@ -19,13 +19,14 @@ use generic_array::{typenum::Unsigned, GenericArray};
use hkdf::Hkdf;
use hmac::{Hmac, Mac, NewMac};
use rand::{CryptoRng, RngCore};
use voprf::group::Group;
use zeroize::Zeroize;
// Constant string used as salt for HKDF computation
const STR_AUTH_KEY: &[u8] = b"AuthKey";
const STR_EXPORT_KEY: &[u8] = b"ExportKey";
const STR_PRIVATE_KEY: &[u8] = b"PrivateKey";
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8] = b"OPAQUE-DeriveAuthKeyPair";
const STR_AUTH_KEY: &[u8; 7] = b"AuthKey";
const STR_EXPORT_KEY: &[u8; 9] = b"ExportKey";
const STR_PRIVATE_KEY: &[u8; 10] = b"PrivateKey";
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8; 24] = b"OPAQUE-DeriveAuthKeyPair";
const NONCE_LEN: usize = 32;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
@@ -117,7 +118,7 @@ impl<CS: CipherSuite> Envelope<CS> {
#[allow(clippy::type_complexity)]
pub(crate) fn seal<R: RngCore + CryptoRng>(
rng: &mut R,
key: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
server_s_pk: &[u8],
optional_ids: Option<Identifiers>,
) -> Result<SealResult<CS>, ProtocolError> {
@@ -126,14 +127,14 @@ impl<CS: CipherSuite> Envelope<CS> {
let (mode, client_s_pk) = (
InnerEnvelopeMode::Internal,
build_inner_envelope_internal::<CS>(key, &nonce)?,
build_inner_envelope_internal::<CS>(randomized_pwd_hasher.clone(), &nonce)?,
);
let (id_u, id_s) =
bytestrings_from_identifiers(&optional_ids, &client_s_pk.to_arr(), server_s_pk)?;
let aad = construct_aad(&id_u, &id_s, server_s_pk);
let result = Self::seal_raw(key, &nonce, &aad, mode)?;
let result = Self::seal_raw(randomized_pwd_hasher, &nonce, &aad, mode)?;
Ok((
result.0,
client_s_pk,
@@ -147,18 +148,19 @@ impl<CS: CipherSuite> Envelope<CS> {
/// Note that a new nonce is sampled for each call to seal.
#[allow(clippy::type_complexity)]
pub(crate) fn seal_raw(
key: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
nonce: &[u8],
aad: &[u8],
mode: InnerEnvelopeMode,
) -> Result<SealRawResult<CS>, InternalError> {
let h = Hkdf::<CS::Hash>::new(None, key);
let mut hmac_key = vec![0u8; Self::hmac_key_size()];
let mut export_key = vec![0u8; Self::export_key_size()];
h.expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
randomized_pwd_hasher
.expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
.map_err(|_| InternalError::HkdfError)?;
h.expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key)
randomized_pwd_hasher
.expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key)
.map_err(|_| InternalError::HkdfError)?;
let mut hmac =
@@ -182,7 +184,7 @@ impl<CS: CipherSuite> Envelope<CS> {
pub(crate) fn open(
&self,
key: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
server_s_pk: &[u8],
optional_ids: &Option<Identifiers>,
) -> Result<OpenedEnvelope<CS>, ProtocolError> {
@@ -190,7 +192,9 @@ impl<CS: CipherSuite> Envelope<CS> {
InnerEnvelopeMode::Zero => {
return Err(InternalError::IncompatibleEnvelopeModeError.into())
}
InnerEnvelopeMode::Internal => recover_keys_internal::<CS>(key, &self.nonce)?,
InnerEnvelopeMode::Internal => {
recover_keys_internal::<CS>(randomized_pwd_hasher.clone(), &self.nonce)?
}
};
let (id_u, id_s) = bytestrings_from_identifiers(
@@ -200,7 +204,7 @@ impl<CS: CipherSuite> Envelope<CS> {
)?;
let aad = construct_aad(&id_u, &id_s, server_s_pk);
let opened = self.open_raw(key, &aad)?;
let opened = self.open_raw(randomized_pwd_hasher, &aad)?;
Ok(OpenedEnvelope {
client_static_keypair,
@@ -214,16 +218,23 @@ impl<CS: CipherSuite> Envelope<CS> {
/// aad used to construct the envelope are the same.
pub(crate) fn open_raw(
&self,
key: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
aad: &[u8],
) -> Result<OpenedInnerEnvelope<CS::Hash>, InternalError> {
let h = Hkdf::<CS::Hash>::new(None, key);
let mut hmac_key = vec![0u8; Self::hmac_key_size()];
let mut export_key = vec![0u8; Self::export_key_size()];
h.expand(&[&self.nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
randomized_pwd_hasher
.expand(
&[self.nonce.clone(), STR_AUTH_KEY.to_vec()].concat(),
&mut hmac_key,
)
.map_err(|_| InternalError::HkdfError)?;
h.expand(&[&self.nonce, STR_EXPORT_KEY].concat(), &mut export_key)
randomized_pwd_hasher
.expand(
&[self.nonce.clone(), STR_EXPORT_KEY.to_vec()].concat(),
&mut export_key,
)
.map_err(|_| InternalError::HkdfError)?;
let mut hmac =
@@ -292,11 +303,6 @@ impl<CS: CipherSuite> Envelope<CS> {
hmac: GenericArray::clone_from_slice(hmac),
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![(self.hmac.as_ptr(), self.hmac.len())]
}
}
// This can't be derived because of the use of a phantom parameter
@@ -317,17 +323,17 @@ impl<CS: CipherSuite> Drop for Envelope<CS> {
// Helper functions
fn build_inner_envelope_internal<CS: CipherSuite>(
random_pwd: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
nonce: &[u8],
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
let mut keypair_seed = vec![0u8; <CS::KeGroup as KeGroup>::SkLen::USIZE];
randomized_pwd_hasher
.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalError::HkdfError)?;
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
&keypair_seed[..],
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash, _, _>(
Some(&keypair_seed[..]),
GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
)?),
)?;
@@ -335,17 +341,17 @@ fn build_inner_envelope_internal<CS: CipherSuite>(
}
fn recover_keys_internal<CS: CipherSuite>(
random_pwd: &[u8],
randomized_pwd_hasher: Hkdf<CS::Hash>,
nonce: &[u8],
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
let mut keypair_seed = vec![0u8; <CS::KeGroup as KeGroup>::SkLen::USIZE];
randomized_pwd_hasher
.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalError::HkdfError)?;
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
&keypair_seed[..],
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash, _, _>(
Some(&keypair_seed[..]),
GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
)?),
)?;
+20
View File
@@ -45,6 +45,10 @@ pub enum InternalError<T = Infallible> {
IncompatibleEnvelopeModeError,
/// This error occurs when the inner envelope is malformed
InvalidInnerEnvelopeError,
/// Error from the OPRF evaluation
OprfError(voprf::errors::InternalError),
/// Error encountered when attempting to produce a keypair
InvalidKeypairError,
}
impl<T: Debug> Debug for InternalError<T> {
@@ -72,6 +76,8 @@ impl<T: Debug> Debug for InternalError<T> {
f.debug_tuple("IncompatibleEnvelopeModeError").finish()
}
Self::InvalidInnerEnvelopeError => f.debug_tuple("InvalidInnerEnvelopeError").finish(),
Self::OprfError(error) => f.debug_tuple("OprfError").field(error).finish(),
Self::InvalidKeypairError => f.debug_tuple("InvalidKeypairError").finish(),
}
}
}
@@ -102,10 +108,24 @@ impl InternalError {
Self::SealOpenHmacError => InternalError::SealOpenHmacError,
Self::IncompatibleEnvelopeModeError => InternalError::IncompatibleEnvelopeModeError,
Self::InvalidInnerEnvelopeError => InternalError::InvalidInnerEnvelopeError,
Self::OprfError(error) => InternalError::OprfError(error),
Self::InvalidKeypairError => InternalError::InvalidKeypairError,
}
}
}
impl From<voprf::errors::InternalError> for InternalError {
fn from(voprf_error: voprf::errors::InternalError) -> Self {
Self::OprfError(voprf_error)
}
}
impl From<voprf::errors::InternalError> for ProtocolError {
fn from(voprf_error: voprf::errors::InternalError) -> Self {
Self::LibraryError(InternalError::OprfError(voprf_error))
}
}
/// Represents an error in protocol handling
#[derive(Clone, Display, Eq, Hash, PartialEq)]
pub enum ProtocolError<T = Infallible> {
-190
View File
@@ -1,190 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use crate::errors::{InternalError, ProtocolError};
use crate::hash::Hash;
use crate::serialization::i2osp;
use alloc::vec::Vec;
use digest::{BlockInput, Digest};
use generic_array::typenum::Unsigned;
// Computes ceil(x / y)
fn div_ceil(x: usize, y: usize) -> usize {
let additive = (x % y != 0) as usize;
x / y + additive
}
fn xor(x: &[u8], y: &[u8]) -> Result<Vec<u8>, InternalError> {
if x.len() != y.len() {
return Err(InternalError::HashToCurveError);
}
Ok(x.iter().zip(y).map(|(&x1, &x2)| x1 ^ x2).collect())
}
/// Corresponds to the expand_message_xmd() function defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt>
pub fn expand_message_xmd<H: Hash>(
msg: &[u8],
dst: &[u8],
len_in_bytes: usize,
) -> Result<Vec<u8>, ProtocolError> {
let b_in_bytes = <H as Digest>::OutputSize::USIZE;
let r_in_bytes = <H as BlockInput>::BlockSize::USIZE;
let ell = div_ceil(len_in_bytes, b_in_bytes);
if ell > 255 {
return Err(InternalError::HashToCurveError.into());
}
let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat();
let z_pad = i2osp(0, r_in_bytes)?;
let l_i_b_str = i2osp(len_in_bytes, 2)?;
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat();
let mut b: Vec<Vec<u8>> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0]
let mut h = H::new();
h.update(&b[0]);
h.update(&i2osp(1, 1)?);
h.update(&dst_prime);
b.push(h.finalize_reset().to_vec()); // b[1]
let mut uniform_bytes: Vec<u8> = Vec::new();
uniform_bytes.extend_from_slice(&b[1]);
for i in 2..(ell + 1) {
h.update(xor(&b[0], &b[i - 1])?);
h.update(&i2osp(i, 1)?);
h.update(&dst_prime);
b.push(h.finalize_reset().to_vec()); // b[i]
uniform_bytes.extend_from_slice(&b[i]);
}
Ok(uniform_bytes[..len_in_bytes].to_vec())
}
#[cfg(test)]
mod tests {
struct Params {
msg: &'static str,
len_in_bytes: usize,
uniform_bytes: &'static str,
}
#[test]
fn test_expand_message_xmd() {
// Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
let test_vectors: alloc::vec::Vec<Params> = alloc::vec![
Params {
msg: "",
len_in_bytes: 0x20,
uniform_bytes: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c\
92181df928fca88",
},
Params {
msg: "abc",
len_in_bytes: 0x20,
uniform_bytes: "1c38f7c211ef233367b2420d04798fa4698080a8901021a79\
5a1151775fe4da7",
},
Params {
msg: "abcdef0123456789",
len_in_bytes: 0x20,
uniform_bytes: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89",
},
Params {
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqq",
len_in_bytes: 0x20,
uniform_bytes: "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e\
1716b1b964e1c642",
},
Params {
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
len_in_bytes: 0x20,
uniform_bytes: "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c\
350db46f429b771b",
},
Params {
msg: "",
len_in_bytes: 0x80,
uniform_bytes: "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8\
9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991\
e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02\
fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608\
61c0cde2005afc2c114042ee7b5848f5303f0611cf297f",
},
Params {
msg: "abc",
len_in_bytes: 0x80,
uniform_bytes: "fe994ec51bdaa821598047b3121c149b364b178606d5e72b\
fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a\
40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01\
98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127\
1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192",
},
Params {
msg: "abcdef0123456789",
len_in_bytes: 0x80,
uniform_bytes: "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011\
75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030\
88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f\
4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9\
24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be",
},
Params {
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqq",
len_in_bytes: 0x80,
uniform_bytes: "48e256ddba722053ba462b2b93351fc966026e6d6db49318\
9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40\
2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3\
720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00\
0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069",
},
Params {
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
len_in_bytes: 0x80,
uniform_bytes: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2\
746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2\
a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8\
42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf\
378fba044a31f5cb44583a892f5969dcd73b3fa128816e",
},
];
let dst = "QUUX-V01-CS02-with-expander";
for tv in test_vectors {
let uniform_bytes = super::expand_message_xmd::<sha2::Sha256>(
tv.msg.as_bytes(),
dst.as_bytes(),
tv.len_in_bytes,
)
.unwrap();
assert_eq!(tv.uniform_bytes, hex::encode(uniform_bytes));
}
}
}
-78
View File
@@ -1,78 +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.
//! Defines the Group trait to specify the underlying prime order group used in
//! OPAQUE's OPRF
mod expand;
#[cfg(feature = "p256")]
pub(crate) mod p256;
mod ristretto;
mod x25519;
use crate::errors::{InternalError, ProtocolError};
use crate::hash::Hash;
use core::ops::Mul;
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use zeroize::Zeroize;
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
/// subgroup is noted additively — as in the draft RFC — in this trait.
pub trait Group: Copy + Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self> {
/// The ciphersuite identifier as dictated by
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
const SUITE_ID: usize;
/// transforms a password and domain separation tag (DST) into a curve point
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, ProtocolError>;
/// Hashes a slice of pseudo-random bytes to a scalar
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, ProtocolError>;
/// Generates the contextString parameter as defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
fn get_context_string(mode: u8) -> Result<alloc::vec::Vec<u8>, ProtocolError> {
use crate::serialization::i2osp;
Ok([i2osp(mode as usize, 1)?, i2osp(Self::SUITE_ID, 2)?].concat())
}
/// The type of base field scalars
type Scalar: Zeroize + Copy;
/// The byte length necessary to represent scalars
type ScalarLen: ArrayLength<u8> + 'static;
/// Return a scalar from its fixed-length bytes representation
fn from_scalar_slice(
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError>;
/// picks a scalar at random
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
/// Serializes a scalar to bytes
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
/// The multiplicative inverse of this scalar
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar;
/// The byte length necessary to represent group elements
type ElemLen: ArrayLength<u8> + 'static;
/// Return an element from its fixed-length bytes representation
fn from_element_slice(
element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError>;
/// Serializes the `self` group element
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen>;
/// 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;
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool;
/// Compares in constant time if the group elements are equal
fn ct_equal(&self, other: &Self) -> bool;
}
-561
View File
@@ -1,561 +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.
// Note: This group implementation of p256 is experimental for now,
// until hash-to-curve or crypto-bigint are fully supported.
#![allow(
clippy::borrow_interior_mutable_const,
clippy::declare_interior_mutable_const
)]
use super::Group;
use crate::errors::{InternalError, ProtocolError};
use crate::hash::Hash;
use core::ops::{Add, Div, Mul, Neg};
use core::str::FromStr;
use generic_array::typenum::{U32, U33};
use generic_array::{ArrayLength, GenericArray};
use num_bigint::{BigInt, Sign};
use num_integer::Integer;
use num_traits::{One, ToPrimitive, Zero};
use once_cell::unsync::Lazy;
use p256_::elliptic_curve::group::prime::PrimeCurveAffine;
use p256_::elliptic_curve::group::GroupEncoding;
use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
use p256_::elliptic_curve::subtle::ConstantTimeEq;
use p256_::elliptic_curve::Field;
use p256_::{AffinePoint, EncodedPoint, ProjectivePoint};
use rand::{CryptoRng, RngCore};
use subtle::{Choice, ConditionallySelectable};
// `L: 48`
pub const L: usize = 48;
impl Group for ProjectivePoint {
const SUITE_ID: usize = 0x0003;
// Implements the `hash_to_curve()` function from
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, ProtocolError> {
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
// `p: 2^256 - 2^224 + 2^192 + 2^96 - 1`
const P: Lazy<BigInt> = Lazy::new(|| {
BigInt::from_str(
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
)
.unwrap()
});
// `A: -3`
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
// `B: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`
const B: Lazy<BigInt> = Lazy::new(|| {
BigInt::parse_bytes(
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
16,
)
.unwrap()
});
// `Z: -10`
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
// `hash_to_curve` calls `hash_to_field` with a `count` of `2`
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
// `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L`
let uniform_bytes = super::expand::expand_message_xmd::<H>(msg, dst, 2 * L)?;
// map to curve
let (q0x, q0y) = map_to_curve_simple_swu(&uniform_bytes[..L], &A, &B, &P, &Z);
let (q1x, q1y) = map_to_curve_simple_swu(&uniform_bytes[L..], &A, &B, &P, &Z);
// convert to `p256` types
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
&q0x, &q0y, false,
))
.ok_or(InternalError::PointError)?
.to_curve();
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
&q1x, &q1y, false,
))
.ok_or(InternalError::PointError)?;
Ok(p0 + p1)
}
// Implements the `HashToScalar()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.3
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, ProtocolError> {
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0]
// P-256 `n` is defined as `115792089210356248762697446949407573529996955224135760342 422259061068512044369`
const N: Lazy<BigInt> = Lazy::new(|| {
BigInt::from_str(
"115792089210356248762697446949407573529996955224135760342422259061068512044369",
)
.unwrap()
});
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
// `HashToScalar` is `hash_to_field`
let uniform_bytes = super::expand::expand_message_xmd::<H>(input, dst, L)?;
let bytes = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes)
.mod_floor(&N)
.to_bytes_be()
.1;
let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(&bytes);
Ok(p256_::Scalar::from_bytes_reduced(&result))
}
type ElemLen = U33;
type Scalar = p256_::Scalar;
type ScalarLen = U32;
fn from_scalar_slice(
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError> {
Ok(Self::Scalar::from_bytes_reduced(scalar_bits))
}
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Self::Scalar::random(rng)
}
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.into()
}
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
scalar.invert().unwrap_or(Self::Scalar::zero())
}
fn from_element_slice(
element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError> {
Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError)
}
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
let bytes = self.to_affine().to_encoded_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(bytes);
result
}
fn base_point() -> Self {
Self::generator()
}
fn mult_by_slice(&self, scalar: &GenericArray<u8, Self::ScalarLen>) -> Self {
self * &Self::Scalar::from_bytes_reduced(scalar)
}
fn is_identity(&self) -> bool {
self == &Self::identity()
}
fn ct_equal(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
/// Corresponds to the map_to_curve_simple_swu() function defined in
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-F.2>
// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which
// will be supported after crypto-bigint is no longer experimental. See
// https://github.com/novifinancial/opaque-ke/issues/239 for more context.
#[allow(clippy::many_single_char_names)]
fn map_to_curve_simple_swu<N: ArrayLength<u8>>(
u: &[u8],
a: &BigInt,
b: &BigInt,
p: &BigInt,
z: &BigInt,
) -> (GenericArray<u8, N>, GenericArray<u8, N>) {
#[derive(Clone)]
struct Field<'a>(&'a BigInt);
impl<'a> Field<'a> {
fn new(p: &'a BigInt) -> Self {
Self(p)
}
fn element(&'a self, number: &BigInt) -> FieldElement<'a> {
FieldElement {
number: number.mod_floor(self.0),
f: self,
}
}
fn one(&'a self) -> FieldElement<'a> {
self.element(&BigInt::one())
}
}
/// Finite field arithmetic
#[derive(Clone)]
struct FieldElement<'a> {
number: BigInt,
f: &'a Field<'a>,
}
impl<'a> Add for FieldElement<'a> {
type Output = FieldElement<'a>;
fn add(self, rhs: Self) -> Self::Output {
&self + &rhs
}
}
impl<'a> Add for &FieldElement<'a> {
type Output = FieldElement<'a>;
fn add(self, rhs: Self) -> Self::Output {
self.f.element(&(&self.number + &rhs.number))
}
}
impl<'a> Neg for FieldElement<'a> {
type Output = FieldElement<'a>;
fn neg(self) -> Self::Output {
-&self
}
}
impl<'a> Neg for &FieldElement<'a> {
type Output = FieldElement<'a>;
fn neg(self) -> Self::Output {
self.f.element(&-&self.number)
}
}
impl<'a> Mul for FieldElement<'a> {
type Output = FieldElement<'a>;
fn mul(self, rhs: Self) -> Self::Output {
&self * &rhs
}
}
impl<'a> Mul<&Self> for FieldElement<'a> {
type Output = FieldElement<'a>;
fn mul(self, rhs: &Self) -> Self::Output {
&self * rhs
}
}
impl<'a> Mul<FieldElement<'a>> for &FieldElement<'a> {
type Output = FieldElement<'a>;
fn mul(self, rhs: FieldElement<'a>) -> Self::Output {
self * &rhs
}
}
impl<'a> Mul for &FieldElement<'a> {
type Output = FieldElement<'a>;
fn mul(self, rhs: Self) -> Self::Output {
self.f.element(&(&self.number * &rhs.number))
}
}
impl<'a> Div<&Self> for FieldElement<'a> {
type Output = FieldElement<'a>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: &Self) -> Self::Output {
self * rhs.inv0()
}
}
impl<'a> FieldElement<'a> {
fn square(&self) -> Self {
self * self
}
fn pow_internal(&self, exponent: &BigInt) -> Self {
let exponent = exponent.mod_floor(&(self.f.0 - 1));
Self {
number: self.number.modpow(&exponent, self.f.0),
f: self.f,
}
}
/// Corresponds to the sqrt_3mod4() function defined in
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.1>
fn sqrt(&self) -> Self {
// constant
let c1 = (self.f.0 + 1) >> 2;
self.pow_internal(&c1)
}
/// Corresponds to the sgn0_m_eq_1() function defined in
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1>
fn sgn0(&self) -> i32 {
(&self.number % 2_usize).to_i32().unwrap()
}
/// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
fn inv0(&self) -> Self {
self.pow_internal(&(self.f.0 - 2))
}
fn is_zero(&self) -> bool {
self.number.is_zero()
}
/// Corresponds to the is_square() function defined in
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
fn is_square(&self) -> bool {
// constant
let exponent = (self.f.0 - 1) >> 1;
let result = self.pow_internal(&exponent);
result.is_zero() || result.number.is_one()
}
fn to_bytes<N: ArrayLength<u8>>(&self) -> GenericArray<u8, N> {
let bytes = self.number.to_bytes_be().1;
let mut result = GenericArray::default();
result[N::USIZE - bytes.len()..].copy_from_slice(&bytes);
result
}
}
fn cmov<'a>(x: &FieldElement<'a>, y: &FieldElement<'a>, b: bool) -> FieldElement<'a> {
let f = x.f;
let x_bytes = x.number.to_bytes_le().1;
let mut x = [0; 32];
x[..x_bytes.len()].copy_from_slice(&x_bytes);
let y_bytes = y.number.to_bytes_le().1;
let mut y = [0; 32];
y[..y_bytes.len()].copy_from_slice(&y_bytes);
let mut bytes = [0; 32];
let choice = Choice::from(u8::from(b));
for ((byte, x), y) in bytes.iter_mut().zip(&x).zip(&y) {
*byte = u8::conditional_select(x, y, choice);
}
FieldElement {
f,
number: BigInt::from_bytes_le(Sign::Plus, &bytes),
}
}
let f = Field::new(p);
let a = f.element(a);
let b = f.element(b);
let z = f.element(z);
let u = f.element(&BigInt::from_bytes_be(Sign::Plus, u));
// Constants:
// 1. c1 = -B / A
let c1 = -&b / &a;
// 2. c2 = -1 / Z
let c2 = -f.one() / &z;
// Steps:
// 1. tv1 = Z * u^2
let tv1 = z * u.square();
// 2. tv2 = tv1^2
let mut tv2 = tv1.square();
// 3. x1 = tv1 + tv2
let mut x1 = &tv1 + &tv2;
// 4. x1 = inv0(x1)
x1 = x1.inv0();
// 5. e1 = x1 == 0
let e1 = x1.is_zero();
// 6. x1 = x1 + 1
x1 = x1 + f.one();
// 7. x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z
x1 = cmov(&x1, &c2, e1);
// 8. x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2)))
x1 = x1 * c1;
// 9. gx1 = x1^2
let mut gx1 = x1.square();
// 10. gx1 = gx1 + A
gx1 = gx1 + a;
// 11. gx1 = gx1 * x1
gx1 = gx1 * &x1;
// 12. gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B
gx1 = gx1 + b;
// 13. x2 = tv1 * x1 # x2 = Z * u^2 * x1
let x2 = &tv1 * &x1;
// 14. tv2 = tv1 * tv2
tv2 = tv1 * tv2;
// 15. gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1
let gx2 = &gx1 * tv2;
// 16. e2 = is_square(gx1)
let e2 = gx1.is_square();
// 17. x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2
let x = cmov(&x2, &x1, e2);
// 18. y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2
let y2 = cmov(&gx2, &gx1, e2);
// 19. y = sqrt(y2)
let mut y = y2.sqrt();
// 20. e3 = sgn0(u) == sgn0(y) # Fix sign of y
let e3 = u.sgn0() == y.sgn0();
// 21. y = CMOV(-y, y, e3)
y = cmov(&-&y, &y, e3);
// 22. return (x, y)
(x.to_bytes(), y.to_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
struct Params {
msg: &'static str,
px: &'static str,
py: &'static str,
u0: &'static str,
u1: &'static str,
q0x: &'static str,
q0y: &'static str,
q1x: &'static str,
q1y: &'static str,
}
#[test]
fn map_to_curve_simple_swu() {
const P: Lazy<BigInt> = Lazy::new(|| {
BigInt::from_str(
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
)
.unwrap()
});
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
const B: Lazy<BigInt> = Lazy::new(|| {
BigInt::parse_bytes(
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
16,
)
.unwrap()
});
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
// Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1
let test_vectors = alloc::vec![
Params {
msg: "",
px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4",
py: "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415",
u0: "ad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009",
u1: "8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a",
q0x: "ab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5",
q0y: "dccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1",
q1x: "51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5",
q1y: "b45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac",
},
Params {
msg: "abc",
px: "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f",
py: "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e",
u0: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1",
u1: "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0",
q0x: "5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48",
q0y: "7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf",
q1x: "019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f",
q1y: "589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e",
},
Params {
msg: "abcdef0123456789",
px: "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80",
py: "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3",
u0: "0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c",
u1: "b68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb",
q0x: "a17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2",
q0y: "4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e",
q1x: "7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66",
q1y: "b765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9",
},
Params {
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
qqqqqqqqqqqqqqqqqqqqqqqqq",
px: "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d",
py: "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e",
u0: "3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919",
u1: "76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33",
q0x: "c76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398",
q0y: "776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1",
q1x: "418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75",
q1y: "fd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff",
},
Params {
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
px: "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5",
py: "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc",
u0: "4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec",
u1: "4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee",
q0x: "d88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8",
q0y: "bb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40",
q1x: "a281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b",
q1y: "f6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184",
},
];
let dst = "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_";
for tv in test_vectors {
let uniform_bytes = super::super::expand::expand_message_xmd::<sha2::Sha256>(
tv.msg.as_bytes(),
dst.as_bytes(),
96,
)
.unwrap();
let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P);
let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P);
assert_eq!(BigInt::parse_bytes(tv.u0.as_bytes(), 16).unwrap(), u0);
assert_eq!(BigInt::parse_bytes(tv.u1.as_bytes(), 16).unwrap(), u1);
let (q0x, q0y) = super::map_to_curve_simple_swu(&u0.to_bytes_be().1, &A, &B, &P, &Z);
let (q1x, q1y) = super::map_to_curve_simple_swu(&u1.to_bytes_be().1, &A, &B, &P, &Z);
assert_eq!(tv.q0x, hex::encode(q0x));
assert_eq!(tv.q0y, hex::encode(q0y));
assert_eq!(tv.q1x, hex::encode(q1x));
assert_eq!(tv.q1y, hex::encode(q1y));
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
&q0x, &q0y, false,
))
.unwrap()
.to_curve();
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
&q1x, &q1y, false,
))
.unwrap();
let p = (p0 + p1).to_encoded_point(false);
assert_eq!(tv.px, hex::encode(p.x().unwrap()));
assert_eq!(tv.py, hex::encode(p.y().unwrap()));
}
}
}
-122
View File
@@ -1,122 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use super::Group;
use crate::errors::{InternalError, ProtocolError};
use crate::hash::Hash;
use core::convert::TryInto;
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::Identity,
};
use generic_array::{typenum::U32, GenericArray};
use rand::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
/// The implementation of such a subgroup for Ristretto
impl Group for RistrettoPoint {
const SUITE_ID: usize = 0x0001;
// 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, ProtocolError> {
let uniform_bytes = super::expand::expand_message_xmd::<H>(msg, dst, 64)?;
Ok(RistrettoPoint::from_uniform_bytes(
uniform_bytes
.as_slice()
.try_into()
.map_err(|_| InternalError::HashToCurveError)?,
))
}
// Implements the `HashToScalar()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, ProtocolError> {
let uniform_bytes = super::expand::expand_message_xmd::<H>(input, dst, 64)?;
Ok(Scalar::from_bytes_mod_order_wide(
uniform_bytes
.as_slice()
.try_into()
.map_err(|_| InternalError::HashToCurveError)?,
))
}
type Scalar = Scalar;
type ScalarLen = U32;
fn from_scalar_slice(
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError> {
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
}
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// 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)
}
};
if scalar != Scalar::zero() {
break scalar;
}
}
}
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.to_bytes().into()
}
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
scalar.invert()
}
// The byte length necessary to represent group elements
type ElemLen = U32;
fn from_element_slice(
element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError> {
CompressedRistretto::from_slice(element_bits)
.decompress()
.ok_or(InternalError::PointError)
}
// serialization of a group element
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
self.compress().to_bytes().into()
}
fn base_point() -> Self {
RISTRETTO_BASEPOINT_POINT
}
fn mult_by_slice(&self, scalar: &GenericArray<u8, Self::ScalarLen>) -> Self {
self * Scalar::from_bits(*scalar.as_ref())
}
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool {
self == &Self::identity()
}
fn ct_equal(&self, other: &Self) -> bool {
ConstantTimeEq::ct_eq(self, other).into()
}
}
-184
View File
@@ -1,184 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use super::Group;
use crate::errors::{InternalError, ProtocolError};
use crate::hash::Hash;
use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar};
use generic_array::{typenum::U32, GenericArray};
use rand::{CryptoRng, RngCore};
/// The implementation of such a subgroup for Ristretto
impl Group for MontgomeryPoint {
const SUITE_ID: usize = 0xFFFF;
fn map_to_curve<H: Hash>(_msg: &[u8], _dst: &[u8]) -> Result<Self, ProtocolError> {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
fn hash_to_scalar<H: Hash>(_input: &[u8], _dst: &[u8]) -> Result<Self::Scalar, ProtocolError> {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
type Scalar = Scalar;
type ScalarLen = U32;
fn from_scalar_slice(
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError> {
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
}
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// 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)
}
};
if scalar != Scalar::zero() {
break scalar;
}
}
}
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.to_bytes().into()
}
fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
// The byte length necessary to represent group elements
type ElemLen = U32;
fn from_element_slice(
element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError> {
Ok(Self(*element_bits.as_ref()))
}
// serialization of a group element
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
self.to_bytes().into()
}
fn base_point() -> Self {
X25519_BASEPOINT
}
fn mult_by_slice(&self, scalar: &GenericArray<u8, Self::ScalarLen>) -> Self {
self * Scalar::from_bits(*scalar.as_ref())
}
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
fn ct_equal(&self, _other: &Self) -> bool {
unreachable!("this algorithm should only be used as the `KeGroup`")
}
}
#[test]
fn test() -> Result<(), ProtocolError> {
use crate::{
key_exchange::tripledh::TripleDH, slow_hash::NoOpHash, CipherSuite, ClientLogin,
ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
ClientRegistrationStartResult, ServerLogin, ServerLoginStartParameters,
ServerLoginStartResult, ServerRegistration, ServerSetup,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use rand::rngs::OsRng;
struct X25519Sha512NoSlowHash;
impl CipherSuite for X25519Sha512NoSlowHash {
type OprfGroup = RistrettoPoint;
type KeGroup = MontgomeryPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = NoOpHash;
}
const PASSWORD: &[u8] = b"1234";
let server_setup = ServerSetup::<X25519Sha512NoSlowHash>::new(&mut OsRng);
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::start(&mut OsRng, PASSWORD)?;
let message = ServerRegistration::start(&server_setup, message, &[])?.message;
let ClientRegistrationFinishResult {
message,
export_key: register_export_key,
..
} = client.finish(
&mut OsRng,
message,
ClientRegistrationFinishParameters::default(),
)?;
let server_registration = ServerRegistration::finish(message);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult {
message,
state: server,
..
} = ServerLogin::start(
&mut OsRng,
&server_setup,
Some(server_registration),
message,
&[],
ServerLoginStartParameters::default(),
)?;
let ClientLoginFinishResult {
message,
session_key: client_session_key,
export_key: login_export_key,
..
} = client.finish(message, ClientLoginFinishParameters::default())?;
let server_session_key = server.finish(message)?.session_key;
assert_eq!(register_export_key, login_export_key);
assert_eq!(client_session_key, server_session_key);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult { message, .. } = ServerLogin::start(
&mut OsRng,
&server_setup,
None,
message,
&[],
ServerLoginStartParameters::default(),
)?;
assert!(matches!(
client.finish(message, ClientLoginFinishParameters::default()),
Err(ProtocolError::InvalidLoginError)
));
Ok(())
}
+10 -37
View File
@@ -110,10 +110,13 @@ macro_rules! impl_serialize_and_deserialize_for {
where
S: serde::Serializer,
{
use serde::ser::Error;
if serializer.is_human_readable() {
serializer.serialize_str(&base64::encode(&self.serialize()))
serializer
.serialize_str(&base64::encode(&self.serialize().map_err(Error::custom)?))
} else {
serializer.serialize_bytes(&self.serialize())
serializer.serialize_bytes(&self.serialize().map_err(Error::custom)?)
}
}
}
@@ -124,45 +127,15 @@ macro_rules! impl_serialize_and_deserialize_for {
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
if deserializer.is_human_readable() {
let s = <&str>::deserialize(deserializer)?;
$t::<CS>::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
.map_err(serde::de::Error::custom)
Self::deserialize(&base64::decode(s).map_err(Error::custom)?)
} else {
struct ByteVisitor<CS: CipherSuite> {
marker: core::marker::PhantomData<CS>,
}
impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor<CS> {
type Value = $t<CS>;
fn expecting(
&self,
formatter: &mut core::fmt::Formatter,
) -> core::fmt::Result {
formatter.write_str(core::concat!(
"the byte representation of a ",
core::stringify!($t)
))
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
$t::<CS>::deserialize(value).map_err(|_| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Bytes(value),
&core::concat!(
"invalid byte sequence for ",
core::stringify!($t)
),
)
})
}
}
deserializer.deserialize_bytes(ByteVisitor::<CS> {
marker: core::marker::PhantomData,
})
Self::deserialize(<&[u8]>::deserialize(deserializer)?)
}
.map_err(Error::custom)
}
}
};
+39
View File
@@ -0,0 +1,39 @@
// 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.
//! Includes the KeGroup trait and definitions for the
//! key exchange groups
use crate::errors::InternalError;
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
/// A group representation for use in the key exchange
pub trait KeGroup: Sized + Clone {
/// Length of the public key
type PkLen: ArrayLength<u8> + 'static;
/// Length of the secret key
type SkLen: ArrayLength<u8> + 'static;
/// Return a public key from its fixed-length bytes representation
fn from_pk_slice(element_bits: &GenericArray<u8, Self::PkLen>) -> Result<Self, InternalError>;
/// Generate a random secret key
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Self::SkLen>;
/// Return a public key from its secret key
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self;
/// Serializes `self`
fn to_arr(&self) -> GenericArray<u8, Self::PkLen>;
/// Diffie-Hellman key exchange
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen>;
}
#[cfg(feature = "p256")]
pub mod p256;
pub mod ristretto255;
pub mod x25519;
+47
View File
@@ -0,0 +1,47 @@
// 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.
//! Key Exchange group implementation for p256
use super::KeGroup;
use crate::errors::InternalError;
use generic_array::typenum::{U32, U33};
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
impl KeGroup for p256_::ProjectivePoint {
type PkLen = U33;
type SkLen = U32;
fn from_pk_slice(element_bits: &GenericArray<u8, Self::PkLen>) -> Result<Self, InternalError> {
use p256_::elliptic_curve::group::GroupEncoding;
Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError)
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Self::SkLen> {
use p256_::elliptic_curve::Field;
p256_::Scalar::random(rng).into()
}
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self {
Self::generator() * p256_::Scalar::from_bytes_reduced(sk)
}
fn to_arr(&self) -> GenericArray<u8, Self::PkLen> {
use p256_::elliptic_curve::sec1::ToEncodedPoint;
let bytes = self.to_affine().to_encoded_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(bytes);
result
}
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen> {
(self * &p256_::Scalar::from_bytes_reduced(sk)).to_arr()
}
}
+63
View File
@@ -0,0 +1,63 @@
// 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.
//! Key Exchange group implementation for ristretto255
use super::KeGroup;
use crate::errors::InternalError;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use generic_array::typenum::U32;
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
impl KeGroup for RistrettoPoint {
type PkLen = U32;
type SkLen = U32;
fn from_pk_slice(element_bits: &GenericArray<u8, Self::PkLen>) -> Result<Self, InternalError> {
CompressedRistretto::from_slice(element_bits)
.decompress()
.ok_or(InternalError::PointError)
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Self::SkLen> {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// 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)
}
};
if scalar != Scalar::zero() {
break scalar.to_bytes().into();
}
}
}
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self {
RISTRETTO_BASEPOINT_POINT * Scalar::from_bits(*sk.as_ref())
}
fn to_arr(&self) -> GenericArray<u8, Self::PkLen> {
self.compress().to_bytes().into()
}
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen> {
(self * Scalar::from_bits(*sk.as_ref())).to_arr()
}
}
+154
View File
@@ -0,0 +1,154 @@
// 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.
//! Key Exchange group implementation for x25519
use super::KeGroup;
use crate::errors::InternalError;
use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar};
use generic_array::{typenum::U32, GenericArray};
use rand::{CryptoRng, RngCore};
/// The implementation of such a subgroup for Ristretto
impl KeGroup for MontgomeryPoint {
type PkLen = U32;
type SkLen = U32;
fn from_pk_slice(element_bits: &GenericArray<u8, Self::PkLen>) -> Result<Self, InternalError> {
Ok(Self(*element_bits.as_ref()))
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Self::SkLen> {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// 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)
}
};
if scalar != Scalar::zero() {
break GenericArray::clone_from_slice(&scalar.to_bytes());
}
}
}
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self {
X25519_BASEPOINT * Scalar::from_bits(*sk.as_ref())
}
fn to_arr(&self) -> GenericArray<u8, Self::PkLen> {
self.to_bytes().into()
}
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen> {
(self * Scalar::from_bits(*sk.as_ref())).to_arr()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ProtocolError;
#[test]
fn test_x25519() -> Result<(), ProtocolError> {
use crate::{
key_exchange::tripledh::TripleDH, slow_hash::NoOpHash, CipherSuite, ClientLogin,
ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
ClientRegistrationStartResult, ServerLogin, ServerLoginStartParameters,
ServerLoginStartResult, ServerRegistration, ServerSetup,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use rand::rngs::OsRng;
struct X25519Sha512NoSlowHash;
impl CipherSuite for X25519Sha512NoSlowHash {
type OprfGroup = RistrettoPoint;
type KeGroup = MontgomeryPoint;
type KeyExchange = TripleDH;
type Hash = sha2::Sha512;
type SlowHash = NoOpHash;
}
const PASSWORD: &[u8] = b"1234";
let server_setup = ServerSetup::<X25519Sha512NoSlowHash>::new(&mut OsRng)?;
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::start(&mut OsRng, PASSWORD)?;
let message = ServerRegistration::start(&server_setup, message, &[])?.message;
let ClientRegistrationFinishResult {
message,
export_key: register_export_key,
..
} = client.finish(
&mut OsRng,
message,
ClientRegistrationFinishParameters::default(),
)?;
let server_registration = ServerRegistration::finish(message);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult {
message,
state: server,
..
} = ServerLogin::start(
&mut OsRng,
&server_setup,
Some(server_registration),
message,
&[],
ServerLoginStartParameters::default(),
)?;
let ClientLoginFinishResult {
message,
session_key: client_session_key,
export_key: login_export_key,
..
} = client.finish(message, ClientLoginFinishParameters::default())?;
let server_session_key = server.finish(message)?.session_key;
assert_eq!(register_export_key, login_export_key);
assert_eq!(client_session_key, server_session_key);
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::start(&mut OsRng, PASSWORD)?;
let ServerLoginStartResult { message, .. } = ServerLogin::start(
&mut OsRng,
&server_setup,
None,
message,
&[],
ServerLoginStartParameters::default(),
)?;
assert!(matches!(
client.finish(message, ClientLoginFinishParameters::default()),
Err(ProtocolError::InvalidLoginError)
));
Ok(())
}
}
+1
View File
@@ -6,5 +6,6 @@
//! Includes instantiations of key exchange protocols used in the
//! login step for OPAQUE
pub mod group;
pub(crate) mod traits;
pub mod tripledh;
+4 -12
View File
@@ -3,10 +3,10 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use crate::key_exchange::group::KeGroup;
use crate::{
ciphersuite::CipherSuite,
errors::ProtocolError,
group::Group,
hash::Hash,
keypair::{PrivateKey, PublicKey, SecretKey},
};
@@ -36,9 +36,9 @@ pub type GenerateKe3Result<K, D, G> = (
generic_array::GenericArray<u8, <D as digest::Digest>::OutputSize>,
);
pub trait KeyExchange<D: Hash, G: Group> {
type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
type KE2State: FromBytes + ToBytesWithPointers + Zeroize + Clone;
pub trait KeyExchange<D: Hash, G: KeGroup> {
type KE1State: FromBytes + ToBytes + Zeroize + Clone;
type KE2State: FromBytes + ToBytes + Zeroize + Clone;
type KE1Message: FromBytes + ToBytes + Clone;
type KE2Message: FromBytes + ToBytes + Clone;
type KE3Message: FromBytes + ToBytes + Clone;
@@ -89,11 +89,3 @@ pub trait FromBytes: Sized {
pub trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
}
pub trait ToBytesWithPointers {
fn to_bytes(&self) -> Vec<u8>;
// Only used for tests to grab raw pointers to data
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)>;
}
+49 -65
View File
@@ -10,10 +10,10 @@ use crate::{
utils::{check_slice_size, check_slice_size_atleast},
InternalError, ProtocolError,
},
group::Group,
hash::Hash,
key_exchange::traits::{
FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes, ToBytesWithPointers,
key_exchange::{
group::KeGroup,
traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes},
},
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
serialization::serialize,
@@ -55,26 +55,26 @@ pub struct TripleDH;
/// The client state produced after the first key exchange message
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
pub struct Ke1State<G: Group> {
client_e_sk: PrivateKey<G>,
pub struct Ke1State<KG: KeGroup> {
client_e_sk: PrivateKey<KG>,
client_nonce: GenericArray<u8, NonceLen>,
}
impl_clone_for!(
struct Ke1State<G: Group>,
struct Ke1State<KG: KeGroup>,
[client_e_sk, client_nonce],
);
impl_debug_eq_hash_for!(
struct Ke1State<G: Group>,
struct Ke1State<KG: KeGroup>,
[client_e_sk, client_nonce],
);
/// The first key exchange message
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
pub struct Ke1Message<G: Group> {
pub struct Ke1Message<KG: KeGroup> {
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
pub(crate) client_e_pk: PublicKey<G>,
pub(crate) client_e_pk: PublicKey<KG>,
}
/// The server state produced after the second key exchange message
@@ -91,9 +91,9 @@ pub struct Ke2State<HashLen: ArrayLength<u8>> {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serialize", serde(bound = ""))]
pub struct Ke2Message<G: Group, HashLen: ArrayLength<u8>> {
pub struct Ke2Message<KG: KeGroup, HashLen: ArrayLength<u8>> {
server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: PublicKey<G>,
server_e_pk: PublicKey<KG>,
mac: GenericArray<u8, HashLen>,
}
@@ -110,17 +110,17 @@ pub struct Ke3Message<HashLen: ArrayLength<u8>> {
// ========================== //
////////////////////////////////
impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE1State = Ke1State<G>;
impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
type KE1State = Ke1State<KG>;
type KE2State = Ke2State<<D as FixedOutput>::OutputSize>;
type KE1Message = Ke1Message<G>;
type KE2Message = Ke2Message<G, <D as FixedOutput>::OutputSize>;
type KE1Message = Ke1Message<KG>;
type KE2Message = Ke2Message<KG, <D as FixedOutput>::OutputSize>;
type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>(
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng);
let client_e_kp = KeyPair::<KG>::generate_random(rng)?;
let client_nonce = generate_nonce::<R>(rng);
let ke1_message = Ke1Message {
@@ -138,18 +138,19 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
}
#[allow(clippy::type_complexity)]
fn generate_ke2<R: RngCore + CryptoRng, S: SecretKey<G>>(
fn generate_ke2<R: RngCore + CryptoRng, S: SecretKey<KG>>(
rng: &mut R,
serialized_credential_request: Vec<u8>,
l2_bytes: Vec<u8>,
ke1_message: Self::KE1Message,
client_s_pk: PublicKey<G>,
client_s_pk: PublicKey<KG>,
server_s_sk: S,
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<GenerateKe2Result<Self, D, G>, ProtocolError<S::Error>> {
let server_e_kp = KeyPair::<G>::generate_random(rng);
) -> Result<GenerateKe2Result<Self, D, KG>, ProtocolError<S::Error>> {
let server_e_kp =
KeyPair::<KG>::generate_random(rng).map_err(|_| InternalError::InvalidKeypairError)?;
let server_nonce = generate_nonce::<R>(rng);
let mut transcript_hasher = D::new()
@@ -162,7 +163,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
.chain(&server_nonce[..])
.chain(&server_e_kp.public().to_arr());
let result = derive_3dh_keys::<D, G, S>(
let result = derive_3dh_keys::<D, KG, S>(
TripleDHComponents {
pk1: ke1_message.client_e_pk.clone(),
sk1: server_e_kp.private().clone(),
@@ -205,12 +206,12 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State,
serialized_credential_request: &[u8],
server_s_pk: PublicKey<G>,
client_s_sk: PrivateKey<G>,
server_s_pk: PublicKey<KG>,
client_s_sk: PrivateKey<KG>,
id_u: Vec<u8>,
id_s: Vec<u8>,
context: Vec<u8>,
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError> {
) -> Result<GenerateKe3Result<Self, D, KG>, ProtocolError> {
let mut transcript_hasher = D::new()
.chain(STR_RFC)
.chain(&serialize(&context, 2)?)
@@ -220,7 +221,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
.chain(&l2_component[..])
.chain(&ke2_message.to_bytes_without_info_or_mac());
let result = derive_3dh_keys::<D, G, PrivateKey<G>>(
let result = derive_3dh_keys::<D, KG, PrivateKey<KG>>(
TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(),
sk1: ke1_state.client_e_sk.clone(),
@@ -275,7 +276,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
}
fn ke2_message_size() -> usize {
NonceLen::USIZE + <G as Group>::ElemLen::USIZE + <D as FixedOutput>::OutputSize::USIZE
NonceLen::USIZE + <KG as KeGroup>::PkLen::USIZE + <D as FixedOutput>::OutputSize::USIZE
}
}
@@ -286,13 +287,13 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
#[allow(clippy::upper_case_acronyms)]
// The triple of public and private components used in the 3DH computation
struct TripleDHComponents<G: Group, S: SecretKey<G>> {
pk1: PublicKey<G>,
sk1: PrivateKey<G>,
pk2: PublicKey<G>,
struct TripleDHComponents<KG: KeGroup, S: SecretKey<KG>> {
pk1: PublicKey<KG>,
sk1: PrivateKey<KG>,
pk2: PublicKey<KG>,
sk2: S,
pk3: PublicKey<G>,
sk3: PrivateKey<G>,
pk3: PublicKey<KG>,
sk3: PrivateKey<KG>,
}
// Consists of a session key, followed by two mac keys: (session_key, km2, km3)
@@ -320,8 +321,8 @@ type TripleDHDerivationResult<D> = (
// Internal function which takes the public and private components of the client and server keypairs, along
// with some auxiliary metadata, to produce the session key and two MAC keys
fn derive_3dh_keys<D: Hash, G: Group, S: SecretKey<G>>(
dh: TripleDHComponents<G, S>,
fn derive_3dh_keys<D: Hash, KG: KeGroup, S: SecretKey<KG>>(
dh: TripleDHComponents<KG, S>,
hashed_derivation_transcript: &[u8],
) -> Result<TripleDHDerivationResult<D>, ProtocolError<S::Error>> {
let ikm: Vec<u8> = [
@@ -430,9 +431,9 @@ fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Nonce
// Serialization and deserialization implementations
impl<G: Group> FromBytes for Ke1State<G> {
impl<KG: KeGroup> FromBytes for Ke1State<KG> {
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <G as Group>::ElemLen::USIZE;
let key_len = <KG as KeGroup>::PkLen::USIZE;
let nonce_len = NonceLen::USIZE;
let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?;
@@ -446,27 +447,19 @@ impl<G: Group> FromBytes for Ke1State<G> {
}
}
impl<G: Group> ToBytesWithPointers for Ke1State<G> {
impl<KG: KeGroup> ToBytes for Ke1State<KG> {
fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
output
}
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.client_e_sk.as_ptr(), G::ScalarLen::USIZE),
(self.client_nonce.as_ptr(), NonceLen::USIZE),
]
}
}
impl<G: Group> FromBytes for Ke1Message<G> {
impl<KG: KeGroup> FromBytes for Ke1Message<KG> {
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
let nonce_len = NonceLen::USIZE;
let checked_nonce = check_slice_size(
ke1_message_bytes,
nonce_len + <G as Group>::ElemLen::USIZE,
nonce_len + <KG as KeGroup>::PkLen::USIZE,
"ke1_message nonce",
)?;
@@ -477,7 +470,7 @@ impl<G: Group> FromBytes for Ke1Message<G> {
}
}
impl<G: Group> ToBytes for Ke1Message<G> {
impl<KG: KeGroup> ToBytes for Ke1Message<KG> {
fn to_bytes(&self) -> Vec<u8> {
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
}
@@ -498,7 +491,7 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
}
}
impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
impl<HashLen: ArrayLength<u8>> ToBytes for Ke2State<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[
&self.km3[..],
@@ -507,20 +500,11 @@ impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
]
.concat()
}
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.km3.as_ptr(), HashLen::USIZE),
(self.hashed_transcript.as_ptr(), HashLen::USIZE),
(self.session_key.as_ptr(), HashLen::USIZE),
]
}
}
impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
impl<KG: KeGroup, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<KG, HashLen> {
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <G as Group>::ElemLen::USIZE;
let key_len = <KG as KeGroup>::PkLen::USIZE;
let nonce_len = NonceLen::USIZE;
let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?;
@@ -536,7 +520,7 @@ impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
)?;
// Check the public key bytes
let server_e_pk = KeyPair::<CS::OprfGroup>::check_public_key(PublicKey::from_bytes(
let server_e_pk = KeyPair::<CS::KeGroup>::check_public_key(PublicKey::from_bytes(
&unchecked_server_e_pk[..key_len],
)?)?;
@@ -548,13 +532,13 @@ impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
}
}
impl<G: Group, HashLen: ArrayLength<u8>> ToBytes for Ke2Message<G, HashLen> {
impl<KG: KeGroup, HashLen: ArrayLength<u8>> ToBytes for Ke2Message<KG, HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat()
}
}
impl<G: Group, HashLen: ArrayLength<u8>> Ke2Message<G, HashLen> {
impl<KG: KeGroup, HashLen: ArrayLength<u8>> Ke2Message<KG, HashLen> {
fn to_bytes_without_info_or_mac(&self) -> Vec<u8> {
[&self.server_nonce[..], &self.server_e_pk.to_arr()].concat()
}
@@ -579,14 +563,14 @@ impl<HashLen: ArrayLength<u8>> ToBytes for Ke3Message<HashLen> {
// Zeroize on drop implementations
// This can't be derived because of the use of a generic parameter
impl<G: Group> Zeroize for Ke1State<G> {
impl<KG: KeGroup> Zeroize for Ke1State<KG> {
fn zeroize(&mut self) {
self.client_e_sk.zeroize();
self.client_nonce.zeroize();
}
}
impl<G: Group> Drop for Ke1State<G> {
impl<KG: KeGroup> Drop for Ke1State<KG> {
fn drop(&mut self) {
self.zeroize();
}
+72 -80
View File
@@ -8,7 +8,7 @@
#![allow(unsafe_code)]
use crate::errors::{InternalError, ProtocolError};
use crate::group::Group;
use crate::key_exchange::group::KeGroup;
use alloc::vec::Vec;
use core::fmt::Debug;
use core::ops::Deref;
@@ -26,12 +26,12 @@ use zeroize::Zeroize;
serialize = "S: serde::Serialize"
))
)]
pub struct KeyPair<G: Group, S: SecretKey<G> = PrivateKey<G>> {
pk: PublicKey<G>,
pub struct KeyPair<KG: KeGroup, S: SecretKey<KG> = PrivateKey<KG>> {
pk: PublicKey<KG>,
sk: S,
}
impl<G: Group, S: SecretKey<G>> Clone for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG>> Clone for KeyPair<KG, S> {
fn clone(&self) -> Self {
Self {
pk: self.pk.clone(),
@@ -40,7 +40,7 @@ impl<G: Group, S: SecretKey<G>> Clone for KeyPair<G, S> {
}
}
impl<G: Group, S: SecretKey<G> + Debug> Debug for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG> + Debug> Debug for KeyPair<KG, S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("KeyPair")
.field("pk", &self.pk)
@@ -49,15 +49,15 @@ impl<G: Group, S: SecretKey<G> + Debug> Debug for KeyPair<G, S> {
}
}
impl<G: Group, S: SecretKey<G> + PartialEq> PartialEq for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG> + PartialEq> PartialEq for KeyPair<KG, S> {
fn eq(&self, other: &Self) -> bool {
self.pk.eq(&other.pk) && self.sk.eq(&other.sk)
}
}
impl<G: Group, S: SecretKey<G> + Eq> Eq for KeyPair<G, S> {}
impl<KG: KeGroup, S: SecretKey<KG> + Eq> Eq for KeyPair<KG, S> {}
impl<G: Group, S: SecretKey<G> + core::hash::Hash> core::hash::Hash for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG> + core::hash::Hash> core::hash::Hash for KeyPair<KG, S> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.pk.hash(state);
self.sk.hash(state);
@@ -65,22 +65,22 @@ impl<G: Group, S: SecretKey<G> + core::hash::Hash> core::hash::Hash for KeyPair<
}
// This can't be derived because of the use of a generic parameter
impl<G: Group, S: SecretKey<G>> Zeroize for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG>> Zeroize for KeyPair<KG, S> {
fn zeroize(&mut self) {
self.pk.zeroize();
self.sk.zeroize();
}
}
impl<G: Group, S: SecretKey<G>> Drop for KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG>> Drop for KeyPair<KG, S> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<G: Group, S: SecretKey<G>> KeyPair<G, S> {
impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
/// The public key component
pub fn public(&self) -> &PublicKey<G> {
pub fn public(&self) -> &PublicKey<KG> {
&self.pk
}
@@ -93,8 +93,8 @@ impl<G: Group, S: SecretKey<G>> KeyPair<G, S> {
/// 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
pub(crate) fn check_public_key(key: PublicKey<G>) -> Result<PublicKey<G>, InternalError> {
G::from_element_slice(GenericArray::from_slice(&key.0)).map(|_| key)
pub(crate) fn check_public_key(key: PublicKey<KG>) -> Result<PublicKey<KG>, InternalError> {
KG::from_pk_slice(GenericArray::from_slice(&key.0)).map(|_| key)
}
/// Obtains a KeyPair from a slice representing the private key
@@ -109,29 +109,22 @@ impl<G: Group, S: SecretKey<G>> KeyPair<G, S> {
}
}
impl<G: Group> KeyPair<G> {
impl<KG: KeGroup> KeyPair<KG> {
/// Generating a random key pair given a cryptographic rng
pub(crate) fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let sk = G::random_nonzero_scalar(rng);
let sk_bytes = G::scalar_as_bytes(sk);
let pk = G::base_point().mult_by_slice(&sk_bytes);
Self {
pub(crate) fn generate_random<R: RngCore + CryptoRng>(
rng: &mut R,
) -> Result<Self, InternalError> {
let sk = KG::random_sk(rng);
let pk = KG::public_key(&sk);
Ok(Self {
pk: PublicKey(Key(pk.to_arr())),
sk: PrivateKey(Key(sk_bytes)),
}
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
alloc::vec![
(self.pk.as_ptr(), G::ElemLen::USIZE),
(self.sk.as_ptr(), G::ScalarLen::USIZE),
]
sk: PrivateKey(Key(sk)),
})
}
}
#[cfg(test)]
impl<G: Group + Debug> KeyPair<G> {
impl<KG: KeGroup + Debug> KeyPair<KG> {
/// Test-only strategy returning a proptest Strategy based on
/// generate_random
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
@@ -143,7 +136,7 @@ impl<G: Group + Debug> KeyPair<G> {
any::<[u8; 32]>()
.prop_filter_map("valid random keypair", |seed| {
let mut rng = StdRng::from_seed(seed);
Some(Self::generate_random(&mut rng))
Some(Self::generate_random(&mut rng).unwrap())
})
.no_shrink()
.boxed()
@@ -217,47 +210,47 @@ impl<L: ArrayLength<u8>> Key<L> {
/// Wrapper around a Key to enforce that it's a private one.
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[repr(transparent)]
pub struct PrivateKey<G: Group>(Key<G::ScalarLen>);
pub struct PrivateKey<KG: KeGroup>(Key<KG::SkLen>);
impl_clone_for!(
tuple PrivateKey<G: Group>,
tuple PrivateKey<KG: KeGroup>,
[0],
);
impl_debug_eq_hash_for!(
tuple PrivateKey<G: Group>,
tuple PrivateKey<KG: KeGroup>,
[0],
);
// This can't be derived because of the use of a generic parameter
impl<G: Group> Zeroize for PrivateKey<G> {
impl<KG: KeGroup> Zeroize for PrivateKey<KG> {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl<G: Group> Drop for PrivateKey<G> {
impl<KG: KeGroup> Drop for PrivateKey<KG> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<G: Group> Deref for PrivateKey<G> {
type Target = Key<G::ScalarLen>;
impl<KG: KeGroup> Deref for PrivateKey<KG> {
type Target = Key<KG::SkLen>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<G: Group> PrivateKey<G> {
impl<KG: KeGroup> PrivateKey<KG> {
/// Convert from bytes
pub fn from_arr(key_bytes: GenericArray<u8, G::ScalarLen>) -> Self {
pub fn from_arr(key_bytes: GenericArray<u8, KG::SkLen>) -> Self {
PrivateKey(Key(key_bytes))
}
/// Convert from slice
pub fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalError> {
if key_bytes.len() == G::ScalarLen::USIZE {
if key_bytes.len() == KG::SkLen::USIZE {
Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone()))
} else {
Err(InternalError::InvalidByteSequence)
@@ -266,15 +259,15 @@ impl<G: Group> PrivateKey<G> {
}
/// A trait specifying the requirements for a private key container
pub trait SecretKey<G: Group>: Clone + Sized + Zeroize {
pub trait SecretKey<KG: KeGroup>: Clone + Sized + Zeroize {
/// Custom error type that can be passed down to `InternalError::Custom`
type Error;
/// Diffie-Hellman key exchange implementation
fn diffie_hellman(&self, pk: PublicKey<G>) -> Result<Vec<u8>, InternalError<Self::Error>>;
fn diffie_hellman(&self, pk: PublicKey<KG>) -> Result<Vec<u8>, InternalError<Self::Error>>;
/// Returns public key from private key
fn public_key(&self) -> Result<PublicKey<G>, InternalError<Self::Error>>;
fn public_key(&self) -> Result<PublicKey<KG>, InternalError<Self::Error>>;
/// Serialization into bytes
fn serialize(&self) -> Vec<u8>;
@@ -283,21 +276,16 @@ pub trait SecretKey<G: Group>: Clone + Sized + Zeroize {
fn deserialize(input: &[u8]) -> Result<Self, InternalError<Self::Error>>;
}
impl<G: Group> SecretKey<G> for PrivateKey<G> {
impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
type Error = core::convert::Infallible;
fn diffie_hellman(&self, pk: PublicKey<G>) -> Result<Vec<u8>, InternalError> {
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(&self.0[..]);
Ok(G::mult_by_slice(&point, secret_data).to_arr().to_vec())
fn diffie_hellman(&self, pk: PublicKey<KG>) -> Result<Vec<u8>, InternalError> {
let pk = KG::from_pk_slice(&pk)?;
Ok(pk.diffie_hellman(self).to_vec())
}
fn public_key(&self) -> Result<PublicKey<G>, InternalError> {
let bytes_data = GenericArray::<u8, G::ScalarLen>::from_slice(&self.0[..]);
Ok(PublicKey(Key(G::base_point()
.mult_by_slice(bytes_data)
.to_arr())))
fn public_key(&self) -> Result<PublicKey<KG>, InternalError> {
Ok(PublicKey(Key(KG::public_key(&self.0).to_arr())))
}
fn serialize(&self) -> Vec<u8> {
@@ -312,47 +300,47 @@ impl<G: Group> SecretKey<G> for PrivateKey<G> {
/// Wrapper around a Key to enforce that it's a public one.
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[repr(transparent)]
pub struct PublicKey<G: Group>(Key<G::ElemLen>);
pub struct PublicKey<KG: KeGroup>(Key<KG::PkLen>);
impl_clone_for!(
tuple PublicKey<G: Group>,
tuple PublicKey<KG: KeGroup>,
[0],
);
impl_debug_eq_hash_for!(
tuple PublicKey<G: Group>,
tuple PublicKey<KG: KeGroup>,
[0],
);
// This can't be derived because of the use of a generic parameter
impl<G: Group> Zeroize for PublicKey<G> {
impl<KG: KeGroup> Zeroize for PublicKey<KG> {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl<G: Group> Drop for PublicKey<G> {
impl<KG: KeGroup> Drop for PublicKey<KG> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<G: Group> Deref for PublicKey<G> {
type Target = Key<G::ElemLen>;
impl<KG: KeGroup> Deref for PublicKey<KG> {
type Target = Key<KG::PkLen>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<G: Group> PublicKey<G> {
impl<KG: KeGroup> PublicKey<KG> {
/// Convert from bytes
pub fn from_arr(key_bytes: GenericArray<u8, G::ElemLen>) -> Self {
pub fn from_arr(key_bytes: GenericArray<u8, KG::PkLen>) -> Self {
Self(Key(key_bytes))
}
/// Convert from slice
pub fn from_bytes(key_bytes: &[u8]) -> Result<Self, InternalError> {
if key_bytes.len() == G::ElemLen::USIZE {
if key_bytes.len() == KG::PkLen::USIZE {
Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone()))
} else {
Err(InternalError::InvalidByteSequence)
@@ -372,8 +360,8 @@ mod tests {
#[test]
fn test_zeroize_key() -> Result<(), ProtocolError> {
let key_len = <RistrettoPoint as Group>::ElemLen::USIZE;
let mut key = Key::<<RistrettoPoint as Group>::ElemLen>(GenericArray::clone_from_slice(
let key_len = <RistrettoPoint as KeGroup>::PkLen::USIZE;
let mut key = Key::<<RistrettoPoint as KeGroup>::PkLen>(GenericArray::clone_from_slice(
&alloc::vec![
1u8;
key_len
@@ -381,7 +369,7 @@ mod tests {
));
let ptr = key.as_ptr();
key.zeroize();
Zeroize::zeroize(&mut key);
let bytes = unsafe { from_raw_parts(ptr, key_len) };
assert!(bytes.iter().all(|&x| x == 0));
@@ -392,15 +380,19 @@ mod tests {
#[test]
fn test_zeroize_keypair() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let mut keypair = KeyPair::<RistrettoPoint>::generate_random(&mut rng);
let ptrs = keypair.as_byte_ptrs();
let mut keypair = KeyPair::<RistrettoPoint>::generate_random(&mut rng)?;
let pk_ptr = keypair.pk.as_ptr();
let sk_ptr = keypair.sk.as_ptr();
let pk_len = <RistrettoPoint as KeGroup>::PkLen::USIZE;
let sk_len = <RistrettoPoint as KeGroup>::SkLen::USIZE;
keypair.zeroize();
Zeroize::zeroize(&mut keypair);
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
let pk_bytes = unsafe { from_raw_parts(pk_ptr, pk_len) };
let sk_bytes = unsafe { from_raw_parts(sk_ptr, sk_len) };
assert!(pk_bytes.iter().all(|&x| x == 0));
assert!(sk_bytes.iter().all(|&x| x == 0));
Ok(())
}
@@ -490,12 +482,12 @@ mod tests {
const PASSWORD: &str = "password";
let sk = RistrettoPoint::random_nonzero_scalar(&mut OsRng);
let sk_bytes = RistrettoPoint::scalar_as_bytes(sk);
let sk = RemoteKey(PrivateKey::from_arr(sk_bytes));
let sk = RistrettoPoint::random_sk(&mut OsRng);
let sk = RemoteKey(PrivateKey(Key(sk)));
let keypair = KeyPair::from_private_key(sk).unwrap();
let server_setup = ServerSetup::<Default, RemoteKey>::new_with_key(&mut OsRng, keypair);
let server_setup =
ServerSetup::<Default, RemoteKey>::new_with_key(&mut OsRng, keypair).unwrap();
let ClientRegistrationStartResult {
message,
+18 -24
View File
@@ -128,7 +128,7 @@
//! # )?;
//! use opaque_ke::ServerRegistration;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! let server_registration_start_result = ServerRegistration::<Default>::start(
//! &server_setup,
//! client_registration_start_result.message,
@@ -165,7 +165,7 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! let client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
@@ -204,7 +204,7 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! let password_file = ServerRegistration::<Default>::finish(
@@ -281,10 +281,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # 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 = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -335,10 +335,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # 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 = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -381,10 +381,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # 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 = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -457,7 +457,7 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! // During registration, the client obtains a ClientRegistrationFinishResult with
//! // a server_s_pk field
@@ -466,7 +466,7 @@
//! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::default(),
//! )?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -535,7 +535,7 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! // During registration...
//! let client_registration_finish_result = client_registration_start_result.state.finish(
@@ -543,7 +543,7 @@
//! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::default()
//! )?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -601,7 +601,7 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! let client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
@@ -640,10 +640,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -690,10 +690,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng)?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize()?;
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -846,7 +846,6 @@ mod impls;
mod serialization;
pub mod ciphersuite;
mod envelope;
pub mod group;
pub mod hash;
pub mod key_exchange;
pub mod keypair;
@@ -854,11 +853,6 @@ mod messages;
mod opaque;
pub mod slow_hash;
#[cfg(feature = "bench")]
pub mod oprf;
#[cfg(not(feature = "bench"))]
mod oprf;
#[cfg(test)]
mod tests;
+79 -73
View File
@@ -12,8 +12,10 @@ use crate::{
utils::{check_slice_size, check_slice_size_atleast},
ProtocolError,
},
group::Group,
key_exchange::traits::{FromBytes, KeyExchange, ToBytes},
key_exchange::{
group::KeGroup,
traits::{FromBytes, KeyExchange, ToBytes},
},
keypair::{KeyPair, PublicKey, SecretKey},
opaque::ServerSetup,
};
@@ -21,6 +23,7 @@ use alloc::vec::Vec;
use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use rand::{CryptoRng, RngCore};
use voprf::group::Group;
////////////////////////////
// High-level API Structs //
@@ -30,14 +33,14 @@ use rand::{CryptoRng, RngCore};
/// The message sent by the client to the server, to initiate registration
pub struct RegistrationRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: CS::OprfGroup,
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
}
/// The answer sent by the server to the user, upon reception of the
/// registration attempt
pub struct RegistrationResponse<CS: CipherSuite> {
/// The server's oprf output
pub(crate) beta: CS::OprfGroup,
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
/// Server's static public key
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
}
@@ -56,8 +59,7 @@ pub struct RegistrationUpload<CS: CipherSuite> {
/// The message sent by the user to the server, to initiate registration
pub struct CredentialRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: CS::OprfGroup,
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
}
@@ -65,7 +67,7 @@ pub struct CredentialRequest<CS: CipherSuite> {
/// login attempt
pub struct CredentialResponse<CS: CipherSuite> {
/// the server's oprf output
pub(crate) beta: CS::OprfGroup,
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
pub(crate) masking_nonce: Vec<u8>,
pub(crate) masked_response: Vec<u8>,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
@@ -85,69 +87,59 @@ pub struct CredentialFinalization<CS: CipherSuite> {
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
self.alpha
pub fn get_blinded_element_for_testing(
&self,
) -> voprf::BlindedElement<CS::OprfGroup, CS::Hash> {
self.blinded_element.clone()
}
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
self.alpha.to_arr().to_vec()
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok(self.blinded_element.serialize())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::OprfGroup as Group>::ElemLen::USIZE;
let checked_slice = check_slice_size(input, elem_len, "first_message_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(checked_slice);
let alpha = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
return Err(ProtocolError::IdentityGroupElementError);
}
Ok(Self { alpha })
Ok(Self {
blinded_element: voprf::BlindedElement::deserialize(input)?,
})
}
}
impl<CS: CipherSuite> RegistrationResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[self.beta.to_arr().to_vec(), self.server_s_pk.to_vec()].concat()
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.evaluation_element.serialize(),
self.server_s_pk.to_vec(),
]
.concat())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::OprfGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
let checked_slice =
check_slice_size(input, elem_len + key_len, "registration_response_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
let beta = CS::OprfGroup::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if beta.is_identity() {
return Err(ProtocolError::IdentityGroupElementError);
}
// Ensure that public key is valid
let server_s_pk = KeyPair::<CS::KeGroup>::check_public_key(PublicKey::from_bytes(
&checked_slice[elem_len..],
)?)?;
Ok(Self { beta, server_s_pk })
Ok(Self {
evaluation_element: voprf::EvaluationElement::deserialize(&checked_slice[..elem_len])?,
server_s_pk,
})
}
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self {
pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self {
Self {
beta: new_beta,
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
server_s_pk: self.server_s_pk.clone(),
}
}
@@ -155,18 +147,18 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
impl<CS: CipherSuite> RegistrationUpload<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.client_s_pk.to_arr().to_vec(),
self.masking_key.to_vec(),
self.envelope.serialize(),
]
.concat()
.concat())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <CS::KeGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
let hash_len = <CS::Hash as Digest>::OutputSize::USIZE;
let checked_slice =
check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?;
@@ -200,8 +192,12 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
impl<CS: CipherSuite> CredentialRequest<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[self.alpha.to_arr().to_vec(), self.ke1_message.to_bytes()].concat()
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.blinded_element.serialize(),
self.ke1_message.to_bytes(),
]
.concat())
}
/// Deserialization from bytes
@@ -212,11 +208,12 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
let alpha = CS::OprfGroup::from_element_slice(arr)?;
let blinded_element = voprf::BlindedElement::<CS::OprfGroup, CS::Hash>::deserialize(
&checked_slice[..elem_len],
)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
if blinded_element.value().is_identity() {
return Err(ProtocolError::IdentityGroupElementError);
}
@@ -225,24 +222,33 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
&checked_slice[elem_len..],
)?;
Ok(Self { alpha, ke1_message })
Ok(Self {
blinded_element,
ke1_message,
})
}
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
self.alpha
pub fn get_blinded_element_for_testing(
&self,
) -> voprf::BlindedElement<CS::OprfGroup, CS::Hash> {
self.blinded_element.clone()
}
}
impl<CS: CipherSuite> CredentialResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
Self::serialize_without_ke(&self.beta, &self.masking_nonce, &self.masked_response),
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
Self::serialize_without_ke(
&self.evaluation_element.value(),
&self.masking_nonce,
&self.masked_response,
),
self.ke2_message.to_bytes(),
]
.concat()
.concat())
}
pub(crate) fn serialize_without_ke(
@@ -256,7 +262,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::OprfGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
let nonce_len: usize = 32;
let envelope_len = Envelope::<CS>::len();
let masked_response_len = key_len + envelope_len;
@@ -271,11 +277,11 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
// Check that the message is actually containing an element of the
// correct subgroup
let beta_bytes = &checked_slice[..elem_len];
let arr = GenericArray::from_slice(beta_bytes);
let beta = CS::OprfGroup::from_element_slice(arr)?;
let evaluation_element =
voprf::EvaluationElement::<CS::OprfGroup, CS::Hash>::deserialize(beta_bytes)?;
// Throw an error if the identity group element is encountered
if beta.is_identity() {
if evaluation_element.value().is_identity() {
return Err(ProtocolError::IdentityGroupElementError);
}
@@ -289,7 +295,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
)?;
Ok(Self {
beta,
evaluation_element,
masking_nonce,
masked_response,
ke2_message,
@@ -299,9 +305,9 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self {
pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self {
Self {
beta: new_beta,
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
masking_nonce: self.masking_nonce.clone(),
masked_response: self.masked_response.clone(),
ke2_message: self.ke2_message.clone(),
@@ -311,8 +317,8 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
impl<CS: CipherSuite> CredentialFinalization<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
self.ke3_message.to_bytes()
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok(self.ke3_message.to_bytes())
}
/// Deserialization from bytes
@@ -332,19 +338,19 @@ impl<CS: CipherSuite> CredentialFinalization<CS> {
impl_clone_for!(
struct RegistrationRequest<CS: CipherSuite>,
[alpha],
[blinded_element],
);
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [alpha], [CS::OprfGroup]);
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [blinded_element], [CS::OprfGroup, CS::Hash]);
impl_serialize_and_deserialize_for!(RegistrationRequest);
impl_clone_for!(
struct RegistrationResponse<CS: CipherSuite>,
[beta, server_s_pk],
[evaluation_element, server_s_pk],
);
impl_debug_eq_hash_for!(
struct RegistrationResponse<CS: CipherSuite>,
[beta, server_s_pk],
[CS::OprfGroup],
[evaluation_element, server_s_pk],
[CS::OprfGroup, CS::Hash],
);
impl_serialize_and_deserialize_for!(RegistrationResponse);
@@ -360,11 +366,11 @@ impl_serialize_and_deserialize_for!(RegistrationUpload);
impl_clone_for!(
struct CredentialRequest<CS: CipherSuite>,
[alpha, ke1_message],
[blinded_element, ke1_message],
);
impl_debug_eq_hash_for!(
struct CredentialRequest<CS: CipherSuite>,
[alpha, ke1_message],
[blinded_element, ke1_message],
[
CS::OprfGroup,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message
@@ -374,11 +380,11 @@ impl_serialize_and_deserialize_for!(CredentialRequest);
impl_clone_for!(
struct CredentialResponse<CS: CipherSuite>,
[beta, masking_nonce, masked_response, ke2_message],
[evaluation_element, masking_nonce, masked_response, ke2_message],
);
impl_debug_eq_hash_for!(
struct CredentialResponse<CS: CipherSuite>,
[beta, masking_nonce, masked_response, ke2_message],
[evaluation_element, masking_nonce, masked_response, ke2_message],
[
CS::OprfGroup,
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
+206 -179
View File
@@ -9,11 +9,12 @@ use crate::{
ciphersuite::CipherSuite,
envelope::Envelope,
errors::{utils::check_slice_size, InternalError, ProtocolError},
group::Group,
hash::Hash,
key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers},
key_exchange::{
group::KeGroup,
traits::{FromBytes, KeyExchange, ToBytes},
},
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
oprf,
serialization::{serialize, tokenize},
slow_hash::SlowHash,
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
@@ -26,6 +27,8 @@ use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use hkdf::Hkdf;
use rand::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
use voprf::group::Group;
use zeroize::Zeroize;
///////////////
@@ -33,10 +36,10 @@ use zeroize::Zeroize;
// ========= //
///////////////
const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad";
const STR_MASKING_KEY: &[u8] = b"MaskingKey";
const STR_OPRF_KEY: &[u8] = b"OprfKey";
const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair";
const STR_CREDENTIAL_RESPONSE_PAD: &[u8; 21] = b"CredentialResponsePad";
const STR_MASKING_KEY: &[u8; 10] = b"MaskingKey";
const STR_OPRF_KEY: &[u8; 7] = b"OprfKey";
const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair";
////////////////////////////
// High-level API Structs //
@@ -73,16 +76,15 @@ impl_debug_eq_hash_for!(
/// The state elements the client holds to register itself
pub struct ClientRegistration<CS: CipherSuite> {
alpha: CS::OprfGroup,
/// token containing the client's password and the blinding factor
pub(crate) token: oprf::Token<CS::OprfGroup>,
pub(crate) oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
}
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [oprf_client, blinded_element]);
impl_debug_eq_hash_for!(
struct ClientRegistration<CS: CipherSuite>,
[token],
[oprf::Token<CS::OprfGroup>],
[oprf_client],
[voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>],
);
impl_serialize_and_deserialize_for!(ClientRegistration);
@@ -97,27 +99,19 @@ impl_debug_eq_hash_for!(
impl_serialize_and_deserialize_for!(ServerRegistration);
/// The state elements the client holds to perform a login
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "serialize",
serde(bound(
deserialize = "oprf::Token<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
))
)]
pub struct ClientLogin<CS: CipherSuite> {
/// token containing the client's password and the blinding factor
token: oprf::Token<CS::OprfGroup>,
oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
serialized_credential_request: Vec<u8>,
}
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serialized_credential_request]);
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [oprf_client, ke1_state, serialized_credential_request]);
impl_debug_eq_hash_for!(
struct ClientLogin<CS: CipherSuite>,
[token, ke1_state, serialized_credential_request],
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
[oprf_client, ke1_state, serialized_credential_request],
[voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
);
impl_serialize_and_deserialize_for!(ClientLogin);
/// The state elements the server holds to record a login
pub struct ServerLogin<CS: CipherSuite> {
@@ -143,8 +137,8 @@ impl_serialize_and_deserialize_for!(ServerLogin);
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
/// Generate a new instance of server setup
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
let keypair = KeyPair::<CS::KeGroup>::generate_random(rng);
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Result<Self, InternalError> {
let keypair = KeyPair::<CS::KeGroup>::generate_random(rng)?;
Self::new_with_key(rng, keypair)
}
}
@@ -154,31 +148,31 @@ impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
pub fn new_with_key<R: CryptoRng + RngCore>(
rng: &mut R,
keypair: KeyPair<CS::KeGroup, S>,
) -> Self {
) -> Result<Self, InternalError> {
let mut seed = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
rng.fill_bytes(&mut seed);
Self {
Ok(Self {
oprf_seed: GenericArray::clone_from_slice(&seed[..]),
keypair,
fake_keypair: KeyPair::<CS::KeGroup>::generate_random(rng),
}
fake_keypair: KeyPair::<CS::KeGroup>::generate_random(rng)?,
})
}
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.oprf_seed.to_vec(),
self.keypair.private().serialize(),
self.fake_keypair.private().serialize(),
]
.concat()
.concat())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<S::Error>> {
let seed_len = <CS::Hash as Digest>::OutputSize::USIZE;
let key_len = <CS::KeGroup as Group>::ScalarLen::USIZE;
let key_len = <CS::KeGroup as KeGroup>::SkLen::USIZE;
let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?;
Ok(Self {
@@ -200,56 +194,37 @@ impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
impl<CS: CipherSuite> ClientRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
&self.alpha.to_arr().to_vec(),
&CS::OprfGroup::scalar_as_bytes(self.token.blind)[..],
&self.token.data,
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
serialize(&self.oprf_client.serialize(), 2)?,
serialize(&self.blinded_element.serialize(), 2)?,
]
.concat()
.concat())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::OprfGroup as Group>::ElemLen::USIZE;
let scalar_len = <CS::OprfGroup as Group>::ScalarLen::USIZE;
let min_expected_len = elem_len + scalar_len;
let checked_slice = (if input.len() <= min_expected_len {
Err(InternalError::SizeError {
name: "client_registration_bytes",
len: min_expected_len,
actual_len: input.len(),
})
} else {
Ok(input)
})?;
let (serialized_oprf_client, remainder) = tokenize(input, 2)?;
let (serialized_blinded_element, remainder) = tokenize(&remainder, 2)?;
let alpha = CS::OprfGroup::from_element_slice(GenericArray::from_slice(
&checked_slice[..elem_len],
))?;
if !remainder.is_empty() {
return Err(ProtocolError::SerializationError);
}
// Check that the message is actually containing an element of the
// correct subgroup
let blinding_factor_bytes =
GenericArray::from_slice(&checked_slice[elem_len..elem_len + scalar_len]);
let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?;
let password = checked_slice[elem_len + scalar_len..].to_vec();
Ok(Self {
alpha,
token: oprf::Token {
data: password,
blind: blinding_factor,
},
oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?,
blinded_element: voprf::BlindedElement::deserialize(&serialized_blinded_element)?,
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.token.data.as_ptr(), self.token.data.len()),
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
/// Only used for testing zeroize
pub(crate) fn to_vec(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.oprf_client.serialize(),
self.blinded_element.serialize(),
]
.concat())
}
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
@@ -257,12 +232,16 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
blinding_factor_rng: &mut R,
password: &[u8],
) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
let (token, alpha) =
oprf::blind::<R, CS::OprfGroup, CS::Hash>(password, blinding_factor_rng)?;
let blind_result = blind::<CS, _>(blinding_factor_rng, password)?;
Ok(ClientRegistrationStartResult {
message: RegistrationRequest::<CS> { alpha },
state: Self { alpha, token },
message: RegistrationRequest::<CS> {
blinded_element: blind_result.message.clone(),
},
state: Self {
oprf_client: blind_result.state,
blinded_element: blind_result.message,
},
})
}
@@ -271,27 +250,35 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
pub fn finish<R: CryptoRng + RngCore>(
self,
rng: &mut R,
r2: RegistrationResponse<CS>,
registration_response: RegistrationResponse<CS>,
params: ClientRegistrationFinishParameters<CS>,
) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
// Check for reflected value from server and halt if detected
if self.alpha.ct_equal(&r2.beta) {
if self
.blinded_element
.value()
.ct_eq(&registration_response.evaluation_element.value())
.into()
{
return Err(ProtocolError::ReflectedValueError);
}
let password_derived_key =
get_password_derived_key::<CS>(&self.token, r2.beta, params.slow_hash)?;
#[cfg_attr(not(test), allow(unused_variables))]
let (randomized_pwd, h) = Hkdf::<CS::Hash>::extract(None, &password_derived_key);
let (randomized_pwd, randomized_pwd_hasher) = get_password_derived_key::<CS>(
self.oprf_client.clone(),
registration_response.evaluation_element,
params.slow_hash,
)?;
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
h.expand(STR_MASKING_KEY, &mut masking_key)
randomized_pwd_hasher
.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalError::HkdfError)?;
let result = Envelope::<CS>::seal(
rng,
&password_derived_key,
&r2.server_s_pk,
randomized_pwd_hasher,
&registration_response.server_s_pk,
params.identifiers,
)?;
@@ -302,7 +289,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
client_s_pk: result.1,
},
export_key: result.2,
server_s_pk: r2.server_s_pk,
server_s_pk: registration_response.server_s_pk,
#[cfg(test)]
state: self,
#[cfg(test)]
@@ -315,7 +302,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
impl<CS: CipherSuite> ServerRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
self.0.serialize()
}
@@ -324,15 +311,6 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
Ok(Self(RegistrationUpload::deserialize(input)?))
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
self.0.envelope.as_byte_ptrs(),
vec![(self.0.client_s_pk.as_ptr(), self.0.client_s_pk.len())],
/* cannot provide raw pointer to self.oprf_key until this is exposed in curve25519_dalek::scalar::Scalar */
].concat()
}
/// From the client's "blinded" password, returns a response to be
/// sent back to the client, as well as a ServerRegistration
pub fn start<S: SecretKey<CS::KeGroup>>(
@@ -345,16 +323,16 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
credential_identifier,
)?;
// Compute beta = alpha^oprf_key
let beta = oprf::evaluate::<CS::OprfGroup>(message.alpha, &oprf_key);
let server = voprf::NonVerifiableServer::new_with_key(&oprf_key)?;
let evaluate_result = server.evaluate(message.blinded_element, None)?;
Ok(ServerRegistrationStartResult {
message: RegistrationResponse {
beta,
evaluation_element: evaluate_result.message,
server_s_pk: server_setup.keypair.public().clone(),
},
#[cfg(test)]
oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key),
oprf_key: GenericArray::clone_from_slice(&oprf_key),
})
}
@@ -380,10 +358,9 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
let output: Vec<u8> = [
&CS::OprfGroup::scalar_as_bytes(self.token.blind)[..],
&serialize(&self.serialized_credential_request, 2)?,
&serialize(&self.ke1_state.to_bytes(), 2)?,
&self.token.data,
serialize(&self.oprf_client.serialize(), 2)?,
serialize(&self.serialized_credential_request, 2)?,
serialize(&self.ke1_state.to_bytes(), 2)?,
]
.concat();
Ok(output)
@@ -391,47 +368,34 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let scalar_len = <CS::OprfGroup as Group>::ScalarLen::USIZE;
let checked_slice = (if input.len() <= scalar_len {
Err(InternalError::SizeError {
name: "client_login_bytes",
len: scalar_len,
actual_len: input.len(),
})
} else {
Ok(input)
})?;
let (serialized_oprf_client, remainder) = tokenize(input, 2)?;
let (serialized_credential_request, remainder) = tokenize(&remainder, 2)?;
let (ke1_state_bytes, remainder) = tokenize(&remainder, 2)?;
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?;
let (serialized_credential_request, remainder) = tokenize(&checked_slice[scalar_len..], 2)?;
let (ke1_state_bytes, password) = tokenize(&remainder, 2)?;
if !remainder.is_empty() {
return Err(ProtocolError::SerializationError);
}
let ke1_state =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State::from_bytes::<CS>(
&ke1_state_bytes[..],
)?;
Ok(Self {
token: oprf::Token {
data: password,
blind: blinding_factor,
},
oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?,
ke1_state,
serialized_credential_request,
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
vec![
(self.token.data.as_ptr(), self.token.data.len()),
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
],
self.ke1_state.as_byte_ptrs(),
vec![ (self.serialized_credential_request.as_ptr(), self.serialized_credential_request.len()) ],
].concat()
/// Only used for testing zeroize
pub(crate) fn to_vec(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
self.oprf_client.serialize(),
self.serialized_credential_request.clone(),
self.ke1_state.to_bytes(),
]
.concat())
}
}
@@ -441,17 +405,19 @@ impl<CS: CipherSuite> ClientLogin<CS> {
rng: &mut R,
password: &[u8],
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
let (token, alpha) = oprf::blind::<R, CS::OprfGroup, CS::Hash>(password, rng)?;
let blind_result = blind::<CS, _>(rng, password)?;
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?;
let credential_request = CredentialRequest { alpha, ke1_message };
let serialized_credential_request = credential_request.serialize();
let credential_request = CredentialRequest {
blinded_element: blind_result.message,
ke1_message,
};
let serialized_credential_request = credential_request.serialize()?;
Ok(ClientLoginStartResult {
message: credential_request,
state: Self {
token,
oprf_client: blind_result.state,
ke1_state,
serialized_credential_request,
},
@@ -468,19 +434,24 @@ impl<CS: CipherSuite> ClientLogin<CS> {
// Check if beta value from server is equal to alpha value from client
let credential_request =
CredentialRequest::<CS>::deserialize(&self.serialized_credential_request[..])?;
if credential_request.alpha.ct_equal(&credential_response.beta) {
if credential_request
.blinded_element
.value()
.ct_eq(&credential_response.evaluation_element.value())
.into()
{
return Err(ProtocolError::ReflectedValueError);
}
let password_derived_key = get_password_derived_key::<CS>(
&self.token,
credential_response.beta,
let (_, randomized_pwd_hasher) = get_password_derived_key::<CS>(
self.oprf_client.clone(),
credential_response.evaluation_element.clone(),
params.slow_hash,
)?;
let h = Hkdf::<CS::Hash>::new(None, &password_derived_key);
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
h.expand(STR_MASKING_KEY, &mut masking_key)
randomized_pwd_hasher
.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalError::HkdfError)?;
let (server_s_pk, envelope) = unmask_response::<CS>(
@@ -496,7 +467,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
let opened_envelope = &envelope
.open(
&password_derived_key,
randomized_pwd_hasher,
&server_s_pk_bytes,
&params.identifiers,
)
@@ -508,7 +479,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
})?;
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
&credential_response.beta,
&credential_response.evaluation_element.value(),
&credential_response.masking_nonce,
&credential_response.masked_response,
);
@@ -544,8 +515,8 @@ impl<CS: CipherSuite> ClientLogin<CS> {
impl<CS: CipherSuite> ServerLogin<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
self.ke2_state.to_bytes()
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok(self.ke2_state.to_bytes())
}
/// Deserialization from bytes
@@ -565,7 +536,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
rng: &mut R,
server_setup: &ServerSetup<CS, S>,
password_file: Option<ServerRegistration<CS>>,
l1: CredentialRequest<CS>,
credential_request: CredentialRequest<CS>,
credential_identifier: &[u8],
params: ServerLoginStartParameters,
) -> Result<ServerLoginStartResult<CS>, ProtocolError<S::Error>> {
@@ -605,23 +576,33 @@ impl<CS: CipherSuite> ServerLogin<CS> {
)
.map_err(ProtocolError::into_custom)?;
let l1_bytes = &l1.serialize();
let credential_request_bytes = credential_request
.serialize()
.map_err(ProtocolError::into_custom)?;
let oprf_key = oprf_key_from_seed::<CS::OprfGroup, CS::Hash>(
&server_setup.oprf_seed,
credential_identifier,
)
.map_err(ProtocolError::into_custom)?;
let beta = oprf::evaluate(l1.alpha, &oprf_key);
let server = voprf::NonVerifiableServer::new_with_key(&oprf_key)
.map_err(|e| ProtocolError::into_custom(e.into()))?;
let evaluate_result = server
.evaluate(credential_request.blinded_element, None)
.map_err(|e| ProtocolError::into_custom(e.into()))?;
let evaluation_element = evaluate_result.message;
let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
&evaluation_element.value(),
&masking_nonce,
&masked_response,
);
let result = CS::KeyExchange::generate_ke2(
rng,
l1_bytes.to_vec(),
credential_request_bytes,
credential_response_component,
l1.ke1_message,
credential_request.ke1_message,
client_s_pk,
server_s_sk.clone(),
id_u,
@@ -630,7 +611,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
)?;
let credential_response = CredentialResponse {
beta,
evaluation_element,
masking_nonce,
masked_response,
ke2_message: result.1,
@@ -647,7 +628,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
#[cfg(test)]
server_mac_key: result.3,
#[cfg(test)]
oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key),
oprf_key: GenericArray::clone_from_slice(&oprf_key),
})
}
@@ -669,11 +650,6 @@ impl<CS: CipherSuite> ServerLogin<CS> {
state: self,
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
self.ke2_state.as_byte_ptrs()
}
}
/////////////////////////
@@ -969,31 +945,47 @@ impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
// Helper functions
#[allow(clippy::type_complexity)]
fn get_password_derived_key<CS: CipherSuite>(
token: &oprf::Token<CS::OprfGroup>,
beta: CS::OprfGroup,
oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
slow_hash: Option<&CS::SlowHash>,
) -> Result<Vec<u8>, ProtocolError> {
let oprf_output = oprf::finalize::<CS::OprfGroup, CS::Hash>(&token.data, &token.blind, beta)?;
) -> Result<
(
GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
Hkdf<CS::Hash>,
),
ProtocolError,
> {
let oprf_output = oprf_client.finalize(evaluation_element, None)?;
if let Some(slow_hash) = slow_hash {
slow_hash.hash(oprf_output)
let hardened_output = if let Some(slow_hash) = slow_hash {
slow_hash.hash(oprf_output.clone())
} else {
CS::SlowHash::default().hash(oprf_output)
CS::SlowHash::default().hash(oprf_output.clone())
}
.map_err(ProtocolError::from)
.map_err(ProtocolError::from)?;
Ok(Hkdf::<CS::Hash>::extract(
None,
&[oprf_output.to_vec(), hardened_output].concat(),
))
}
fn oprf_key_from_seed<G: Group, D: Hash>(
oprf_seed: &GenericArray<u8, D::OutputSize>,
credential_identifier: &[u8],
) -> Result<G::Scalar, ProtocolError> {
) -> Result<Vec<u8>, ProtocolError> {
let mut ikm = vec![0u8; G::ScalarLen::USIZE];
Hkdf::<D>::from_prk(oprf_seed)
.map_err(|_| InternalError::HkdfError)?
.expand(&[credential_identifier, STR_OPRF_KEY].concat(), &mut ikm)
.map_err(|_| InternalError::HkdfError)?;
G::hash_to_scalar::<D>(&ikm[..], STR_OPAQUE_DERIVE_KEY_PAIR)
Ok(G::scalar_as_bytes(G::hash_to_scalar::<D, _, _>(
Some(&ikm[..]),
GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR),
)?)
.to_vec())
}
fn mask_response<CS: CipherSuite>(
@@ -1002,7 +994,7 @@ fn mask_response<CS: CipherSuite>(
server_s_pk: &PublicKey<CS::KeGroup>,
envelope: &Envelope<CS>,
) -> Result<Vec<u8>, ProtocolError> {
let mut xor_pad = vec![0u8; <CS::KeGroup as Group>::ElemLen::USIZE + Envelope::<CS>::len()];
let mut xor_pad = vec![0u8; <CS::KeGroup as KeGroup>::PkLen::USIZE + Envelope::<CS>::len()];
Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalError::HkdfError)?
.expand(
@@ -1025,7 +1017,7 @@ fn unmask_response<CS: CipherSuite>(
masking_nonce: &[u8],
masked_response: &[u8],
) -> Result<(PublicKey<CS::KeGroup>, Envelope<CS>), ProtocolError> {
let mut xor_pad = vec![0u8; <CS::KeGroup as Group>::ElemLen::USIZE + Envelope::<CS>::len()];
let mut xor_pad = vec![0u8; <CS::KeGroup as KeGroup>::PkLen::USIZE + Envelope::<CS>::len()];
Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalError::HkdfError)?
.expand(
@@ -1038,7 +1030,7 @@ fn unmask_response<CS: CipherSuite>(
.zip(masked_response.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
let key_len = <CS::KeGroup as Group>::ElemLen::USIZE;
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
let unchecked_server_s_pk = PublicKey::from_bytes(&plaintext[..key_len])?;
let envelope = Envelope::deserialize(&plaintext[key_len..])?;
@@ -1066,13 +1058,49 @@ pub(crate) fn bytestrings_from_identifiers(
))
}
/// Internal function for computing the blind result by calling the
/// voprf library. Note that for tests, we use the deterministic blinding
/// in order to be able to set the blinding factor directly from the passed-in
/// rng.
fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
rng: &mut R,
password: &[u8],
) -> Result<
voprf::NonVerifiableClientBlindResult<CS::OprfGroup, CS::Hash>,
voprf::errors::InternalError,
> {
#[cfg(not(test))]
let result = voprf::NonVerifiableClient::blind(password.to_vec(), rng)?;
#[cfg(test)]
let result = {
let mut blind_bytes = vec![0u8; <CS::OprfGroup as Group>::ScalarLen::USIZE];
let blind = loop {
rng.fill_bytes(&mut blind_bytes);
let scalar = <CS::OprfGroup as Group>::from_scalar_slice_unchecked(
&GenericArray::clone_from_slice(&blind_bytes),
)?;
match scalar
.ct_eq(&<CS::OprfGroup as Group>::scalar_zero())
.into()
{
false => break scalar,
true => (),
}
};
voprf::NonVerifiableClient::deterministic_blind_unchecked(password.to_vec(), blind)?
};
Ok(result)
}
// Zeroize on drop implementations
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
self.oprf_client.zeroize();
self.blinded_element.zeroize();
}
}
@@ -1100,8 +1128,7 @@ impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
self.oprf_client.zeroize();
self.ke1_state.zeroize();
self.serialized_credential_request.zeroize();
}
-173
View File
@@ -1,173 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use crate::{errors::ProtocolError, group::Group, hash::Hash, serialization::serialize};
use digest::Digest;
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
/// Used to store the OPRF input and blinding factor
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
pub struct Token<Grp: Group> {
pub(crate) data: alloc::vec::Vec<u8>,
pub(crate) blind: Grp::Scalar,
}
impl_clone_for!(struct Token<Grp: Group>, [data, blind]);
impl_debug_eq_hash_for!(struct Token<Grp: Group>, [data, blind], [Grp::Scalar]);
static STR_VOPRF: &[u8] = b"HashToGroup-VOPRF07-";
static STR_VOPRF_FINALIZE: &[u8] = b"Finalize-VOPRF07-";
static MODE_BASE: u8 = 0x00;
/// Computes the first step for the multiplicative blinding version of DH-OPRF. This
/// message is sent from the client (who holds the input) to the server (who holds the OPRF key).
/// The client can also pass in an optional "pepper" string to be mixed in with the input through
/// an HKDF computation.
pub(crate) fn blind<R: RngCore + CryptoRng, G: Group, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<(Token<G>, G), ProtocolError> {
// Choose a random scalar that must be non-zero
let blind = G::random_nonzero_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 blind_token = mapped_point * &blind;
Ok((
Token {
data: input.to_vec(),
blind,
},
blind_token,
))
}
/// Computes the second step for the multiplicative blinding version of DH-OPRF. This
/// message is sent from the server (who holds the OPRF key) to the client.
pub(crate) fn evaluate<G: Group>(point: G, oprf_key: &G::Scalar) -> G {
point * oprf_key
}
/// Computes the third step for the multiplicative blinding version of DH-OPRF, in which
/// the client unblinds the server's message.
pub(crate) fn finalize<G: Group, H: Hash>(
input: &[u8],
blind: &G::Scalar,
evaluated_element: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
let unblinded_element = evaluated_element * &G::scalar_invert(blind);
finalize_after_unblind::<G, H>(input, unblinded_element)
}
fn finalize_after_unblind<G: Group, H: Hash>(
input: &[u8],
unblinded_element: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
let finalize_dst = [STR_VOPRF_FINALIZE, &G::get_context_string(MODE_BASE)?].concat();
let hash_input = [
serialize(input, 2)?,
serialize(&unblinded_element.to_arr().to_vec(), 2)?,
serialize(&finalize_dst, 2)?,
]
.concat();
Ok(<H as Digest>::digest(&hash_input))
}
////////////////////////
// Benchmarking Shims //
////////////////////////
#[cfg(feature = "bench")]
#[doc(hidden)]
#[inline]
pub fn blind_shim<R: RngCore + CryptoRng, G: Group, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<(Token<G>, G), ProtocolError> {
blind::<R, G, H>(input, blinding_factor_rng)
}
#[cfg(feature = "bench")]
#[doc(hidden)]
#[inline]
pub fn evaluate_shim<G: Group>(point: G, oprf_key: &G::Scalar) -> G {
evaluate(point, oprf_key)
}
#[cfg(feature = "bench")]
#[doc(hidden)]
#[inline]
pub fn finalize_shim<G: Group, H: Hash>(
token: &Token<G>,
point: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
finalize::<G, H>(&token.data, &token.blind, point)
}
///////////
// Tests //
// ===== //
///////////
#[cfg(test)]
mod tests {
use super::*;
use crate::group::Group;
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::{arr, GenericArray};
use rand::rngs::OsRng;
use sha2::Sha512;
fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray<u8, <Sha512 as Digest>::OutputSize> {
let dst = [
STR_VOPRF,
&RistrettoPoint::get_context_string(MODE_BASE).unwrap(),
]
.concat();
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_after_unblind::<RistrettoPoint, sha2::Sha512>(&input, res).unwrap()
}
#[test]
fn oprf_retrieval() {
let input = b"hunter2";
let mut rng = OsRng;
let (token, alpha) = blind::<_, RistrettoPoint, Sha512>(&input[..], &mut rng).unwrap();
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,
];
let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes).unwrap();
let beta = evaluate::<RistrettoPoint>(alpha, &oprf_key);
let res =
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &token.blind, beta).unwrap();
let res2 = prf(&input[..], &oprf_key.as_bytes());
assert_eq!(res, res2);
}
#[test]
fn oprf_inversion_unsalted() {
let mut rng = OsRng;
let mut input = alloc::vec![0u8; 64];
rng.fill_bytes(&mut input);
let (token, alpha) = blind::<_, RistrettoPoint, sha2::Sha512>(&input, &mut rng).unwrap();
let res =
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &token.blind, alpha).unwrap();
let dst = [
STR_VOPRF,
&RistrettoPoint::get_context_string(MODE_BASE).unwrap(),
]
.concat();
let point = RistrettoPoint::map_to_curve::<Sha512>(&input, &dst).unwrap();
let res2 = finalize_after_unblind::<RistrettoPoint, sha2::Sha512>(&input, point).unwrap();
assert_eq!(res, res2);
}
}
+100 -63
View File
@@ -7,7 +7,6 @@ use crate::{
ciphersuite::CipherSuite,
envelope::{Envelope, InnerEnvelopeMode},
errors::*,
group::Group,
key_exchange::{
traits::{FromBytes, KeyExchange, ToBytes},
tripledh::{NonceLen, TripleDH},
@@ -21,10 +20,11 @@ use alloc::vec;
#[cfg(test)]
use alloc::vec::Vec;
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use proptest::{collection::vec, prelude::*};
use rand::{rngs::OsRng, RngCore};
use voprf::group::Group;
use sha2::Digest;
@@ -55,21 +55,27 @@ fn random_ristretto_point() -> RistrettoPoint {
}
#[test]
fn client_registration_roundtrip() {
fn client_registration_roundtrip() -> Result<(), ProtocolError> {
let pw = b"hunter2";
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let elem = <RistrettoPoint as Group>::base_point() * sc;
// serialization order: scalar, password, group element
let bytes: Vec<u8> = [&elem.to_arr(), &sc.as_bytes()[..], &pw[..]].concat();
let reg = ClientRegistration::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize();
let blind_result =
&voprf::NonVerifiableClient::<RistrettoPoint, sha2::Sha512>::blind(pw.to_vec(), &mut rng)?;
let bytes: Vec<u8> = [
serialize(&blind_result.state.serialize(), 2)?,
serialize(&blind_result.message.serialize(), 2)?,
]
.concat();
let reg = ClientRegistration::<Default>::deserialize(&bytes[..])?;
let reg_bytes = reg.serialize()?;
assert_eq!(reg_bytes, bytes);
Ok(())
}
#[test]
fn server_registration_roundtrip() {
fn server_registration_roundtrip() -> Result<(), ProtocolError> {
// If we don't have envelope and client_pk, the server registration just
// contains the prf key
let mut rng = OsRng;
@@ -82,27 +88,28 @@ fn server_registration_roundtrip() {
// mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key
mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac
let mock_client_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let mock_client_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
// serialization order: oprf_key, public key, envelope
let mut bytes = Vec::<u8>::new();
bytes.extend_from_slice(&mock_client_kp.public().to_arr());
bytes.extend_from_slice(&masking_key);
bytes.extend_from_slice(&mock_envelope_bytes);
let reg = ServerRegistration::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize();
let reg = ServerRegistration::<Default>::deserialize(&bytes[..])?;
let reg_bytes = reg.serialize()?;
assert_eq!(reg_bytes, bytes);
Ok(())
}
#[test]
fn registration_request_roundtrip() {
fn registration_request_roundtrip() -> Result<(), ProtocolError> {
let pt = random_ristretto_point();
let pt_bytes = pt.to_arr().to_vec();
let mut input = Vec::new();
input.extend_from_slice(pt_bytes.as_slice());
let r1 = RegistrationRequest::<Default>::deserialize(input.as_slice()).unwrap();
let r1_bytes = r1.serialize();
let r1 = RegistrationRequest::<Default>::deserialize(input.as_slice())?;
let r1_bytes = r1.serialize()?;
assert_eq!(input, r1_bytes);
// Assert that identity group element is rejected
@@ -111,26 +118,30 @@ fn registration_request_roundtrip() {
assert!(
match RegistrationRequest::<Default>::deserialize(identity_bytes.as_slice()) {
Err(ProtocolError::IdentityGroupElementError) => true,
Err(ProtocolError::LibraryError(InternalError::OprfError(
voprf::errors::InternalError::PointError,
))) => true,
_ => false,
}
);
Ok(())
}
#[test]
fn registration_response_roundtrip() {
fn registration_response_roundtrip() -> Result<(), ProtocolError> {
let pt = random_ristretto_point();
let beta_bytes = pt.to_arr();
let mut rng = OsRng;
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let pubkey_bytes = skp.public().to_arr();
let mut input = Vec::new();
input.extend_from_slice(beta_bytes.as_slice());
input.extend_from_slice(&pubkey_bytes.as_slice());
let r2 = RegistrationResponse::<Default>::deserialize(input.as_slice()).unwrap();
let r2_bytes = r2.serialize();
let r2 = RegistrationResponse::<Default>::deserialize(input.as_slice())?;
let r2_bytes = r2.serialize()?;
assert_eq!(input, r2_bytes);
// Assert that identity group element is rejected
@@ -140,15 +151,19 @@ fn registration_response_roundtrip() {
assert!(match RegistrationResponse::<Default>::deserialize(
&[identity_bytes, pubkey_bytes.to_vec()].concat()
) {
Err(ProtocolError::IdentityGroupElementError) => true,
Err(ProtocolError::LibraryError(InternalError::OprfError(
voprf::errors::InternalError::PointError,
))) => true,
_ => false,
});
Ok(())
}
#[test]
fn registration_upload_roundtrip() {
fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let pubkey_bytes = skp.public().to_arr();
let mut key = [0u8; 32];
@@ -159,9 +174,15 @@ fn registration_upload_roundtrip() {
let mut masking_key = vec![0u8; <sha2::Sha512 as Digest>::OutputSize::USIZE];
rng.fill_bytes(&mut masking_key);
let (envelope, _, _) =
Envelope::<Default>::seal_raw(&key, &nonce, &pubkey_bytes, InnerEnvelopeMode::Internal)
.unwrap();
let randomized_pwd_hasher = hkdf::Hkdf::new(None, &key);
let (envelope, _, _) = Envelope::<Default>::seal_raw(
randomized_pwd_hasher,
&nonce,
&pubkey_bytes,
InnerEnvelopeMode::Internal,
)
.unwrap();
let envelope_bytes = envelope.serialize();
let mut input = Vec::new();
@@ -169,18 +190,20 @@ fn registration_upload_roundtrip() {
input.extend_from_slice(&masking_key[..]);
input.extend_from_slice(&envelope_bytes);
let r3 = RegistrationUpload::<Default>::deserialize(&input[..]).unwrap();
let r3_bytes = r3.serialize();
let r3 = RegistrationUpload::<Default>::deserialize(&input[..])?;
let r3_bytes = r3.serialize()?;
assert_eq!(input, r3_bytes);
Ok(())
}
#[test]
fn credential_request_roundtrip() {
fn credential_request_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let alpha = random_ristretto_point();
let alpha_bytes = alpha.to_arr().to_vec();
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let mut client_nonce = vec![0u8; NonceLen::USIZE];
rng.fill_bytes(&mut client_nonce);
@@ -190,8 +213,8 @@ fn credential_request_roundtrip() {
input.extend_from_slice(&alpha_bytes);
input.extend_from_slice(&ke1m[..]);
let l1 = CredentialRequest::<Default>::deserialize(input.as_slice()).unwrap();
let l1_bytes = l1.serialize();
let l1 = CredentialRequest::<Default>::deserialize(input.as_slice())?;
let l1_bytes = l1.serialize()?;
assert_eq!(input, l1_bytes);
// Assert that identity group element is rejected
@@ -201,13 +224,17 @@ fn credential_request_roundtrip() {
assert!(match CredentialRequest::<Default>::deserialize(
&[identity_bytes, ke1m.to_vec()].concat()
) {
Err(ProtocolError::IdentityGroupElementError) => true,
Err(ProtocolError::LibraryError(InternalError::OprfError(
voprf::errors::InternalError::PointError,
))) => true,
_ => false,
});
Ok(())
}
#[test]
fn credential_response_roundtrip() {
fn credential_response_roundtrip() -> Result<(), ProtocolError> {
let pt = random_ristretto_point();
let pt_bytes = pt.to_arr().to_vec();
@@ -220,7 +247,7 @@ fn credential_response_roundtrip() {
vec![0u8; <RistrettoPoint as Group>::ElemLen::USIZE + Envelope::<Default>::len()];
rng.fill_bytes(&mut masked_response);
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::USIZE];
@@ -234,8 +261,8 @@ fn credential_response_roundtrip() {
input.extend_from_slice(&masked_response);
input.extend_from_slice(&ke2m[..]);
let l2 = CredentialResponse::<Default>::deserialize(&input).unwrap();
let l2_bytes = l2.serialize();
let l2 = CredentialResponse::<Default>::deserialize(&input)?;
let l2_bytes = l2.serialize()?;
assert_eq!(input, l2_bytes);
// Assert that identity group element is rejected
@@ -251,72 +278,80 @@ fn credential_response_roundtrip() {
]
.concat()
) {
Err(ProtocolError::IdentityGroupElementError) => true,
Err(ProtocolError::LibraryError(InternalError::OprfError(
voprf::errors::InternalError::PointError,
))) => true,
_ => false,
});
Ok(())
}
#[test]
fn credential_finalization_roundtrip() {
fn credential_finalization_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let input: Vec<u8> = [&mac[..]].concat();
let l3 = CredentialFinalization::<Default>::deserialize(&input).unwrap();
let l3_bytes = l3.serialize();
let l3 = CredentialFinalization::<Default>::deserialize(&input)?;
let l3_bytes = l3.serialize()?;
assert_eq!(input, l3_bytes);
Ok(())
}
#[test]
fn client_login_roundtrip() {
fn client_login_roundtrip() -> Result<(), ProtocolError> {
let pw = b"hunter2";
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let mut client_nonce = vec![0u8; NonceLen::USIZE];
rng.fill_bytes(&mut client_nonce);
let serialized_credential_request = b"serialized credential_request".to_vec();
let l1_data = [client_e_kp.private().to_arr().to_vec(), client_nonce].concat();
// serialization order: scalar, credential_request, ke1_state, password
let blind_result =
&voprf::NonVerifiableClient::<RistrettoPoint, sha2::Sha512>::blind(pw.to_vec(), &mut rng)?;
let bytes: Vec<u8> = [
&sc.as_bytes()[..],
&serialize(&serialized_credential_request, 2).unwrap(),
&serialize(&l1_data, 2).unwrap(),
&pw[..],
serialize(&blind_result.state.serialize(), 2)?,
serialize(&serialized_credential_request, 2)?,
serialize(&l1_data, 2)?,
]
.concat();
let reg = ClientLogin::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize().unwrap();
let reg = ClientLogin::<Default>::deserialize(&bytes[..])?;
let reg_bytes = reg.serialize()?;
assert_eq!(reg_bytes, bytes);
Ok(())
}
#[test]
fn ke1_message_roundtrip() {
fn ke1_message_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let mut client_nonce = vec![0u8; NonceLen::USIZE];
rng.fill_bytes(&mut client_nonce);
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE1Message::from_bytes::<
Default,
>(&ke1m[..])
.unwrap();
>(&ke1m[..])?;
let reg_bytes = reg.to_bytes();
assert_eq!(reg_bytes, ke1m);
Ok(())
}
#[test]
fn ke2_message_roundtrip() {
fn ke2_message_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::USIZE];
@@ -326,14 +361,15 @@ fn ke2_message_roundtrip() {
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE2Message::from_bytes::<
Default,
>(&ke2m[..])
.unwrap();
>(&ke2m[..])?;
let reg_bytes = reg.to_bytes();
assert_eq!(reg_bytes, ke2m);
Ok(())
}
#[test]
fn ke3_message_roundtrip() {
fn ke3_message_roundtrip() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
@@ -342,10 +378,11 @@ fn ke3_message_roundtrip() {
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE3Message::from_bytes::<
Default,
>(&ke3m[..])
.unwrap();
>(&ke3m[..])?;
let reg_bytes = reg.to_bytes();
assert_eq!(reg_bytes, ke3m);
Ok(())
}
proptest! {
+122 -123
View File
@@ -12,7 +12,6 @@ use crate::{
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use core::slice::from_raw_parts;
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use rand::rngs::OsRng;
use serde_json::Value;
@@ -71,38 +70,38 @@ static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier";
// To regenerate, run: cargo test -- --nocapture generate_test_vectors
static TEST_VECTOR: &str = r#"
{
"client_s_pk": "520364faf278ac5b407465c8a802ac57bf61ec4bbf3e1c508a39e1667c7cf904",
"client_s_sk": "e30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06",
"client_e_pk": "1c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24",
"client_e_sk": "56b7ff7a43c7d6f75d881606f733c12a53af70b35d8c5cd982a8e94a816dfa0d",
"server_s_pk": "bcf39fe745ca2f945c7d75b542bfe4217fe6f481c49b7ce410f4a13079cd0d2b",
"server_s_sk": "cd4a4916bf0226c7fa1f18c97cbf5e0c03d1ae8b74d1155270202d848f4d640e",
"server_e_pk": "e02644fdcb782c335c0c1f801b70f7a873bc2613e3ed2a6ad05103a85dd2ec01",
"server_e_sk": "b9b14238988b6ea6e6aa6e8981a65c45e28ba01ddedf9437aff62a7071a64d09",
"fake_sk": "55ed2b029216bd0953db96c614c737001fc9c79ad4b2c896f2aeebe052e9770a",
"client_s_pk": "181bbea01a5e444390c4b335f8bcb9a846a1c60042669ce6d731af4587960c06",
"client_s_sk": "2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b",
"client_e_pk": "58a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e417",
"client_e_sk": "c1a4db9d650ce1700e05fbd472d30c13e0a4c6926b114e7ca11e2e9f397c5005",
"server_s_pk": "dc8c66b6dcf4731836a0cdb336985c77a6321ffb75db6bb1aef20974c141dd3c",
"server_s_sk": "410ef173f972994eeabb2cb39fd5db907e39a1abd6b36c9f514ab903d9d16305",
"server_e_pk": "68630597c4593cb8158f398ab6ff956a1d87232be4300be1f96a6860663d9461",
"server_e_sk": "155452c6b08b889b31d5a810925136adfb2d363eee4ccaed0ef15b594fb88b04",
"fake_sk": "b009e67b83c418f0ae271b8790d6c5e06ea3874fa5a9e66752b5ebdbec953b04",
"credential_identifier": "637265644964656e746966696572",
"id_u": "696455",
"id_s": "696453",
"password": "70617373776f7264",
"blinding_factor": "1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd04",
"oprf_seed": "aee2ac3f7c043526f1cbda952f34840482a63afbf959e32f9172343d8b9fe076cd0a87a017f16e6511c37f171cea277d906d6d1dc31a3de4c0cc99c46e76c94c",
"masking_nonce": "62f49aa47625d01466051799e1ba6f99e1916c15fa1cc651867d1bbc55c6c5255269d21a5854a9756ffd8fc33de366da1d715c5de16609f7aa55e7e8ce01c8f5",
"envelope_nonce": "3cf5a62a4ce8ffe7e1da0ac2066597f1d7cd7a91285e8a108943f6beea91b1aa",
"client_nonce": "de58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f9362879",
"server_nonce": "50d85de8a9a6fb656981a17811db0a381dd98aa37514a90d8abf990b56829daf",
"blinding_factor": "544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf4339403",
"oprf_seed": "f929fa161a065bec163bea6dbab6d6eccd960666951fc7fd3da7cf2b6baf20a2763598aba89a4e5bcaa57096c66cfded26d683e07ab1a3b37a7c82706dfaee81",
"masking_nonce": "4c0099b7067c7c243ed804def0fd490babd577abcd7b05a1f24a05d2d1cc344e079a75936ed36b89a3056661a2dc981a6628edde6a86da2714cce71659d84f8d",
"envelope_nonce": "9888ba54ffc1e1be5deb23a2efa5432f318f9a17d681d1273e909ca3bf1b2fea",
"client_nonce": "652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c29",
"server_nonce": "5e2f19069ea9791d6b346b676d8d8aaf45536148ca0357a595f330c7aed107d2",
"context": "636f6e74657874",
"registration_request": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52c",
"registration_response": "389fcdd9b327e1ad9eaa30b8ea9db665b49a52b96583c4952f087d21aa47e15abcf39fe745ca2f945c7d75b542bfe4217fe6f481c49b7ce410f4a13079cd0d2b",
"registration_upload": "7a1daeb91dc6af4e8685287922020dda9e4445ddb107aa76f30ef3defec982046c81af9eba7baef5e5dc3aca419a62b207542315edc732834b18fa394cbfa9c210caed864e36bb19cdd7324553b7e35d1a7384f4262a15e38049f910b2ca6baae30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06383ff4ec0a1f237f70e51cae0f859e7476917cb8d28cda3aeccd970a205a71e20d3e592eaadabc2216d0ed14e494dbf733984e607b6d5f8b68f9b2321ca01b30",
"credential_request": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52cde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f93628791c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24",
"credential_response": "389fcdd9b327e1ad9eaa30b8ea9db665b49a52b96583c4952f087d21aa47e15a62f49aa47625d01466051799e1ba6f99e1916c15fa1cc651867d1bbc55c6c5257e7d8f435b95456607ec8767840b5c192c1132d78b38cf72511ae4605a8539f570452bbfd864a34a71b635f77c9d7dee227b7906697ab0dea2dda3043672c523a0a2c651e9bbef212e85439c8f227c564470cdb6d9a74ac8b7d2f6f3eba59b43489082c93f17f7de3c2840848e2c5db07bf4a8c5011f6ff01895bcb0a3b10ca0b9b14238988b6ea6e6aa6e8981a65c45e28ba01ddedf9437aff62a7071a64d098eaf6a09f9dd3db7e9ea307c4df21852c24fdd27bc335815afdfa03237facb0834b5d393d8b5d61526d8a61077a345a864020a10be67f46a6a6a02fd86ea05a2f25e262d9fe23bfe9489d5bc585dd4a018fa5dea295e51a7824526b6360cc03a",
"credential_finalization": "eefb1823d84629dc0d9e149dbb07fb115e473d0d3a004e0b2dd20fdb168a7d33423347c07960d272f1fa670f45ca8e6571d3b396661383da8f07380d89488116",
"client_registration_state": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52c1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd0470617373776f7264",
"client_login_state": "1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd0400607ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52cde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f93628791c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24004056b7ff7a43c7d6f75d881606f733c12a53af70b35d8c5cd982a8e94a816dfa0dde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f936287970617373776f7264",
"server_login_state": "b54d627533bf9545c5450b4c518e4cb298b118b34383db02ceddd2178683e7201d1e3ffd5eff03bc224a75760cecd10ce0a6514ba4f638fbfc21f968398d159f5f5a78b6309646b6e9ecd5856aa109abdc53d720791f198edde539d19ee7deeb5438f6a1156476d8e11404b344c2a97db92902eb2d77d723defeb3a7d3be1c5e954c3ba56dff507f6d0de3e9c02b1fe3d8935b32fbaa4be2cf5c82225b9e47c931bb51eb04a2a5345914c33a52c3f7d5184b631fe1a5efd4633f433e408589fa",
"password_file": "7a1daeb91dc6af4e8685287922020dda9e4445ddb107aa76f30ef3defec982046c81af9eba7baef5e5dc3aca419a62b207542315edc732834b18fa394cbfa9c210caed864e36bb19cdd7324553b7e35d1a7384f4262a15e38049f910b2ca6baae30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06383ff4ec0a1f237f70e51cae0f859e7476917cb8d28cda3aeccd970a205a71e20d3e592eaadabc2216d0ed14e494dbf733984e607b6d5f8b68f9b2321ca01b30",
"export_key": "9eaea2ca345b9fac4c7924abe5783d23ac7452d61ebb026d40ecab402178c8669b135783e5dcd148554e9d861fa4af809f1e41c1ccca929000cdb3a78a2dc4fb",
"session_key": "954c3ba56dff507f6d0de3e9c02b1fe3d8935b32fbaa4be2cf5c82225b9e47c931bb51eb04a2a5345914c33a52c3f7d5184b631fe1a5efd4633f433e408589fa"
"registration_request": "f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d",
"registration_response": "2c6f5ba3de9af2719529e9a993097e8c0ecd5110a24471414e4225950189cc46dc8c66b6dcf4731836a0cdb336985c77a6321ffb75db6bb1aef20974c141dd3c",
"registration_upload": "08a51d9973140af4f911f235d4910e9536503157bfaffefaeaa11f69d723cc54d35d9ae50d6a0a7ab38614e571a81821cfbfec36ed9fd46e397e173252d02ff623287035e190153e9fb88509da1c225765bb200ed59249cbfd6201656d1672db2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b19e07582aea6c5e782b15ff18f6188203f54ea62dfb1efb77d641f030b86062c9f0d1bc3c39b7f824fe81df456c702ea4fa084eba803fea7e5a80d2284c2ff15",
"credential_request": "f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c2958a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e417",
"credential_response": "2c6f5ba3de9af2719529e9a993097e8c0ecd5110a24471414e4225950189cc464c0099b7067c7c243ed804def0fd490babd577abcd7b05a1f24a05d2d1cc344e4100aea1aa461bed485d0e441c275d0d04388e1b6b739379cd9ebabe6a53ca025375487ea73cc17ea884fb3fd91d05a8970628459ab7996e9e20aacdc613a669a687c95479814b866c43cde2ca225f43a44035ba921b5986877589013acd8eba3f4e00bd4c5e83756651c5faa0319487e4b80bcedfef82b9e46c3cace5b4aa8e155452c6b08b889b31d5a810925136adfb2d363eee4ccaed0ef15b594fb88b04605c82fb3bb14125d8274619e5ceef90151b6309610aa35c2c0a004337c44b50162d27726ed40aca064e88cff1413d7098f81b16567919ca8d9c425b8b3445684e2ad7e7d8d14475c2ca1ace56a5949199fb836f9f343d46994098955bb9f129",
"credential_finalization": "e13284ada3e78eed48047934115ce7e6c2cdff0c3012e9ba2d423759c4000ddf11ecd186dd7f0740ee3413ff0d253e2437eced56f3717e45c071b170d12db1dd",
"client_registration_state": "0028544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf433940370617373776f72640020f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d",
"client_login_state": "0028544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf433940370617373776f72640060f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c2958a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e4170040c1a4db9d650ce1700e05fbd472d30c13e0a4c6926b114e7ca11e2e9f397c5005652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c29",
"server_login_state": "2a009e5881454a2b42fb8c039762f78828c5b4ba7008d2e57b16ffdc937ee846b56461f9cda751b2a1c2b03793d72d5ca8c482adf8009880779323e64e12eb1d4c0cf45ec665be918cb9d655f9eca974494f0f4c0f6e714c6ddcca37b547c122cf7419984e123fa4c7981212e5171b01bfd6f8ac88e7964c8da4a88b5df4f2c85da5318465cef76fbddd389ff36be66c693cfc6feecbcf43bf16a22c97de8430e824b2812449934d13fb666b24de78a007f1fc06064304b0abfae3fc5caba7f6",
"password_file": "08a51d9973140af4f911f235d4910e9536503157bfaffefaeaa11f69d723cc54d35d9ae50d6a0a7ab38614e571a81821cfbfec36ed9fd46e397e173252d02ff623287035e190153e9fb88509da1c225765bb200ed59249cbfd6201656d1672db2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b19e07582aea6c5e782b15ff18f6188203f54ea62dfb1efb77d641f030b86062c9f0d1bc3c39b7f824fe81df456c702ea4fa084eba803fea7e5a80d2284c2ff15",
"export_key": "ea8d1f871a3c8ad5d2a7a2d647e020105a33f8b8534055c56ab4bae2b8467d22806968159f918d9c31098602790fcad3e5969f1d8ff0b90b48c26b4132877ed4",
"session_key": "5da5318465cef76fbddd389ff36be66c693cfc6feecbcf43bf16a22c97de8430e824b2812449934d13fb666b24de78a007f1fc06064304b0abfae3fc5caba7f6"
}
"#;
@@ -272,19 +271,20 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> alloc::string::String {
s
}
fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
use crate::{group::Group, key_exchange::tripledh::NonceLen, keypair::KeyPair};
fn generate_parameters<CS: CipherSuite>() -> Result<TestVectorParameters, ProtocolError> {
use crate::{key_exchange::tripledh::NonceLen, keypair::KeyPair};
use generic_array::typenum::Unsigned;
use rand::RngCore;
use voprf::group::Group;
let mut rng = OsRng;
// Inputs
let server_s_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let server_e_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let client_s_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let client_e_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let fake_kp = KeyPair::<CS::OprfGroup>::generate_random(&mut rng);
let server_s_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng)?;
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng)?;
let client_s_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng)?;
let client_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng)?;
let fake_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng)?;
let credential_identifier = b"credIdentifier";
let id_u = b"idU";
let id_s = b"idS";
@@ -313,18 +313,19 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
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::OprfGroup::scalar_as_bytes(client_registration_start_result.state.token.blind);
let blinding_factor_bytes_returned = CS::OprfGroup::scalar_as_bytes(
client_registration_start_result
.state
.oprf_client
.get_blind(),
);
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 client_registration_state = client_registration_start_result.state.serialize().to_vec();
let registration_request_bytes = client_registration_start_result.message.serialize()?;
let client_registration_state = client_registration_start_result.state.serialize()?;
let server_registration_start_result = ServerRegistration::<CS>::start(
&server_setup,
@@ -332,10 +333,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
&credential_identifier[..],
)
.unwrap();
let registration_response_bytes = server_registration_start_result
.message
.serialize()
.to_vec();
let registration_response_bytes = server_registration_start_result.message.serialize()?;
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr());
@@ -356,13 +354,10 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
),
)
.unwrap();
let registration_upload_bytes = client_registration_finish_result
.message
.serialize()
.to_vec();
let registration_upload_bytes = client_registration_finish_result.message.serialize()?;
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
let password_file_bytes = password_file.serialize();
let password_file_bytes = password_file.serialize()?;
let mut client_login_start: Vec<u8> = Vec::new();
client_login_start.extend_from_slice(&blinding_factor_bytes);
@@ -372,7 +367,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut client_login_start_rng = CycleRng::new(client_login_start);
let client_login_start_result =
ClientLogin::<CS>::start(&mut client_login_start_rng, password).unwrap();
let credential_request_bytes = client_login_start_result.message.serialize().to_vec();
let credential_request_bytes = client_login_start_result.message.serialize()?;
let client_login_state = client_login_start_result
.state
.serialize()
@@ -399,8 +394,8 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize().to_vec();
let server_login_state = server_login_start_result.state.serialize().to_vec();
let credential_response_bytes = server_login_start_result.message.serialize()?;
let server_login_state = server_login_start_result.state.serialize()?;
let client_login_finish_result = client_login_start_result
.state
@@ -416,9 +411,9 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
),
)
.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
let credential_finalization_bytes = client_login_finish_result.message.serialize()?;
TestVectorParameters {
Ok(TestVectorParameters {
client_s_pk: client_s_kp.public().to_arr().to_vec(),
client_s_sk: client_s_kp.private().to_arr().to_vec(),
client_e_pk: client_e_kp.public().to_arr().to_vec(),
@@ -451,13 +446,14 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
server_login_state,
session_key: client_login_finish_result.session_key,
export_key: client_registration_finish_result.export_key.to_vec(),
}
})
}
#[test]
fn generate_test_vectors() {
let parameters = generate_parameters::<RistrettoSha5123dhNoSlowHash>();
fn generate_test_vectors() -> Result<(), ProtocolError> {
let parameters = generate_parameters::<RistrettoSha5123dhNoSlowHash>()?;
println!("{}", stringify_test_vectors(&parameters));
Ok(())
}
#[test]
@@ -468,11 +464,11 @@ fn test_registration_request() -> Result<(), ProtocolError> {
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut rng, &parameters.password)?;
assert_eq!(
hex::encode(&parameters.registration_request),
hex::encode(client_registration_start_result.message.serialize())
hex::encode(client_registration_start_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.client_registration_state),
hex::encode(client_registration_start_result.state.serialize())
hex::encode(client_registration_start_result.state.serialize()?)
);
Ok(())
}
@@ -490,13 +486,13 @@ fn test_serialization() -> Result<(), ProtocolError> {
serde_json::to_string(&client_registration_start_result.message).unwrap();
assert_eq!(
registration_request_json,
r#""fqJ2p5GUcEpQVtF8Pi4XUdmLHR/F0deNEQBOmkTHxSw=""#
r#""8FBIuznz9aOkFPUCVMQls2+EIWKmML9zRW30UzUcsz0=""#
);
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
serde_json::from_str(&registration_request_json).unwrap();
assert_eq!(
hex::encode(client_registration_start_result.message.serialize()),
hex::encode(registration_request.serialize()),
hex::encode(client_registration_start_result.message.serialize()?),
hex::encode(registration_request.serialize()?),
);
}
{
@@ -507,15 +503,17 @@ fn test_serialization() -> Result<(), ProtocolError> {
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
bincode::deserialize(&registration_request_bin).unwrap();
assert_eq!(
hex::encode(client_registration_start_result.message.serialize()),
hex::encode(registration_request.serialize()),
hex::encode(client_registration_start_result.message.serialize()?),
hex::encode(registration_request.serialize()?),
);
}
Ok(())
}
#[test]
fn test_registration_response() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let parameters = populate_test_vectors(
&serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?,
);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize(
&[
@@ -534,14 +532,16 @@ fn test_registration_response() -> Result<(), ProtocolError> {
)?;
assert_eq!(
hex::encode(parameters.registration_response),
hex::encode(server_registration_start_result.message.serialize())
hex::encode(server_registration_start_result.message.serialize()?)
);
Ok(())
}
#[test]
fn test_registration_upload() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let parameters = populate_test_vectors(
&serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?,
);
let client_s_sk_and_nonce: Vec<u8> =
[parameters.client_s_sk, parameters.envelope_nonce].concat();
@@ -563,7 +563,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
assert_eq!(
hex::encode(parameters.registration_upload),
hex::encode(result.message.serialize())
hex::encode(result.message.serialize()?)
);
assert_eq!(
hex::encode(parameters.export_key),
@@ -585,7 +585,7 @@ fn test_password_file() -> Result<(), ProtocolError> {
assert_eq!(
hex::encode(parameters.password_file),
hex::encode(password_file.serialize())
hex::encode(password_file.serialize()?)
);
Ok(())
}
@@ -607,7 +607,7 @@ fn test_credential_request() -> Result<(), ProtocolError> {
)?;
assert_eq!(
hex::encode(&parameters.credential_request),
hex::encode(client_login_start_result.message.serialize())
hex::encode(client_login_start_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.client_login_state),
@@ -654,11 +654,11 @@ fn test_credential_response() -> Result<(), ProtocolError> {
)?;
assert_eq!(
hex::encode(&parameters.credential_response),
hex::encode(server_login_start_result.message.serialize())
hex::encode(server_login_start_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.server_login_state),
hex::encode(server_login_start_result.state.serialize())
hex::encode(server_login_start_result.state.serialize()?)
);
Ok(())
}
@@ -694,7 +694,7 @@ fn test_credential_finalization() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.credential_finalization),
hex::encode(client_login_finish_result.message.serialize())
hex::encode(client_login_finish_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.export_key),
@@ -730,7 +730,7 @@ fn test_complete_flow(
let credential_identifier = b"credentialIdentifier";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -810,11 +810,8 @@ fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> {
)?;
let mut state = client_registration_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.to_vec() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -825,7 +822,7 @@ fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> {
fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -844,11 +841,8 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
)?;
let mut state = client_registration_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.to_vec() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -859,7 +853,7 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -879,11 +873,8 @@ fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let mut state = p_file;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.serialize() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -899,11 +890,8 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
)?;
let mut state = client_login_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.to_vec() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -914,7 +902,7 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -946,11 +934,8 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
)?;
let mut state = server_login_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.serialize() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -961,7 +946,7 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -997,11 +982,8 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
)?;
let mut state = client_login_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.to_vec() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -1012,7 +994,7 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -1051,11 +1033,8 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
.finish(client_login_finish_result.message)?;
let mut state = server_login_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
Zeroize::zeroize(&mut state);
for bytes in state.serialize() {
assert!(bytes.iter().all(|&x| x == 0));
}
@@ -1076,7 +1055,23 @@ fn test_scalar_always_nonzero() -> Result<(), ProtocolError> {
RistrettoPoint::identity(),
client_registration_start_result
.message
.get_alpha_for_testing()
.get_blinded_element_for_testing()
.value(),
);
// Start out with a bunch of zeros to force resampling of scalar
let mut client_login_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat());
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_login_rng,
STR_PASSWORD.as_bytes(),
)?;
assert_ne!(
RistrettoPoint::identity(),
client_login_start_result
.message
.get_blinded_element_for_testing()
.value(),
);
Ok(())
@@ -1088,12 +1083,13 @@ fn test_reflected_value_error_registration() -> Result<(), ProtocolError> {
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, password)?;
let alpha = client_registration_start_result
.message
.get_alpha_for_testing();
.get_blinded_element_for_testing()
.value();
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&server_setup,
@@ -1103,7 +1099,7 @@ fn test_reflected_value_error_registration() -> Result<(), ProtocolError> {
let reflected_registration_response = server_registration_start_result
.message
.set_beta_for_testing(alpha);
.set_evaluation_element_for_testing(alpha);
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
@@ -1125,7 +1121,7 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng)?;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, password)?;
let server_registration_start_result =
@@ -1142,7 +1138,10 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result =
ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, password)?;
let alpha = client_login_start_result.message.get_alpha_for_testing();
let alpha = client_login_start_result
.message
.get_blinded_element_for_testing()
.value();
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
@@ -1154,7 +1153,7 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
let reflected_credential_response = server_login_start_result
.message
.set_beta_for_testing(alpha);
.set_evaluation_element_for_testing(alpha);
let client_login_result = client_login_start_result.state.finish(
reflected_credential_response,
-1
View File
@@ -8,4 +8,3 @@ pub mod mock_rng;
mod opaque_vectors;
mod parser;
mod test_opaque_vectors;
mod voprf_test_vectors;
+158 -158
View File
@@ -66,25 +66,25 @@ bf8d6503
#### Intermediate Values
~~~
client_public_key: a2ba5f0b3c7d340f33852c354ba36983230abbcb78a2ae2219
6f6d6272faba00
auth_key: bfe5696d23897e60acd8d8df3b458983bebf8c7322982b0de6f56311367
887bef0bbaecc2c4a6de3528851030a0d5256288b902e37798e129988814faa5aad33
randomized_pwd: 8bdf54a171e0c0bcbad3a821e0954fe5e758a3c1c07df697e5532
265cc06687b295ca98e1f635a7f31ac78af483a9d272e8ee763dd3b567f9a5dc9f4e7
05a36b
client_public_key: 3ea81ee30a44d65ca6db8f42a9c125277898ead7fe1604da98
70ad4542044a56
auth_key: 7816871e2ab2d039dc0d8a07ce94081dfd975de003ea1b7ff2b120cc74c
f18c32e11d2ed730fae9040f87be5c11cfc90cdf3393557c47065d7127ece8ca2b09c
randomized_pwd: c9c8c47dece13aed16b80ca049cedc86e984177d98b549ba40390
eb77981f954537e743ca3ec854fce472981714aadb3a280f4b2c15040d97653c64b7e
5c265e
envelope: 71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c46
76775749f7c447c49605d117f4cea49637ae644f2fee8a9c5eeb44dc78c60f1fc7a31
caec487e7117b4fa948042615525820f0dce8c763b4859db56fef87816b106e0
handshake_secret: 3847f696cc538d008e5e2d54f5b92a18404022ec7c3fc6f16c5
1cec65cff678dc1fdee81e3e001d282dabb9be877aa15de1d425f7612a94af864d00e
6d84350f
server_mac_key: 8b8ed457decef90f8864aec84f5d3716ab2091a8013d377976a32
073007b1685b851539a04cbfeaeedf63565f441ca840479a84d9e6dc2b264278c4913
08947c
client_mac_key: 9e891f73fabfce6a232ac10c2191fa9fc7ee9421cb39bcab5e376
a24b5c89a8f7d8589ba5f110298ef209a40676af9cf53a31f16fac47a97440597544b
036a07
76775eca8b48680e4973b26f754fbe0865af5135b284b00afce879c285db39b2ef0a9
c1bd2f847c3f2139556b34885691be2d1917be4de9d2f50ef198950964142256
handshake_secret: 706bae252489cd38228083790e3ead93f6b5b41682468741508
124a58185fbb5fc2ee23850dd5285095d6e18dd133f16ecda7e35837429efdf71455e
a6642cf6
server_mac_key: b63f98de411d2b008abaee6699ed14fa2834986f9fc2f9ae59af2
292fe8aa5e125d0ab0d5e5954cb8de5966da7994d798c800062c17fc167c598aa0d86
071685
client_mac_key: 187335e3edd777cb419d055e54001325cb19ebac8d43cf736f101
127bb19c932717960b2401699b0d5029cd3637a3175248048f1eba202eada9428b475
f2867a
oprf_key: 3f76113135e6ca7e51ac5bb3e8774eb84709ad36b8907ec8f7bc3537828
71906
~~~
@@ -94,36 +94,36 @@ oprf_key: 3f76113135e6ca7e51ac5bb3e8774eb84709ad36b8907ec8f7bc3537828
~~~
registration_request: 76cc85628d5ac0e01de4ede72479d607490e7f58b94578d
b7a0606d74bc58b03
registration_response: 865f3305ff73be7388313e7a74b5fc277a165ff2895f92
60391057b84c7bc72718d5035fd0a9c1d6412226df037125901a43f4dff660c0549d4
registration_response: 583fd26fd3130386b1a1a970e4617d45dc21c7a6f07052
8f0175985570b4ea2018d5035fd0a9c1d6412226df037125901a43f4dff660c0549d4
02f672bcc0933
registration_upload: a2ba5f0b3c7d340f33852c354ba36983230abbcb78a2ae22
196f6d6272faba0079a983ebe317141ee594ffd03535c1efce5411da15a92f92981bb
c1c02c292a5e472eb7d953cdecd10691aff451364b3eb67578758184c89830c420d0e
bee4ad71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c467677
5749f7c447c49605d117f4cea49637ae644f2fee8a9c5eeb44dc78c60f1fc7a31caec
487e7117b4fa948042615525820f0dce8c763b4859db56fef87816b106e0
registration_upload: 3ea81ee30a44d65ca6db8f42a9c125277898ead7fe1604da
9870ad4542044a56dde0af371300d8b13f4dc7039ae643560c279c0e67be6086bfbe2
188a65480689c9044a36b844b3f137a1fb594caff5e79002232a877aba39da085ce62
e1a59f71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c467677
5eca8b48680e4973b26f754fbe0865af5135b284b00afce879c285db39b2ef0a9c1bd
2f847c3f2139556b34885691be2d1917be4de9d2f50ef198950964142256
KE1: e47c1c5e5eed1910a1cbb6420c5edf26ea3c099aaaedcb03599fc311a724d84f
804133133e7ee6836c8515752e24bb44d323fef4ead34cde967798f2e9784f69f6792
6bd036c5dc4971816b9376e9f64737f361ef8269c18f69f1ab555e96d4a
KE2: 9692d473e0bde7a1fbb6d2c0e4001ccc58902102857d0e67e5fa44f4b902b17f
54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab86ff39ed7fbd513
832aba2b4ce0057dad8a9264cba31eee4713745b502b741bb8255c500dc16f1e5a57c
347bca59229cf4ed4e65abd59529fcaa0fd25ec20a777895785c1dba58c6a2517e5db
5e2940109fd2bf77d7db86b65a2032519b89559775dca96a0e8ea76fafacab0fff0de
09e1e8fecca1b835d6759b51cce90120391b3aa0bd6ff9c5ec75a8cd571370add249e
KE2: b01355438b21dc20aaa46a5dea61d5b60f81b81f347f80e8ee3addc12f62be05
54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab86ff39ed7f98431
127a277d18a821887ef0e23d92d73000161f14a3437d5fa6e028e4b33edad12212183
73ef515b13f023d4a17ed5f62201416b4ae07f56605093eda407390f83457ce80c4ad
48e25a7875deacb41fa9ee9c3fb9895407733ef147a97fb0eee9b47abd930777b6599
1c53b126f9f5aae3307a111a85107fe2229f93db8fe0f9c5ec75a8cd571370add249e
99cb8a8c43f6ef05610ac6e354642bf4fedbf696e77d4749eb304c4d74be9457c5975
46bc22aed699225499910fc913b3e9071235c693acb2f588a9ff4a7d1eb9ae3bc7f97
c2b29d939c405cd9f010a53ee5c20400c605ee4b85bccb2523d8a00b62a160a7ad6f5
45950e92e21802c73a1e3fa3
KE3: 5bab0d8fdabe655eb370d81c0233588b2c3e1519ed82aa8c6faf747db4a3210b
3db5b8b7807fd1f9fb5f4304f369bab081a4fce002aa3a55bc1a45239956e3ea
export_key: 5b622c41b12add9bd88da466777d2b34ba09c975164b573280340495f
26f17d3a7dbbe163a069d54bf10e95750e86ea4f4c3a4eaa78c435bc26ef3f9058439
a9
session_key: 32cfd43d68402222c9ee4ba93a266aca15c0e57a9fc728d03b557be9
ac1f8222d1c083b49988c34575ba4b2c972c5da728f3d8e8d78b2f99e62c74398c299
881
46bc22aed699225499910fc913b3e907125ff3ab1b935c6740b10dd4441074507761b
1d955aba1296ba52061dfec49c5e003bf84a354d30f2a1f00fb320256002b781da075
e838cc535ece42b26d6043af
KE3: 19624915863ad577d4c6c15d56207fe0dfd96a8ec489b4bc25a7b79cc33c1d75
717c0ba4db2cc5bb95d275ba5a5bcd39ad17459fb162d83455658acd20a4b4e4
export_key: 252cfa49d8fd722663fe16ef7451d8cc25345f05f4967859e7500763f
42b18b036fb1c4e7f0ad0110c71fb87f80da9ab53724976c4f8fa368d1c4cb6b9c6cd
e4
session_key: f31b89ce77171a51e6da037ad77f634184406844057f7875f04c7c4d
e08aa26d2f3bf9ab78e1e8a5faa6f625b12008d98da5e00d8aa36f9e097890337ac9c
005
~~~
### OPAQUE-3DH Real Test Vector 2
@@ -185,25 +185,25 @@ blind_login: e6f161ac189e6873a19a54efca4baa0719e801e336d929d35ca28b5b
#### Intermediate Values
~~~
client_public_key: 4e54972ea754782f53c77c027ee2d39a12ec1c8821047fc7c9
5bd286cbc9c279
auth_key: 0ffb3201ca1ee9cb60e18a952ad6f178cc9944f54c7b6e7ad29fef8e7f8
95dfc4446ab9e26f150b7e4a710853d4a63616d4c4c7cd5e95d2fbc706c16d5d8e846
randomized_pwd: 6f0250970339fbf8dae7950e3010fe30eb7eb4e83bfadf428983c
451f310f3765cfcfd1d6ed0c8929cb9df59443adbdc6b554f5c636f9d08ac545e84b1
9921dc
client_public_key: 003e4b5cd9124a026302a223cd2187fb62285d33d987155c4a
3307aa55f60c5e
auth_key: c34629e50ddce1db7aa01edb9cf6979890c40f0e55d8abfc388dae762a6
5300955344d1e273be49a1a513dbadeb045163b8882809f885dd46e978797484da85b
randomized_pwd: 2d0285e5872af71e6b6f3e76e1605de56ee229d1436563988aab1
13b2f8104a7a14377b71158451988bb4ec11a235fbac2063dad136cffd0b1f76019d4
123199
envelope: d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf274782
9b2d2906f10d832323d9a2a32a9f785e53cdb4a8534069409ab58e1886fa3e22de6b2
9476314d24fdb01a4ea80e358babe708a237ac17474b858a59482a58430c11ce
handshake_secret: b404a36e7d92890dbee76e443a86a0c2868569e81b5cfc7e16c
e60dfe1614d0707e875dcc65a9447b5ba00b9b0b97fcf4e6af7c47fc6199ec7122feb
9138c3e3
server_mac_key: 23ed6e15617061e9d7fdae661c1b746139ebb84f53bef5f6c29cc
e312c60fa36719aea50a338d7b02871da291ec44d0f5087c582c9147ac272c62f1244
6bd0bb
client_mac_key: 13750671047d8d0b7f119b222b6b33b7dd63d4e128a766f2cc76e
49792055232a58d5919117e2b91a5afd5756f93e3cb68ac2c9424651b05d30c282a59
e70f6c
9b2d2d20f701d2571da6f0fe29687724db96371b552df07e06c6cf3a2534147161ba4
5e543a6d4a15eda6af8ec5aab229abbaac2af81b48c1737a419a524bb618a48d
handshake_secret: b7cab8367758393c3a7c72b38accaa91e164d77be82d4c01364
0b8d2d6e1750f5ce1b77dcaf4173d07deeefc67895ad2d9ae6c6be8bdb384a9f54c7d
946ad85e
server_mac_key: aa959f4338f440f08dbaee86a576aae1ba65ee348f51e339feeda
8ce93ab0673904c1bb3de591689613f489705de68ae9e96146b0f54c7e0333fa50855
587fd5
client_mac_key: 3a3ca98df30379ac1cf59f3508b3daa60ea644c28f1e9de5a4911
f641514db7df1f46ec850ed2f9a2053f5a7cccd77f0a9b14e0e7c570319b8080c4484
cecf74
oprf_key: 531b0c7b0a3f90060c28d3d96ef5fecf56e25b8e4bf71c14bc770804c3f
b4507
~~~
@@ -213,36 +213,36 @@ b4507
~~~
registration_request: ec2927a03ced1220168b6d5a54f0372f813ced8ad3673d5
1dee92d2cbfee500c
registration_response: f6e244e131f8cd14bc37a856a933c91128b2498c06540d
2dba3a197ed7d8bd778aa90cb321a38759fc253c444f317782962ca18d33101eab2c8
registration_response: 588b259785af29c162958ebfdc4ca3b1fcf46bd1894c81
854db7d2d41bf1933c8aa90cb321a38759fc253c444f317782962ca18d33101eab2c8
cda04405a181f
registration_upload: 4e54972ea754782f53c77c027ee2d39a12ec1c8821047fc7
c95bd286cbc9c2793d81743982004acf05243d6c5405be93c137a8015727f59a4128d
db373cae8ffcef5db11460df6cbcc2c8d3ce0bb48fc58712a32de8dea7ac746671413
f9eeaad0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf2747829b2d
2906f10d832323d9a2a32a9f785e53cdb4a8534069409ab58e1886fa3e22de6b29476
314d24fdb01a4ea80e358babe708a237ac17474b858a59482a58430c11ce
registration_upload: 003e4b5cd9124a026302a223cd2187fb62285d33d987155c
4a3307aa55f60c5e3114123a82551b4851bbc26b4a6a25444a60c501bb3e8eae95f2a
eae7971f1358c5fe0cfa3cbf7f493be05418740bc884c1a20ad220c43ea30a7af1e92
3e81b7d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf2747829b2d
2d20f701d2571da6f0fe29687724db96371b552df07e06c6cf3a2534147161ba45e54
3a6d4a15eda6af8ec5aab229abbaac2af81b48c1737a419a524bb618a48d
KE1: d0a498e621d3ff7a011b37166a63ef40fe268f93c7d75a467eea42a98c0a490d
a6bcd29b5aecc3507fc1f8f7631af3d2f5105155222e48099e5e6085d8c1187a642e7
eecf19b804a62817486663d6c6c239396f709b663a4350cda67d025687a
KE2: e6d0c01cdc14f7bfdb38effbd63394f6304b47c2dc26fd510ecdbf471486b972
30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b5def228c8563f8f
17910f13eee01e10947637da13dc9a31c07c9dd324a8a59b22b4b58546a073d289b82
0b9658dcd6c387032248c1eae07ecf16eb63ea49c39cb3e9973d619e2e41299f00e42
defca71d240e72062c4466b4cd8b69b05ba4b3692e63f5ae15f08f142ae96fb245c48
4610d4a169fd99911bebae28dbb14a622452e7372b823fa57f7ef652185f89114109f
KE2: 6810ba4cf3049dab416529542385d194eddf0105ad6480ea0c92f872cab6af46
30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b5def228c854aa9f
c9066d61f2a64d5890ed79ff8e4a200444dc2e180ae57d6e78bdb594722d57283a3ff
9a6600f61341f9ad49c32be9054ad8a26837d8ccea6de753520b1eb836c85faa907a4
428828fb5ad362ff1b5327afdd1d1c300e03db05f9d55c1535bac50d76b4e5f19dcd7
0ee043d2254ffcae31bffaf426f0b862f5fd311649723fa57f7ef652185f89114109f
5a61cc8c9216fdd7398246bb7a0c20e2fbca2d8ae070cdffe5bb4b1c373e71be8e7d8
f356ee5de37881533f10397bcd84d354454fa8a9e5f63ce02d024dc602e2f61be2d7d
454ca88550af0851f3ee5e4de05e6e255c6b3bde1a47d808ca920783d5409040312d2
b3ee35f1fd8e24d009ed37ca
KE3: ea093c79f3d9d7718f3f632ed9c2e9004d069dea13ba7f08cd33de0f3042886e
bf6c4dc9cc9898a96f4b539b5b735f440005d07c9a636b6c5f76f2e78582ddc1
export_key: 987da0b1086098b5e11fcd74e9232515f2545779865cab6d911d78b4f
d4c9a464e9df1864419c26d7eb8708edf95a2a17b76ebbeb2979d30d6c2b4a3f26345
64
session_key: 5c2f66f5e3602b3150af772515ffb7cdfc9ace8626b319659f86f6c2
a01fe437d12a0c3f72f216bbaef007a35273bfa0d8d1220fb54e53a70f3fb9ae39345
064
f356ee5de37881533f10397bcd84d35445a04e7e9047091f5b371582b6f7eb8f8f665
6f800f2ad2491122a28fb5623987c28cd9d8fae45e0ab205cff7635c83fe993a83b0f
b2c1c85e599f0a33bd65a7ef
KE3: 37de6eb166462dd8d36a6f0c75bbe826856cf6f7f067935aaf5fddaeb6e56935
e8f1b53af96d5194dd49cb2438801745597affd3af7508f260746573d42144bd
export_key: cb43c5c131fc4ee3c72a9c5a664a0137d46088c58cf24de4408e838d8
1b743f6e1b9516cdc307775c45d3c70d3a446180782cb4a7dea4be5777b1934a78c11
e1
session_key: a40fa034f8220e18c1991f981655e8255009d62a596a579f08fd476a
08cca7d66189a19e68f180e520de10ee841ab4e4f1a61291468429f093776f0bc8663
528
~~~
### OPAQUE-3DH Real Test Vector 3
@@ -301,20 +301,20 @@ cd15c478
#### Intermediate Values
~~~
client_public_key: 0286913c9cdffc7a71c54cc5b32a3faa62cfba0180f0b76351
096409c9e19cd72e
auth_key: 91ec5a156946628872502569eee2a1a3d9deaa3320a11175ea462bfa607
98af5
randomized_pwd: 10706a06fc4c1c9101c655e0bc1fe9cf6fecdf38d8d0c15219bd2
4fc6d738214
client_public_key: 03c84a1dc96d2b896f20b390e75ae7e5ebedbb4db6c6cc9a78
96e3c5d5f280e7ab
auth_key: 9a6ce467dca8841cb0f706bfd83f39cc8e855d000d982554af799acc33d
8354e
randomized_pwd: d8a0060fa0d6118cf89fe9df92a9b65dd1b0dd86cccbdee067926
7dce6f50e3a
envelope: 2527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1acacc4
a831981a30154a6b4305a0d6906b704a148673ec0266683850abe9a2a91971eaa53b9
handshake_secret: 742560ea50065a0af34e458d98ef78f935bedf880437bc73164
15324dbab525c
server_mac_key: c3299b1ed28d5b8cb3e37ab09ef31a2a0406c0521d0faf2320a20
d7fdbb53ddc
client_mac_key: f96f80d13370635c3822fe6eba028ae3e05c1b8c298e05b32c669
83663cdb3a3
a8319bb19ce6d364d86bd95a1516a49e288e0e013a197609e1de0b4e9e5950ade9c13
handshake_secret: 3f11e9bb0233a88d5c00f236485058ebcbfd24180d0b8f7f078
e8b88fa8a1c04
server_mac_key: 7d82030842afc175e1a0fbddceba0e0e53102bf170ee394419d2b
fdefefda358
client_mac_key: a605eb6d2e72308f29fc1d1709a683aaa5d09e775134bded0deae
e17dccaa9a1
oprf_key: d153d662a1e7dd4383837aa7125685d2be6f8041472ecbfd610e46952a6
a24f1
~~~
@@ -324,30 +324,30 @@ a24f1
~~~
registration_request: 0325768a660df0c15f6f2a1dcbb7efd4f1c92702401edf3
e2f0742c8dce85d5fa8
registration_response: 0244211a4d2a067f7a61ed88dff6764856d347465f330d
0e15502700afd1865911025b95a6add1f2f3d038811b5ad3494bed73b1e2500d8dade
registration_response: 03de5c8f7d8ea7fd9590b0c8321b5f508bb8f49bbff83c
5449ef50d66bf3e93892025b95a6add1f2f3d038811b5ad3494bed73b1e2500d8dade
c592d88406e25c2f2
registration_upload: 0286913c9cdffc7a71c54cc5b32a3faa62cfba0180f0b763
51096409c9e19cd72e2dd8b5c51a4689c469f067e2766b987fa6c4ee4353b19f2974f
2407e2dc07fd92527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1aca
cc4a831981a30154a6b4305a0d6906b704a148673ec0266683850abe9a2a91971eaa5
3b9
registration_upload: 03c84a1dc96d2b896f20b390e75ae7e5ebedbb4db6c6cc9a
7896e3c5d5f280e7aba453596f5b719f2cf3c0982ebd2466a8442f3a98d9dcfe420b3
5acb7cd8d0e592527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1aca
cc4a8319bb19ce6d364d86bd95a1516a49e288e0e013a197609e1de0b4e9e5950ade9
c13
KE1: 03884e56429f1ee53559f2e244392eb8f994fd46c8fd9ffdd24ac5a7af963a66
3b967fcded96ed46986e60fcbdf985232639f537377ca3fcf07ad489956b2e9019033
58b4eae039953116889466bfddeb40168e39ed83809fd5f0d5f2de9c5234398
KE2: 0383fff1b3e8003723dff1b1f90a7934a036bd6691aca0366b07a100bf2bb3dc
2acb792f3657240ce5296dd5633e7333531009c11ee6ab46b6111f156d96a160b2977
de9d90759e8c525469d291422e8f4d66751b4b8d01fa9f62d142e46b78e672b8212b2
9d7d6d5d6884efd48a34827c7699d92e6dea7a8b608735f6bae399b1840c3f2eb0901
da4a2468467677abc843ec7d12e0dcc6359b74d145506fa79aebf8018e88ecfc53891
KE2: 0225dbce19cf48eb908d66d0e955d0fe7d0f67d09bb0362154c7316d69700e23
29cb792f3657240ce5296dd5633e7333531009c11ee6ab46b6111f156d96a160b268f
4a82c2b61a752672a3e322b6b8580c1a2c76fad4563d06c12a27146f73dbb5267ca8b
b86a83d0b902b97ac14d12501697300815c5d5fdc262830a351bb4416baceb16938e6
cfd021f43dee80b9bc400304a4398480e195bc51b3bcc186ff0bf8018e88ecfc53891
529278c47239f8fe6f1be88972721898ef81cc0a76a0b5500242bc29993976185dacf
6be815cbfa923aac80fad8b7f020c9d4f18e0b6867a175e6476d88558e5c2035530b5
3ce13597a06d22b9586040847b6d1e6f24a5ab90
KE3: a3dbf3ba53af908306d649ff0f7ff8db80311143d340356d0c817d02632e5e23
export_key: 4cd8130ebe797e83bb86a60dc100923349180d1da9c2f880791097111
c18aa96
session_key: 049948e93acc746a961a1efeec71af938957281a640623670f6f7a86
f5a3539b
6be815cbfa923aac80fad8b7f020c9d4f18e0b6867a17c4af032f0221800fec352a2b
ec9ddb2dd8b91a087aa51c7fbbaf5efcbbea52fe
KE3: eb86a68c5e8812293d1da4a60e499236ffdffb34b29f6f8f0ac46979f07b1ef4
export_key: b755602f5d0a8c2118f38608a98cf08f20adadf5ef759cea8e246e5ed
5bf95c3
session_key: 08b539a036c888da87a25205c9c386f382bc53b098dae42f88f2320c
48f1a3dd
~~~
### OPAQUE-3DH Real Test Vector 4
@@ -408,20 +408,20 @@ blind_login: 4308682dc1bdab92ff91bb1a5fc5bc084223fe4369beddca3f1640a6
#### Intermediate Values
~~~
client_public_key: 035d5fedb78e26e301de5b93bf181fba61f6407d8df3da212d
fdcfea66ef271f55
auth_key: 3bd6e6627f0f880f290fb77904f39ed2699e90b80440ba26f5eeb1bf30f
5f565
randomized_pwd: 59ae24f1bcf649d009bf972845e0aab0b37951c5201b6f2699707
2c79d84e104
client_public_key: 03a12f7047c8a1774a745520b2eaac995687fbb6212a418f9c
1696d4186278eaa3
auth_key: 2461460e02dde8a10c98e2911d4d5a3be0bd85f095064ade2f3a0ae79a4
89b07
randomized_pwd: 3d990218aab34ca0137bbbb298adcf585d4495ae843eddcbe3ca8
f969b690676
envelope: 75c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842e44
26e42e05c3e55ce81561193b699bd0981f86cda079361b1864b332335e239c707fa67
handshake_secret: 208f53119e80ea03007ab521ccad95a90fafb815551a6b0996c
0c96620415176
server_mac_key: d246f0371736ce72116f88cd15b59a87a7d3fe25a1b09d34a3f88
56bcb1b5785
client_mac_key: 2fc9858f5e228e9e344a790ff5157e96dbfa3b5ed2cfd050073cf
9ca4b49ee9f
26e423d452786ba80be94bd8ebe643394d1a07e745a07e97b37a88b585b8afd6ce3cb
handshake_secret: 586e927091452c797e0eb69fc90840f4c1923a6852834644c5f
47cc8b5810d55
server_mac_key: 09f90fb15227c185ba6102b797251d32f6bfaec56b218743ba4bb
c64696f9734
client_mac_key: 7612361b97d074852344ee92ecfe154b93241e696808438745d4c
b4f5b8513fb
oprf_key: f14e1fc34ba1218bfd3f7373f036889bf4f35a8fbc9e8c9c07ccf2d2388
79d9c
~~~
@@ -431,30 +431,30 @@ oprf_key: f14e1fc34ba1218bfd3f7373f036889bf4f35a8fbc9e8c9c07ccf2d2388
~~~
registration_request: 02792b0f4670aced5970a68b01bb951004ccad962159be4
b6783170c9ad68f6052
registration_response: 03cc3491b4bcb3e4804f3eadbc6a04c8fff18cc9ca5a4f
eeb577fdfebd71f5060f029a2c6097fbbcf3457fe3ff7d4ef8e89dab585a67dfed090
registration_response: 02101f7b9999e363b44dfa946eaad9930fda88d53632aa
701778747b6a411a071c029a2c6097fbbcf3457fe3ff7d4ef8e89dab585a67dfed090
5c9f104d909138bae
registration_upload: 035d5fedb78e26e301de5b93bf181fba61f6407d8df3da21
2dfdcfea66ef271f55f83490fc11b3ceccad8e302f5d7e98661dc91191f6fb742e05f
3278095a9741875c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842
e4426e42e05c3e55ce81561193b699bd0981f86cda079361b1864b332335e239c707f
a67
registration_upload: 03a12f7047c8a1774a745520b2eaac995687fbb6212a418f
9c1696d4186278eaa37b6e2a7531d9ca9a324ac5c1a02303f00175c41646a873441a5
eb69dcbec4ea975c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842
e4426e423d452786ba80be94bd8ebe643394d1a07e745a07e97b37a88b585b8afd6ce
3cb
KE1: 02fe96fc48d9fc921edd8e92ada581cbcc2a65e30962d0002ea5242f5baf627f
f646498f95ec7986f0602019b3fbb646db87a2fdbc12176d4f7ab74fa5fadace6002a
9f857ad3eabe09047049e8b8cee72feea2acb7fc487777c0b22d3add6a0e0c0
KE2: 035115b21dde0992cb812926d65c7dccd5e0f8ffff573da4a7c1e603e0e40827
895947586f69259e0708bdfab794f689eec14c7deb7edde68c81645156cf278f219d6
6e0743f1106cd46a10f249f404071bc798a39273d3cc3d2f4b88bb34e3b0df9488d32
72d42be1fddf60ee63c0628015aaefb8f4fa3ca2365e70041a1014c9dff4014550cd0
dd29a9b476f699b3a6d9528f98c27f471054f4883c875be99a0ed581ac468101aee52
KE2: 03463f69fc22bfa666c55bd38319addcf5816f063ec5ae9fdeb7e572603c6698
025947586f69259e0708bdfab794f689eec14c7deb7edde68c81645156cf278f219cb
3882d08a0617909c1a9f545dace3d56b5034d8025220e0280d5d541eb22ade140fa11
663bd0a4c787203b93e423f431b3702ffcc635919dcf0d22520d90596fccdef52f3ca
c75d2804a96d1521b78205c47c998cdb4aafcb7cce4c174671423581ac468101aee52
8cc6b69daac7a90de8837d49708e76310767cbe4af18594d022aa8746ab4329d59129
6652d44f6dfb04470103311bacd7ad51060ef5abac41bb37b28686cee6592632e4dba
19c914f418da2f9b79a4f0783edb013d189fe9b8
KE3: 342b1213e0b0ab4101decbae6a6759e174cfbaf916ebe304e07128ae3ebf6076
export_key: 446f5a94d53f60823f27547118f7b499636617a81c9bbcd28e0ddacb8
5bdce59
session_key: 6d15cde32da560bf0d2363066e66028515d9117348cf78d205bca58b
e35550b0
6652d44f6dfb04470103311bacd7ad51060ef5abac41ba38a2e46ce2cfd59c6dfdd1e
77758505d944b28e753a7254bac79302947dc7d0
KE3: 2ae94c682a2bc4c89eb16c395dc09d2b14d216dee0e59f34c317f5a6d8bbc717
export_key: 04f3100265180b083abbd84109f5ed963481eb78a5d377e888810217f
fb8af04
session_key: 7a0a42051497621e659270552be01baadddd1ee829f802891535a3fb
ac2a33ec
~~~
## Fake Test Vectors {#fake-vectors}
@@ -515,16 +515,16 @@ KE1: 1ef5fc13fa7695e81b5fcadf57eb49a579b10e4f51bbee11afb278608592456b
#### Output Values
~~~
KE2: 2e1bb024ff255d0f35eb7b1f11174b3e60d8aaabb11ea347a6da0c1964594f4f
KE2: 02648d7558231b92265efe08ec0b3dec70e596e36ea6c70ceae961411bf8f328
7cb33db5ba8082e4f4bfb830e8e3f525b0ddcb70469b34224758d725ce53ac76094c0
aa800d9a0884392e4efbc0479e3cb84a38c9ead879f1ff755ad762c06812b9858f82c
9722acc61b8eb1d156bc994839bf9ed8a760615258d23e0f94fa2cffadc655ed0d6ff
6914066427366019d4e6989b65d13e38e8edc5ae6f82aa1b6a46bfe6ca0256c64d0cf
db50a3eb7676e1d212e155e152e3bbc9d1fae3c679aacae1f4fee4ee4ba509fda550e
a0421a85762305b1db20e37f4539b2327d37b805e5c0ac2904c7d9bf38f99e0050594
e484b4d8ded8038ef6e0c141a985fa6b3528ef79e28dbd3783322ab69900a43be8919
a840cfcc5aa31a8f42b6f2a0c1ce1f9fa50c58dc5787a957af588580117b70d304639
dc68851224301bbbae9cd654
e484b4d8ded8038ef6e0c141a985fa6b35afc0c330be0512ba1eace7c1cae0b807f01
6f2a67b604008b270f3e41a8fb3d54084b62510495baa0309a993a48cf2110cfe2555
33047291134a010c13509ba1
~~~
### OPAQUE-3DH Fake Test Vector 2
@@ -581,13 +581,13 @@ KE1: 031ac7e5c8099fcb7de5ad5b6cf33ff53078dbee1da64f15f6cd53b2afe6e332
#### Output Values
~~~
KE2: 02200f91b03819f6a4b0957216fc94a2230d75d0e1be1fe0ced9434b0ec9d23a
5621cd364318a92b2afbfccea5d80d337f07defe40d92673a52f3844058f5d949a604
KE2: 02ed3cb4182cb2c2659d6c1d88014e821ea4fc00de1aca987fae5483f5f8aa59
d021cd364318a92b2afbfccea5d80d337f07defe40d92673a52f3844058f5d949a604
39294e7567fc29643e0d5c8799d0dffbbfc8609558b982012fa90aef2ce52b1ffdd8f
96bda49f5306ae346cd745812d3a953ff94712e4ed0acc67c99b432860e337fe3234b
ba88415ac55368b938106cca4049b5c13496fe167d3a092bd990e2b772c1eb569cc2b
57741bf3be630e377c8245b11d0b6ad1fe1d606490c2720802a59205c836a2ab86e19
dbd9a417818052179e9a5c99221e2d1d8a780dfe4734d7325a81225091665460460ec
37fcf0431f738ba6cb80b63756ee70c6e43aeae5
dbd9a417818052179e9a5c99221e2d1d8a780dfe4734dc9b9b3f64e5b3572a8f05f68
93b0fa4dd12fba85ea99c8760b8011321bc37263
~~~
"#;
-2
View File
@@ -15,10 +15,8 @@ fn parse_vector_types(input: &str) -> String {
let chunks: Vec<&str> = re.split(input).collect();
println!("{:?}", chunks.len());
let mut count = 1;
for caps in re.captures_iter(input) {
println!("{:?}", caps["type"].to_string());
let vector_type = format!(
"\"{}\": [\n {} \n]",
caps["type"].to_string(),
+53 -33
View File
@@ -11,6 +11,7 @@ use alloc::{string::ToString, vec, vec::Vec};
use json::JsonValue;
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct OpaqueTestVectorParameters {
pub dummy_private_key: Vec<u8>,
pub dummy_masking_key: Vec<u8>,
@@ -66,6 +67,19 @@ macro_rules! parse_default {
};
}
/// If no entry is found, default to filling a random buffer of a specified size
macro_rules! parse_default_random {
( $v:ident, $s:expr, $size:expr ) => {
parse_default!($v, $s, {
use rand::{rngs::OsRng, RngCore};
let mut rng = OsRng;
let mut v = vec![0u8; $size];
rng.fill_bytes(&mut v);
v
})
};
}
fn decode(values: &JsonValue, key: &str) -> Option<Vec<u8>> {
values[key]
.as_str()
@@ -74,8 +88,8 @@ fn decode(values: &JsonValue, key: &str) -> Option<Vec<u8>> {
fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters {
OpaqueTestVectorParameters {
dummy_private_key: parse_default!(values, "client_private_key", vec![0u8; 32]),
dummy_masking_key: parse_default!(values, "masking_key", vec![0u8; 64]),
dummy_private_key: parse_default_random!(values, "client_private_key", 32),
dummy_masking_key: parse_default_random!(values, "masking_key", 64),
context: parse!(values, "Context"),
client_private_key: decode(values, "client_private_key"),
client_keyshare: parse!(values, "client_keyshare"),
@@ -121,7 +135,7 @@ fn get_password_file_bytes<CS: CipherSuite>(
RegistrationUpload::deserialize(&parameters.registration_upload[..]).unwrap(),
);
Ok(password_file.serialize())
password_file.serialize()
}
fn parse_identifiers(
@@ -141,9 +155,15 @@ fn parse_identifiers(
macro_rules! json_to_test_vectors {
( $v:ident, $vector_type:expr, $cs:expr, ) => {
$v[$vector_type][$cs]
$v[$vector_type]
.members()
.map(|x| populate_test_vectors(&x))
.filter_map(|x| {
if x.has_key($cs) {
Some(populate_test_vectors(&x[$cs]))
} else {
None
}
})
.collect::<Vec<OpaqueTestVectorParameters>>()
};
}
@@ -153,16 +173,13 @@ fn tests() -> Result<(), ProtocolError> {
let rfc = json::parse(super::parser::rfc_to_json(super::opaque_vectors::VECTORS).as_str())
.expect("Could not parse json");
let ristretto_real_tvs = json_to_test_vectors!(
rfc,
String::from("Real"),
String::from("ristretto255, SHA512"),
);
let ristretto_fake_tvs = json_to_test_vectors!(
rfc,
String::from("Fake"),
String::from("ristretto255, SHA512"),
);
let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512",);
let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "ristretto255, SHA512",);
if ristretto_real_tvs.len() == 0 || ristretto_fake_tvs.len() == 0 {
panic!("Parsing error");
}
struct Ristretto255Sha512NoSlowHash;
impl CipherSuite for Ristretto255Sha512NoSlowHash {
@@ -184,16 +201,14 @@ fn tests() -> Result<(), ProtocolError> {
#[cfg(feature = "p256")]
{
let p256_real_tvs = json_to_test_vectors!(
rfc,
String::from("Real"),
String::from("P256_XMD:SHA-256_SSWU_RO_, SHA256"),
);
let p256_fake_tvs = json_to_test_vectors!(
rfc,
String::from("Fake"),
String::from("P256_XMD:SHA-256_SSWU_RO_, SHA256"),
);
let p256_real_tvs =
json_to_test_vectors!(rfc, "Real", "P256_XMD:SHA-256_SSWU_RO_, SHA256",);
let p256_fake_tvs =
json_to_test_vectors!(rfc, "Fake", "P256_XMD:SHA-256_SSWU_RO_, SHA256",);
if p256_real_tvs.len() == 0 || p256_fake_tvs.len() == 0 {
panic!("Parsing error");
}
struct P256Sha256NoSlowHash;
impl CipherSuite for P256Sha256NoSlowHash {
@@ -226,7 +241,7 @@ fn test_registration_request<CS: CipherSuite>(
ClientRegistration::<CS>::start(&mut rng, &parameters.password)?;
assert_eq!(
hex::encode(&parameters.registration_request),
hex::encode(client_registration_start_result.message.serialize())
hex::encode(client_registration_start_result.message.serialize()?)
);
}
Ok(())
@@ -255,7 +270,7 @@ fn test_registration_response<CS: CipherSuite>(
);
assert_eq!(
hex::encode(&parameters.registration_response),
hex::encode(server_registration_start_result.message.serialize())
hex::encode(server_registration_start_result.message.serialize()?)
);
}
Ok(())
@@ -278,7 +293,6 @@ fn test_registration_upload<CS: CipherSuite>(
Some(ids) => ClientRegistrationFinishParameters::new(Some(ids), None),
},
)?;
assert_eq!(
hex::encode(&parameters.auth_key),
hex::encode(result.auth_key)
@@ -289,7 +303,7 @@ fn test_registration_upload<CS: CipherSuite>(
);
assert_eq!(
hex::encode(&parameters.registration_upload),
hex::encode(result.message.serialize())
hex::encode(result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.export_key),
@@ -308,12 +322,18 @@ fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
&parameters.client_nonce[..],
]
.concat();
println!(
"&parameters.blind_login[..]: {:?}",
hex::encode(&parameters.blind_login[..])
);
let mut client_login_start_rng = CycleRng::new(client_login_start);
let client_login_start_result =
ClientLogin::<CS>::start(&mut client_login_start_rng, &parameters.password)?;
assert_eq!(
hex::encode(&parameters.KE1),
hex::encode(client_login_start_result.message.serialize())
hex::encode(client_login_start_result.message.serialize()?)
);
}
Ok(())
@@ -370,7 +390,7 @@ fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
);
assert_eq!(
hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize())
hex::encode(server_login_start_result.message.serialize()?)
);
}
Ok(())
@@ -416,7 +436,7 @@ fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
);
assert_eq!(
hex::encode(&parameters.KE3),
hex::encode(client_login_finish_result.message.serialize())
hex::encode(client_login_finish_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.export_key),
@@ -516,7 +536,7 @@ fn test_fake_vectors<CS: CipherSuite>(
)?;
assert_eq!(
hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize())
hex::encode(server_login_start_result.message.serialize()?)
);
}
Ok(())
-158
View File
@@ -1,158 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use crate::group::Group;
use crate::hash::Hash;
use crate::tests::mock_rng::CycleRng;
use crate::{errors::*, oprf};
use alloc::string::ToString;
use alloc::vec::Vec;
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::GenericArray;
use serde_json::Value;
use sha2::Sha512;
struct VOPRFTestVectorParameters {
sksm: Vec<u8>,
input: Vec<u8>,
blind: Vec<u8>,
blinded_element: Vec<u8>,
evaluation_element: Vec<u8>,
output: Vec<u8>,
}
// Taken from https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md
// in base mode
static OPRF_RISTRETTO255_SHA512: &[&str] = &[
r#"
{
"sksm": "caeff69352df4905a9121a4997704ca8cee1524a110819eb87deba1a39ec1701",
"input": "00",
"blind": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03",
"blinded_element": "fc20e03aff3a9de9b37e8d35886ade11ec7d85c2a1fb5bb0b1686c64e07ac467",
"evaluation_element": "7c72cc293cd7d44c0b57c273f27befd598b132edc665694bdc9c42a4d3083c0a",
"output": "e3a209dce2d3ea3d84fcddb282818caebb756a341e08a310d9904314f5392085d13c3f76339d745db0f46974a6049c3ea9546305af55d37760b2136d9b3f0134"
}
"#,
r#"
{
"sksm": "caeff69352df4905a9121a4997704ca8cee1524a110819eb87deba1a39ec1701",
"input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b",
"blinded_element": "483d4f39de5ff77fa0f9a0ad2334dd5bf87f2cda868539d21de67ce49e7d1536",
"evaluation_element": "026f2758fc62f02a7ff95f35ec6f20186aa57c0274361655543ea235d7b2aa34",
"output": "2c17dc3e9398dadb44bb2d3360c446302e99f1fe0ec40f0b1ad25c9cf002be1e4b41b4900ef056537fe8c14532ccea4d796f5feab9541af48057d83c0db86fe9"
}
"#,
];
#[cfg(feature = "p256")]
static OPRF_P256_SHA256: &[&str] = &[
r#"
{
"sksm": "a1b2355828f2c76de6749af9d093bd9fe0f2cada3ec653cd9a6d3126a7a7827b",
"input": "00",
"blind": "5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98af0d0",
"blinded_element": "03e3c379698da853d9844098fa0ac676970d5ec24167b598714cd2ee188604ddd2",
"evaluation_element": "03ea54e8d095332d1a601a3f8a5013188aea036bf9b563236f7fd3b046908b42fd",
"output": "464e3e51e4086a824d9a2f939524d7069ae4072a788bc9d5daa0762b25826437"
}
"#,
r#"
{
"sksm": "a1b2355828f2c76de6749af9d093bd9fe0f2cada3ec653cd9a6d3126a7a7827b",
"input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"blind": "825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5fcbe",
"blinded_element": "030b40be181ffbb3c3ae4a4911287c43261f5e4034781def69c51608f372a02102",
"evaluation_element": "03115ad70ea55dbb4006da0ee3589a3582f31ef9cd143996d1e31a25ad3abdcf6f",
"output": "b597d58c843d0f9d2712121b0a3e2912ebee1c829eed3089eade9af4359ab275"
}
"#,
];
fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
values[key]
.as_str()
.and_then(|s| hex::decode(&s.to_string()).ok())
}
fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
VOPRFTestVectorParameters {
sksm: decode(&values, "sksm").unwrap(),
input: decode(&values, "input").unwrap(),
blind: decode(&values, "blind").unwrap(),
blinded_element: decode(&values, "blinded_element").unwrap(),
evaluation_element: decode(&values, "evaluation_element").unwrap(),
output: decode(&values, "output").unwrap(),
}
}
#[test]
fn tests() -> Result<(), ProtocolError> {
test_blind::<RistrettoPoint, Sha512>(OPRF_RISTRETTO255_SHA512)?;
test_evaluate::<RistrettoPoint>(OPRF_RISTRETTO255_SHA512)?;
test_finalize::<RistrettoPoint, Sha512>(OPRF_RISTRETTO255_SHA512)?;
#[cfg(feature = "p256")]
{
use p256_::ProjectivePoint;
use sha2::Sha256;
test_blind::<ProjectivePoint, Sha256>(OPRF_P256_SHA256)?;
test_evaluate::<ProjectivePoint>(OPRF_P256_SHA256)?;
test_finalize::<ProjectivePoint, Sha256>(OPRF_P256_SHA256)?;
}
Ok(())
}
// Tests input -> blind, blinded_element
fn test_blind<G: Group, H: Hash>(tvs: &[&str]) -> Result<(), ProtocolError> {
for tv in tvs {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let mut rng = CycleRng::new(parameters.blind.to_vec());
let (token, blinded_element) = oprf::blind::<_, G, H>(&parameters.input, &mut rng)?;
assert_eq!(&parameters.blind, &G::scalar_as_bytes(token.blind).to_vec());
assert_eq!(
&parameters.blinded_element,
&blinded_element.to_arr().to_vec()
);
}
Ok(())
}
// Tests sksm, blinded_element -> evaluation_element
fn test_evaluate<G: Group>(tvs: &[&str]) -> Result<(), ProtocolError> {
for tv in tvs {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let evaluation_element = oprf::evaluate::<G>(
G::from_element_slice(GenericArray::from_slice(&parameters.blinded_element)).unwrap(),
&G::from_scalar_slice(GenericArray::from_slice(&parameters.sksm)).unwrap(),
);
assert_eq!(
&parameters.evaluation_element,
&evaluation_element.to_arr().to_vec()
);
}
Ok(())
}
// Tests input, blind, evaluation_element -> output
fn test_finalize<G: Group, H: Hash>(tvs: &[&str]) -> Result<(), ProtocolError> {
for tv in tvs {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let output = oprf::finalize::<G, H>(
&parameters.input,
&G::from_scalar_slice(GenericArray::from_slice(&parameters.blind))?,
G::from_element_slice(GenericArray::from_slice(&parameters.evaluation_element))?,
)?;
assert_eq!(&parameters.output, &output.to_vec());
}
Ok(())
}