Using voprf as a dependency (#248)
* Using voprf as a dependency * Adding back x25519 and KeGroup * Addressing comments
This commit is contained in:
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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![];
|
||||
|
||||
@@ -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
@@ -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
@@ -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),
|
||||
)?),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)>;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(®istration_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,
|
||||
®istration_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,
|
||||
¶ms.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
@@ -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
@@ -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
@@ -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(¶meters));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -468,11 +464,11 @@ fn test_registration_request() -> Result<(), ProtocolError> {
|
||||
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut rng, ¶meters.password)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.registration_request),
|
||||
hex::encode(client_registration_start_result.message.serialize())
|
||||
hex::encode(client_registration_start_result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.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(®istration_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(®istration_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(¶meters.credential_request),
|
||||
hex::encode(client_login_start_result.message.serialize())
|
||||
hex::encode(client_login_start_result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.client_login_state),
|
||||
@@ -654,11 +654,11 @@ fn test_credential_response() -> Result<(), ProtocolError> {
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.credential_response),
|
||||
hex::encode(server_login_start_result.message.serialize())
|
||||
hex::encode(server_login_start_result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.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(¶meters.credential_finalization),
|
||||
hex::encode(client_login_finish_result.message.serialize())
|
||||
hex::encode(client_login_finish_result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.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,
|
||||
|
||||
@@ -8,4 +8,3 @@ pub mod mock_rng;
|
||||
mod opaque_vectors;
|
||||
mod parser;
|
||||
mod test_opaque_vectors;
|
||||
mod voprf_test_vectors;
|
||||
|
||||
+158
-158
@@ -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
|
||||
~~~
|
||||
"#;
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(¶meters.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, ¶meters.password)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.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(¶meters.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(¶meters.auth_key),
|
||||
hex::encode(result.auth_key)
|
||||
@@ -289,7 +303,7 @@ fn test_registration_upload<CS: CipherSuite>(
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.registration_upload),
|
||||
hex::encode(result.message.serialize())
|
||||
hex::encode(result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.export_key),
|
||||
@@ -308,12 +322,18 @@ fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
|
||||
¶meters.client_nonce[..],
|
||||
]
|
||||
.concat();
|
||||
|
||||
println!(
|
||||
"¶meters.blind_login[..]: {:?}",
|
||||
hex::encode(¶meters.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, ¶meters.password)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.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(¶meters.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(¶meters.KE3),
|
||||
hex::encode(client_login_finish_result.message.serialize())
|
||||
hex::encode(client_login_finish_result.message.serialize()?)
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.export_key),
|
||||
@@ -516,7 +536,7 @@ fn test_fake_vectors<CS: CipherSuite>(
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.KE2),
|
||||
hex::encode(server_login_start_result.message.serialize())
|
||||
hex::encode(server_login_start_result.message.serialize()?)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -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>(¶meters.input, &mut rng)?;
|
||||
|
||||
assert_eq!(¶meters.blind, &G::scalar_as_bytes(token.blind).to_vec());
|
||||
assert_eq!(
|
||||
¶meters.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(¶meters.blinded_element)).unwrap(),
|
||||
&G::from_scalar_slice(GenericArray::from_slice(¶meters.sksm)).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
¶meters.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>(
|
||||
¶meters.input,
|
||||
&G::from_scalar_slice(GenericArray::from_slice(¶meters.blind))?,
|
||||
G::from_element_slice(GenericArray::from_slice(¶meters.evaluation_element))?,
|
||||
)?;
|
||||
|
||||
assert_eq!(¶meters.output, &output.to_vec());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user