General improvements (#250)
* Remove unnecessary constraints on hash * Remove unnecessary `Result` on `KeyPair::generate_random` * Fix de-serialization issue on `Ke1State` * Fix rustfmt * Remove allocations in `envelope` * Run Clippy for tests and rustdoc lints too * Fix `Debug` implementation * Fix missing constraints on `ClientRegistration` * Fix de-serialization * Pin temporary dependency * Update dependencies * Replace macro with derive-where * Remove unnecessary installation of Rust components * Improve macro naming * Implement `Copy`, `Debug`, `Ord` and `PartialOrd` for high-level items * Add `rust-version` field to `Cargo.toml` * Remove unnecessary allocations * Fix MSRV * Fix no_std * Remove unnecessary allocations * Remove unnecessary allocations * Not importing items from voprf helps readability * Fix rustdoc * Remove unnecessary allocations * Remove unnecessary allocations * Replace `Vec` from `diffie_hellman` with `GenericArray` * Remove unnecessary allocations * Remove unnecessary allocations * Remove `cfg(feature = bench)` guard for `missing_docs` * Fix documentation * Remove all remaining allocations from `KeyExchange` * Improve type-safety * Remove all remaining allocations in `keypair` * Remove last remaining allocations except `NonVerifiableClient` input * Remove base64 encoding in Serde implementation * Remove unnecessary Serde `alloc` feature * Make curve25519-dalek optional * Rename `serialize` crate feature to `serde` * Switch `KeGroup` implementations to higher-level libraries - Fixes missing clamping in X25519 - X25519 is now a separate crate feature * Fix typo
This commit is contained in:
+40
-26
@@ -4,7 +4,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, repoened, synchronize]
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -13,14 +13,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256
|
||||
- x25519_u64,ristretto255_u64
|
||||
toolchain:
|
||||
- stable
|
||||
- 1.51.0
|
||||
exclude:
|
||||
- backend_feature: p256,u64_backend
|
||||
- backend_feature: p256
|
||||
toolchain: 1.51.0
|
||||
name: test
|
||||
steps:
|
||||
@@ -33,7 +34,6 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -57,9 +57,10 @@ jobs:
|
||||
# 32-bit x86
|
||||
- i686-unknown-linux-gnu
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256
|
||||
- x25519_u64,ristretto255_u64
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
@@ -76,12 +77,18 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256
|
||||
- ristretto255_u64,p256
|
||||
- x25519_u64,ristretto255_u64
|
||||
- x25519_u32,ristretto255_u32
|
||||
- x25519_u64,p256
|
||||
- x25519_u32,p256
|
||||
- x25519_u64,ristretto255_u64,p256
|
||||
frontend_feature:
|
||||
- slow-hash
|
||||
- serialize
|
||||
- serde
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
@@ -108,7 +115,6 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Run expect (which then runs cargo run)
|
||||
run: expect -f scripts/simple_login.exp
|
||||
|
||||
@@ -132,7 +138,6 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Run expect (which then runs cargo run)
|
||||
run: expect -f scripts/digital_locker.exp
|
||||
|
||||
@@ -148,12 +153,13 @@ jobs:
|
||||
# for any no_std target
|
||||
- thumbv6m-none-eabi
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256
|
||||
- x25519_u64,ristretto255_u64
|
||||
frontend_feature:
|
||||
- slow-hash
|
||||
- serialize
|
||||
- serde
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
@@ -167,9 +173,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256
|
||||
- x25519_u64,ristretto255_u64
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
@@ -185,7 +192,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: --no-default-features --features bench --features ${{ matrix.backend_feature }} --no-run
|
||||
args: --no-default-features --features ${{ matrix.backend_feature }} --no-run
|
||||
|
||||
clippy:
|
||||
name: cargo clippy
|
||||
@@ -200,14 +207,21 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
components: clippy
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
args: --all-targets -- -D warnings
|
||||
|
||||
- name: Run cargo doc
|
||||
uses: actions-rs/cargo@v1
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
with:
|
||||
command: doc
|
||||
args: --no-deps --document-private-items --features p256,slow-hash,std
|
||||
|
||||
format:
|
||||
name: cargo fmt
|
||||
@@ -222,7 +236,7 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
Regular → Executable
+25
-13
@@ -10,22 +10,33 @@ license = "Apache-2.0 OR MIT"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
resolver = "2"
|
||||
rust-version = "1.51"
|
||||
|
||||
[features]
|
||||
default = ["u64_backend", "serialize"]
|
||||
default = ["ristretto255_u64", "serde"]
|
||||
slow-hash = ["argon2"]
|
||||
p256 = ["p256_", "voprf/p256"]
|
||||
bench = []
|
||||
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"]
|
||||
ristretto255 = []
|
||||
ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255", "voprf/ristretto255_u64"]
|
||||
ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255", "voprf/ristretto255_u32"]
|
||||
ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255", "voprf/ristretto255_fiat_u64"]
|
||||
ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255", "voprf/ristretto255_fiat_u32"]
|
||||
ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255", "voprf/ristretto255_simd"]
|
||||
x25519 = []
|
||||
x25519_u64 = ["x25519", "x25519-dalek/u64_backend"]
|
||||
x25519_u32 = ["x25519", "x25519-dalek/u32_backend"]
|
||||
x25519_fiat_u64 = ["x25519", "x25519-dalek/fiat_u64_backend"]
|
||||
x25519_fiat_u32 = ["x25519", "x25519-dalek/fiat_u32_backend"]
|
||||
# x25519-dalek isn't properly re-exposing `simd_backend`.
|
||||
x25519_simd = ["curve25519-dalek/simd_backend", "x25519", "x25519-dalek/nightly"]
|
||||
std = ["getrandom", "rand/std", "rand/std_rng", "voprf/std"]
|
||||
serde = ["serde_", "generic-array/serde", "voprf/serde"]
|
||||
|
||||
[dependencies]
|
||||
argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true }
|
||||
base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true }
|
||||
constant_time_eq = "0.1"
|
||||
curve25519-dalek = { version = "3", default-features = false }
|
||||
curve25519-dalek = { version = "3", default-features = false, optional = true }
|
||||
derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] }
|
||||
digest = "0.9"
|
||||
displaydoc = { version = "0.2", default-features = false }
|
||||
generic-array = "0.14"
|
||||
@@ -34,9 +45,10 @@ hkdf = "0.11"
|
||||
hmac = "0.11"
|
||||
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 }
|
||||
serde_ = { version = "1", package = "serde", default-features = false, features = ["derive"], optional = true }
|
||||
subtle = { version = "2.3", default-features = false }
|
||||
voprf = { version = "0.2", default-features = false, features = ["danger"] }
|
||||
voprf = { git = "https://github.com/khonsulabs/voprf", rev = "f8c19eab4ecc9e7a2a5ae26c59661ce797229566", default-features = false, features = ["danger"] }
|
||||
x25519-dalek = { version = "1", default-features = false, optional = true }
|
||||
zeroize = { version = "1", features = ["zeroize_derive"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
@@ -45,7 +57,7 @@ getrandom = { version = "0.2", features = ["js"], optional = true }
|
||||
[dev-dependencies]
|
||||
base64 = "0.13"
|
||||
bincode = "1"
|
||||
chacha20poly1305 = "0.8"
|
||||
chacha20poly1305 = "0.9"
|
||||
criterion = "0.3"
|
||||
hex = "0.4"
|
||||
json = "0.12"
|
||||
@@ -55,9 +67,9 @@ serde_json = "1"
|
||||
sha2 = "0.9"
|
||||
proptest = "1"
|
||||
regex = "1"
|
||||
rustyline = "8"
|
||||
# Version 9.1 requires an MSRV of 1.56
|
||||
rustyline = "~9.0"
|
||||
|
||||
[[bench]]
|
||||
name = "opaque"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
+48
-49
@@ -12,16 +12,20 @@ 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")]
|
||||
#[cfg(feature = "ristretto255_u64")]
|
||||
static SUFFIX: &str = "ristretto255_u64";
|
||||
#[cfg(feature = "ristretto255_u32")]
|
||||
static SUFFIX: &str = "ristretto255_u32";
|
||||
#[cfg(feature = "ristretto255_fiat_u64")]
|
||||
static SUFFIX: &str = "ristretto255_fiat_u64";
|
||||
#[cfg(feature = "ristretto255_fiat_u32")]
|
||||
static SUFFIX: &str = "ristretto255_fiat_u32";
|
||||
#[cfg(all(not(feature = "ristretto255"), feature = "p256"))]
|
||||
static SUFFIX: &str = "p256";
|
||||
|
||||
struct Default;
|
||||
|
||||
#[cfg(not(feature = "p256"))]
|
||||
#[cfg(feature = "ristretto255")]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -30,10 +34,10 @@ impl CipherSuite for Default {
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::PublicKey;
|
||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -44,7 +48,7 @@ fn server_setup(c: &mut Criterion) {
|
||||
|
||||
c.bench_function(&format!("server setup ({})", SUFFIX), move |b| {
|
||||
b.iter(|| {
|
||||
ServerSetup::<Default>::new(&mut rng).unwrap();
|
||||
ServerSetup::<Default>::new(&mut rng);
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -57,7 +61,7 @@ fn client_registration_start(c: &mut Criterion) {
|
||||
&format!("client registration start ({})", SUFFIX),
|
||||
move |b| {
|
||||
b.iter(|| {
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
})
|
||||
},
|
||||
);
|
||||
@@ -67,9 +71,9 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!("server registration start ({})", SUFFIX),
|
||||
@@ -78,7 +82,7 @@ fn server_registration_start(c: &mut Criterion) {
|
||||
ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
@@ -90,13 +94,13 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -122,21 +126,20 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
let client_registration_finish_result = client_registration_start_result
|
||||
.clone()
|
||||
.state
|
||||
.finish(
|
||||
&mut rng,
|
||||
server_registration_start_result.message.clone(),
|
||||
server_registration_start_result.message,
|
||||
ClientRegistrationFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -157,7 +160,7 @@ fn client_login_start(c: &mut Criterion) {
|
||||
|
||||
c.bench_function(&format!("client login start ({})", SUFFIX), move |b| {
|
||||
b.iter(|| {
|
||||
ClientLogin::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientLogin::<Default>::start(&mut rng, password).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -166,26 +169,25 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
let client_registration_finish_result = client_registration_start_result
|
||||
.clone()
|
||||
.state
|
||||
.finish(
|
||||
&mut rng,
|
||||
server_registration_start_result.message.clone(),
|
||||
server_registration_start_result.message,
|
||||
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 client_login_start_result = ClientLogin::<Default>::start(&mut rng, password).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!("server login start (real) ({})", SUFFIX),
|
||||
@@ -196,7 +198,7 @@ fn server_login_start_real(c: &mut Criterion) {
|
||||
&server_setup,
|
||||
Some(password_file.clone()),
|
||||
client_login_start_result.clone().message,
|
||||
&username[..],
|
||||
username,
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -209,8 +211,8 @@ 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();
|
||||
let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_login_start_result = ClientLogin::<Default>::start(&mut rng, password).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!("server login start (fake) ({})", SUFFIX),
|
||||
@@ -221,7 +223,7 @@ fn server_login_start_fake(c: &mut Criterion) {
|
||||
&server_setup,
|
||||
None,
|
||||
client_login_start_result.clone().message,
|
||||
&username[..],
|
||||
username,
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -234,32 +236,31 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
let client_registration_finish_result = client_registration_start_result
|
||||
.clone()
|
||||
.state
|
||||
.finish(
|
||||
&mut rng,
|
||||
server_registration_start_result.message.clone(),
|
||||
server_registration_start_result.message,
|
||||
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 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()),
|
||||
Some(password_file),
|
||||
client_login_start_result.clone().message,
|
||||
&username[..],
|
||||
username,
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -282,37 +283,35 @@ 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 server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
let client_registration_start_result =
|
||||
ClientRegistration::<Default>::start(&mut rng, &password[..]).unwrap();
|
||||
ClientRegistration::<Default>::start(&mut rng, password).unwrap();
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
client_registration_start_result.message.clone(),
|
||||
&username[..],
|
||||
username,
|
||||
)
|
||||
.unwrap();
|
||||
let client_registration_finish_result = client_registration_start_result
|
||||
.clone()
|
||||
.state
|
||||
.finish(
|
||||
&mut rng,
|
||||
server_registration_start_result.message.clone(),
|
||||
server_registration_start_result.message,
|
||||
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 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()),
|
||||
Some(password_file),
|
||||
client_login_start_result.clone().message,
|
||||
&username[..],
|
||||
username,
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let client_login_finish_result = client_login_start_result
|
||||
.clone()
|
||||
.state
|
||||
.finish(
|
||||
server_login_start_result.clone().message,
|
||||
|
||||
+32
-29
@@ -28,6 +28,8 @@
|
||||
|
||||
use chacha20poly1305::aead::{Aead, NewAead};
|
||||
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
||||
use generic_array::GenericArray;
|
||||
use opaque_ke::ServerRegistrationLen;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
use std::process::exit;
|
||||
@@ -45,6 +47,8 @@ use opaque_ke::{
|
||||
// that will be used in the OPAQUE protocol
|
||||
#[allow(dead_code)]
|
||||
struct Default;
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -53,9 +57,18 @@ impl CipherSuite for Default {
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::PublicKey;
|
||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
struct Locker {
|
||||
contents: Vec<u8>,
|
||||
password_file: Vec<u8>,
|
||||
password_file: GenericArray<u8, ServerRegistrationLen<Default>>,
|
||||
}
|
||||
|
||||
// Given a key and plaintext, produce an AEAD ciphertext along with a nonce
|
||||
@@ -92,22 +105,16 @@ 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()
|
||||
.unwrap();
|
||||
let registration_request_bytes = client_registration_start_result.message.serialize();
|
||||
|
||||
// Client sends registration_request_bytes to server
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
RegistrationRequest::deserialize(®istration_request_bytes[..]).unwrap(),
|
||||
server_setup,
|
||||
RegistrationRequest::deserialize(®istration_request_bytes).unwrap(),
|
||||
&locker_id.to_be_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
let registration_response_bytes = server_registration_start_result
|
||||
.message
|
||||
.serialize()
|
||||
.unwrap();
|
||||
let registration_response_bytes = server_registration_start_result.message.serialize();
|
||||
|
||||
// Server sends registration_response_bytes to client
|
||||
|
||||
@@ -115,14 +122,11 @@ fn register_locker(
|
||||
.state
|
||||
.finish(
|
||||
&mut client_rng,
|
||||
RegistrationResponse::deserialize(®istration_response_bytes[..]).unwrap(),
|
||||
RegistrationResponse::deserialize(®istration_response_bytes).unwrap(),
|
||||
ClientRegistrationFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let message_bytes = client_finish_registration_result
|
||||
.message
|
||||
.serialize()
|
||||
.unwrap();
|
||||
let message_bytes = client_finish_registration_result.message.serialize();
|
||||
|
||||
// Client encrypts secret message using export key
|
||||
let ciphertext = encrypt(
|
||||
@@ -133,12 +137,12 @@ fn register_locker(
|
||||
// Client sends message_bytes to server
|
||||
|
||||
let password_file = ServerRegistration::finish(
|
||||
RegistrationUpload::<Default>::deserialize(&message_bytes[..]).unwrap(),
|
||||
RegistrationUpload::<Default>::deserialize(&message_bytes).unwrap(),
|
||||
);
|
||||
|
||||
Locker {
|
||||
contents: ciphertext,
|
||||
password_file: password_file.serialize().unwrap(),
|
||||
password_file: password_file.serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,28 +156,27 @@ 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().unwrap();
|
||||
let credential_request_bytes = client_login_start_result.message.serialize();
|
||||
|
||||
// Client sends credential_request_bytes to server
|
||||
|
||||
let password_file =
|
||||
ServerRegistration::<Default>::deserialize(&locker.password_file[..]).unwrap();
|
||||
let password_file = ServerRegistration::<Default>::deserialize(&locker.password_file).unwrap();
|
||||
let mut server_rng = OsRng;
|
||||
let server_login_start_result = ServerLogin::start(
|
||||
&mut server_rng,
|
||||
&server_setup,
|
||||
server_setup,
|
||||
Some(password_file),
|
||||
CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(),
|
||||
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
||||
&locker_id.to_be_bytes(),
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
|
||||
let credential_response_bytes = server_login_start_result.message.serialize();
|
||||
|
||||
// Server sends credential_response_bytes to client
|
||||
|
||||
let result = client_login_start_result.state.finish(
|
||||
CredentialResponse::deserialize(&credential_response_bytes[..]).unwrap(),
|
||||
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
||||
ClientLoginFinishParameters::default(),
|
||||
);
|
||||
|
||||
@@ -182,13 +185,13 @@ 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().unwrap();
|
||||
let credential_finalization_bytes = client_login_finish_result.message.serialize();
|
||||
|
||||
// Client sends credential_finalization_bytes to server
|
||||
|
||||
let server_login_finish_result = server_login_start_result
|
||||
.state
|
||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes[..]).unwrap())
|
||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap())
|
||||
.unwrap();
|
||||
|
||||
// Server sends locker contents, encrypted under the session key, to the client
|
||||
@@ -208,7 +211,7 @@ fn open_locker(
|
||||
|
||||
fn main() {
|
||||
let mut rng = OsRng;
|
||||
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
|
||||
let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
|
||||
let mut rl = Editor::<()>::new();
|
||||
let mut registered_lockers: Vec<Locker> = vec![];
|
||||
@@ -292,7 +295,7 @@ fn main() {
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn display_lockers(lockers: &Vec<Locker>) {
|
||||
fn display_lockers(lockers: &[Locker]) {
|
||||
let mut locker_numbers = vec![];
|
||||
for (i, _) in lockers.iter().enumerate() {
|
||||
locker_numbers.push(i);
|
||||
|
||||
+33
-28
@@ -22,6 +22,7 @@
|
||||
//! messages over "the wire" to the server. These bytes are serialized
|
||||
//! and explicitly annotated in the below functions.
|
||||
|
||||
use generic_array::GenericArray;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
use std::collections::HashMap;
|
||||
@@ -31,13 +32,16 @@ use opaque_ke::{
|
||||
ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters,
|
||||
ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
|
||||
CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
|
||||
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, ServerSetup,
|
||||
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
|
||||
ServerRegistrationLen, ServerSetup,
|
||||
};
|
||||
|
||||
// The ciphersuite trait allows to specify the underlying primitives
|
||||
// that will be used in the OPAQUE protocol
|
||||
#[allow(dead_code)]
|
||||
struct Default;
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -46,32 +50,35 @@ impl CipherSuite for Default {
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::PublicKey;
|
||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
// Password-based registration between a client and server
|
||||
fn account_registration(
|
||||
server_setup: &ServerSetup<Default>,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Vec<u8> {
|
||||
) -> GenericArray<u8, ServerRegistrationLen<Default>> {
|
||||
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()
|
||||
.unwrap();
|
||||
let registration_request_bytes = client_registration_start_result.message.serialize();
|
||||
|
||||
// Client sends registration_request_bytes to server
|
||||
|
||||
let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||
&server_setup,
|
||||
RegistrationRequest::deserialize(®istration_request_bytes[..]).unwrap(),
|
||||
server_setup,
|
||||
RegistrationRequest::deserialize(®istration_request_bytes).unwrap(),
|
||||
username.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
let registration_response_bytes = server_registration_start_result
|
||||
.message
|
||||
.serialize()
|
||||
.unwrap();
|
||||
let registration_response_bytes = server_registration_start_result.message.serialize();
|
||||
|
||||
// Server sends registration_response_bytes to client
|
||||
|
||||
@@ -79,21 +86,18 @@ fn account_registration(
|
||||
.state
|
||||
.finish(
|
||||
&mut client_rng,
|
||||
RegistrationResponse::deserialize(®istration_response_bytes[..]).unwrap(),
|
||||
RegistrationResponse::deserialize(®istration_response_bytes).unwrap(),
|
||||
ClientRegistrationFinishParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let message_bytes = client_finish_registration_result
|
||||
.message
|
||||
.serialize()
|
||||
.unwrap();
|
||||
let message_bytes = client_finish_registration_result.message.serialize();
|
||||
|
||||
// Client sends message_bytes to server
|
||||
|
||||
let password_file = ServerRegistration::finish(
|
||||
RegistrationUpload::<Default>::deserialize(&message_bytes[..]).unwrap(),
|
||||
RegistrationUpload::<Default>::deserialize(&message_bytes).unwrap(),
|
||||
);
|
||||
password_file.serialize().unwrap()
|
||||
password_file.serialize()
|
||||
}
|
||||
|
||||
// Password-based login between a client and server
|
||||
@@ -106,7 +110,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().unwrap();
|
||||
let credential_request_bytes = client_login_start_result.message.serialize();
|
||||
|
||||
// Client sends credential_request_bytes to server
|
||||
|
||||
@@ -114,19 +118,19 @@ fn account_login(
|
||||
let mut server_rng = OsRng;
|
||||
let server_login_start_result = ServerLogin::start(
|
||||
&mut server_rng,
|
||||
&server_setup,
|
||||
server_setup,
|
||||
Some(password_file),
|
||||
CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(),
|
||||
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
||||
username.as_bytes(),
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
|
||||
let credential_response_bytes = server_login_start_result.message.serialize();
|
||||
|
||||
// Server sends credential_response_bytes to client
|
||||
|
||||
let result = client_login_start_result.state.finish(
|
||||
CredentialResponse::deserialize(&credential_response_bytes[..]).unwrap(),
|
||||
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
||||
ClientLoginFinishParameters::default(),
|
||||
);
|
||||
|
||||
@@ -135,13 +139,13 @@ fn account_login(
|
||||
return false;
|
||||
}
|
||||
let client_login_finish_result = result.unwrap();
|
||||
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
|
||||
let credential_finalization_bytes = client_login_finish_result.message.serialize();
|
||||
|
||||
// Client sends credential_finalization_bytes to server
|
||||
|
||||
let server_login_finish_result = server_login_start_result
|
||||
.state
|
||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes[..]).unwrap())
|
||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap())
|
||||
.unwrap();
|
||||
|
||||
client_login_finish_result.session_key == server_login_finish_result.session_key
|
||||
@@ -149,10 +153,11 @@ fn account_login(
|
||||
|
||||
fn main() {
|
||||
let mut rng = OsRng;
|
||||
let server_setup = ServerSetup::<Default>::new(&mut rng).unwrap();
|
||||
let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
|
||||
let mut rl = Editor::<()>::new();
|
||||
let mut registered_users = HashMap::<String, Vec<u8>>::new();
|
||||
let mut registered_users =
|
||||
HashMap::<String, GenericArray<u8, ServerRegistrationLen<Default>>>::new();
|
||||
loop {
|
||||
println!(
|
||||
"\nCurrently registered usernames: {:?}\n",
|
||||
|
||||
Regular → Executable
+101
-124
@@ -12,12 +12,17 @@ use crate::{
|
||||
key_exchange::group::KeGroup,
|
||||
keypair::{KeyPair, PublicKey},
|
||||
opaque::{bytestrings_from_identifiers, Identifiers},
|
||||
serialization::{MacExt, Serialize},
|
||||
};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryFrom;
|
||||
use digest::Digest;
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use core::ops::Add;
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::{Digest, FixedOutput};
|
||||
use generic_array::{
|
||||
sequence::Concat,
|
||||
typenum::{Sum, Unsigned, U2, U32},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
@@ -25,13 +30,13 @@ use voprf::group::Group;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// Constant string used as salt for HKDF computation
|
||||
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;
|
||||
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";
|
||||
type NonceLen = U32;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
|
||||
#[zeroize(drop)]
|
||||
pub(crate) enum InnerEnvelopeMode {
|
||||
Zero = 0,
|
||||
@@ -49,42 +54,31 @@ impl TryFrom<u8> for InnerEnvelopeMode {
|
||||
}
|
||||
|
||||
/// This struct is an instantiation of the envelope as described in
|
||||
/// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4
|
||||
/// <https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4>
|
||||
///
|
||||
/// Note that earlier versions of this specification described an
|
||||
/// implementation of this envelope using an encryption scheme that
|
||||
/// satisfied random-key robustness
|
||||
/// (https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05#section-4).
|
||||
/// (<https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05#section-4>).
|
||||
/// The specification update has simplified this assumption by taking
|
||||
/// an XOR-based approach without compromising on security, and to avoid
|
||||
/// the confusion around the implementation of an RKR-secure encryption.
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub(crate) struct Envelope<CS: CipherSuite> {
|
||||
mode: InnerEnvelopeMode,
|
||||
nonce: Vec<u8>,
|
||||
nonce: GenericArray<u8, NonceLen>,
|
||||
hmac: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for Envelope<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
mode: self.mode.clone(),
|
||||
nonce: self.nonce.clone(),
|
||||
hmac: self.hmac.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_debug_eq_hash_for!(struct Envelope<CS: CipherSuite>, [mode, nonce, hmac]);
|
||||
|
||||
// Note that this struct represents an envelope that has been "opened" with the asssociated
|
||||
// key. This key is also used to derive the export_key parameter, which is technically
|
||||
// unrelated to the envelope's encrypted and authenticated contents.
|
||||
pub(crate) struct OpenedEnvelope<CS: CipherSuite> {
|
||||
pub(crate) struct OpenedEnvelope<'a, CS: CipherSuite> {
|
||||
pub(crate) client_static_keypair: KeyPair<CS::KeGroup>,
|
||||
pub(crate) export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
pub(crate) id_u: Vec<u8>,
|
||||
pub(crate) id_s: Vec<u8>,
|
||||
pub(crate) id_u: Serialize<'a, U2, <CS::KeGroup as KeGroup>::PkLen>,
|
||||
pub(crate) id_s: Serialize<'a, U2, <CS::KeGroup as KeGroup>::PkLen>,
|
||||
}
|
||||
|
||||
pub(crate) struct OpenedInnerEnvelope<D: Hash> {
|
||||
@@ -100,7 +94,7 @@ type SealRawResult<CS> = (
|
||||
type SealRawResult<CS> = (
|
||||
Envelope<CS>,
|
||||
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
|
||||
Vec<u8>,
|
||||
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
|
||||
);
|
||||
#[cfg(not(test))]
|
||||
type SealResult<CS> = (
|
||||
@@ -113,30 +107,36 @@ type SealResult<CS> = (
|
||||
Envelope<CS>,
|
||||
PublicKey<<CS as CipherSuite>::KeGroup>,
|
||||
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
|
||||
Vec<u8>,
|
||||
GenericArray<u8, <<CS as CipherSuite>::Hash as Digest>::OutputSize>,
|
||||
);
|
||||
|
||||
#[allow(type_alias_bounds)]
|
||||
pub(crate) type EnvelopeLen<CS: CipherSuite> = Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>;
|
||||
|
||||
impl<CS: CipherSuite> Envelope<CS> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
server_s_pk: &[u8],
|
||||
optional_ids: Option<Identifiers>,
|
||||
server_s_pk: &PublicKey<CS::KeGroup>,
|
||||
ids: Identifiers,
|
||||
) -> Result<SealResult<CS>, ProtocolError> {
|
||||
let mut nonce = vec![0u8; NONCE_LEN];
|
||||
let mut nonce = GenericArray::default();
|
||||
rng.fill_bytes(&mut nonce);
|
||||
|
||||
let (mode, client_s_pk) = (
|
||||
InnerEnvelopeMode::Internal,
|
||||
build_inner_envelope_internal::<CS>(randomized_pwd_hasher.clone(), &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 (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
|
||||
ids,
|
||||
client_s_pk.to_arr(),
|
||||
server_s_pk.to_arr(),
|
||||
)?;
|
||||
let aad = construct_aad(id_u.iter(), id_s.iter(), server_s_pk);
|
||||
|
||||
let result = Self::seal_raw(randomized_pwd_hasher, &nonce, &aad, mode)?;
|
||||
let result = Self::seal_raw(randomized_pwd_hasher, nonce, aad, mode)?;
|
||||
Ok((
|
||||
result.0,
|
||||
client_s_pk,
|
||||
@@ -149,64 +149,64 @@ impl<CS: CipherSuite> Envelope<CS> {
|
||||
/// Uses a key to convert the plaintext into an envelope, authenticated by the aad field.
|
||||
/// Note that a new nonce is sampled for each call to seal.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn seal_raw(
|
||||
pub(crate) fn seal_raw<'a>(
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
nonce: &[u8],
|
||||
aad: &[u8],
|
||||
nonce: GenericArray<u8, NonceLen>,
|
||||
aad: impl Iterator<Item = &'a [u8]>,
|
||||
mode: InnerEnvelopeMode,
|
||||
) -> Result<SealRawResult<CS>, InternalError> {
|
||||
let mut hmac_key = vec![0u8; Self::hmac_key_size()];
|
||||
let mut export_key = vec![0u8; Self::export_key_size()];
|
||||
let mut hmac_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
let mut export_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
|
||||
randomized_pwd_hasher
|
||||
.expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
|
||||
.expand_multi_info(&[&nonce, &STR_AUTH_KEY], &mut hmac_key)
|
||||
.map_err(|_| InternalError::HkdfError)?;
|
||||
randomized_pwd_hasher
|
||||
.expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key)
|
||||
.expand_multi_info(&[&nonce, &STR_EXPORT_KEY], &mut export_key)
|
||||
.map_err(|_| InternalError::HkdfError)?;
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<CS::Hash>::new_from_slice(&hmac_key).map_err(|_| InternalError::HmacError)?;
|
||||
hmac.update(nonce);
|
||||
hmac.update(aad);
|
||||
hmac.update(&nonce);
|
||||
hmac.update_iter(aad);
|
||||
|
||||
let hmac_bytes = hmac.finalize().into_bytes();
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
mode,
|
||||
nonce: nonce.to_vec(),
|
||||
nonce,
|
||||
hmac: hmac_bytes,
|
||||
},
|
||||
GenericArray::clone_from_slice(&export_key),
|
||||
export_key,
|
||||
#[cfg(test)]
|
||||
hmac_key,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn open(
|
||||
pub(crate) fn open<'a>(
|
||||
&self,
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
server_s_pk: &[u8],
|
||||
optional_ids: &Option<Identifiers>,
|
||||
) -> Result<OpenedEnvelope<CS>, ProtocolError> {
|
||||
server_s_pk: PublicKey<CS::KeGroup>,
|
||||
optional_ids: Identifiers<'a>,
|
||||
) -> Result<OpenedEnvelope<'a, CS>, ProtocolError> {
|
||||
let client_static_keypair = match self.mode {
|
||||
InnerEnvelopeMode::Zero => {
|
||||
return Err(InternalError::IncompatibleEnvelopeModeError.into())
|
||||
}
|
||||
InnerEnvelopeMode::Internal => {
|
||||
recover_keys_internal::<CS>(randomized_pwd_hasher.clone(), &self.nonce)?
|
||||
recover_keys_internal::<CS>(randomized_pwd_hasher.clone(), self.nonce)?
|
||||
}
|
||||
};
|
||||
|
||||
let (id_u, id_s) = bytestrings_from_identifiers(
|
||||
let (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
|
||||
optional_ids,
|
||||
&client_static_keypair.public().to_arr(),
|
||||
server_s_pk,
|
||||
client_static_keypair.public().to_arr(),
|
||||
server_s_pk.to_arr(),
|
||||
)?;
|
||||
let aad = construct_aad(&id_u, &id_s, server_s_pk);
|
||||
let aad = construct_aad(id_u.iter(), id_s.iter(), &server_s_pk);
|
||||
|
||||
let opened = self.open_raw(randomized_pwd_hasher, &aad)?;
|
||||
let opened = self.open_raw(randomized_pwd_hasher, aad)?;
|
||||
|
||||
Ok(OpenedEnvelope {
|
||||
client_static_keypair,
|
||||
@@ -218,51 +218,37 @@ impl<CS: CipherSuite> Envelope<CS> {
|
||||
|
||||
/// Attempts to decrypt the envelope using a key, which is successful only if the key and
|
||||
/// aad used to construct the envelope are the same.
|
||||
pub(crate) fn open_raw(
|
||||
pub(crate) fn open_raw<'a>(
|
||||
&self,
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
aad: &[u8],
|
||||
aad: impl Iterator<Item = &'a [u8]>,
|
||||
) -> Result<OpenedInnerEnvelope<CS::Hash>, InternalError> {
|
||||
let mut hmac_key = vec![0u8; Self::hmac_key_size()];
|
||||
let mut export_key = vec![0u8; Self::export_key_size()];
|
||||
let mut hmac_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
let mut export_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
|
||||
randomized_pwd_hasher
|
||||
.expand(
|
||||
&[self.nonce.clone(), STR_AUTH_KEY.to_vec()].concat(),
|
||||
&mut hmac_key,
|
||||
)
|
||||
.expand(&self.nonce.concat(STR_AUTH_KEY.into()), &mut hmac_key)
|
||||
.map_err(|_| InternalError::HkdfError)?;
|
||||
randomized_pwd_hasher
|
||||
.expand(
|
||||
&[self.nonce.clone(), STR_EXPORT_KEY.to_vec()].concat(),
|
||||
&mut export_key,
|
||||
)
|
||||
.expand(&self.nonce.concat(STR_EXPORT_KEY.into()), &mut export_key)
|
||||
.map_err(|_| InternalError::HkdfError)?;
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<CS::Hash>::new_from_slice(&hmac_key).map_err(|_| InternalError::HmacError)?;
|
||||
hmac.update(&self.nonce);
|
||||
hmac.update(aad);
|
||||
if hmac.verify(&self.hmac).is_err() {
|
||||
return Err(InternalError::SealOpenHmacError);
|
||||
}
|
||||
hmac.update_iter(aad);
|
||||
hmac.verify(&self.hmac)
|
||||
.map_err(|_| InternalError::SealOpenHmacError)?;
|
||||
|
||||
Ok(OpenedInnerEnvelope {
|
||||
export_key: GenericArray::<u8, <CS::Hash as Digest>::OutputSize>::clone_from_slice(
|
||||
&export_key,
|
||||
),
|
||||
})
|
||||
Ok(OpenedInnerEnvelope { export_key })
|
||||
}
|
||||
|
||||
// Creates a dummy envelope object that serializes to the all-zeros byte string
|
||||
pub(crate) fn dummy() -> Self {
|
||||
Self {
|
||||
mode: InnerEnvelopeMode::Zero,
|
||||
nonce: vec![0u8; NONCE_LEN],
|
||||
hmac: GenericArray::clone_from_slice(&vec![
|
||||
0u8;
|
||||
<CS::Hash as Digest>::OutputSize::USIZE
|
||||
]),
|
||||
nonce: GenericArray::default(),
|
||||
hmac: GenericArray::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,34 +256,36 @@ impl<CS: CipherSuite> Envelope<CS> {
|
||||
<CS::Hash as Digest>::OutputSize::USIZE
|
||||
}
|
||||
|
||||
fn export_key_size() -> usize {
|
||||
<CS::Hash as Digest>::OutputSize::USIZE
|
||||
}
|
||||
|
||||
pub(crate) fn len() -> usize {
|
||||
<CS::Hash as Digest>::OutputSize::USIZE + NONCE_LEN
|
||||
<CS::Hash as Digest>::OutputSize::USIZE + NonceLen::USIZE
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> Vec<u8> {
|
||||
[&self.nonce[..], &self.hmac[..]].concat()
|
||||
pub(crate) fn serialize(&self) -> GenericArray<u8, EnvelopeLen<CS>>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
self.nonce.concat(self.hmac.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this?
|
||||
|
||||
if bytes.len() < NONCE_LEN {
|
||||
if bytes.len() < NonceLen::USIZE {
|
||||
return Err(ProtocolError::SerializationError);
|
||||
}
|
||||
let nonce = bytes[..NONCE_LEN].to_vec();
|
||||
let nonce = GenericArray::clone_from_slice(&bytes[..NonceLen::USIZE]);
|
||||
|
||||
let remainder = match mode {
|
||||
InnerEnvelopeMode::Zero => {
|
||||
return Err(InternalError::IncompatibleEnvelopeModeError.into())
|
||||
}
|
||||
InnerEnvelopeMode::Internal => bytes[NONCE_LEN..].to_vec(),
|
||||
InnerEnvelopeMode::Internal => &bytes[NonceLen::USIZE..],
|
||||
};
|
||||
|
||||
let hmac_key_size = Self::hmac_key_size();
|
||||
let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?;
|
||||
let hmac = check_slice_size(remainder, hmac_key_size, "hmac_key_size")?;
|
||||
|
||||
Ok(Self {
|
||||
mode,
|
||||
@@ -307,35 +295,20 @@ impl<CS: CipherSuite> Envelope<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for Envelope<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.mode.zeroize();
|
||||
self.nonce.zeroize();
|
||||
self.hmac.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for Envelope<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn build_inner_envelope_internal<CS: CipherSuite>(
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
nonce: &[u8],
|
||||
nonce: GenericArray<u8, NonceLen>,
|
||||
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
|
||||
let mut keypair_seed = vec![0u8; <CS::KeGroup as KeGroup>::SkLen::USIZE];
|
||||
let mut keypair_seed = GenericArray::<_, <CS::KeGroup as KeGroup>::SkLen>::default();
|
||||
randomized_pwd_hasher
|
||||
.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
||||
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &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, _, _>(
|
||||
Some(&keypair_seed[..]),
|
||||
GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
|
||||
Some(keypair_seed.as_slice()),
|
||||
GenericArray::from(STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
|
||||
)?),
|
||||
)?;
|
||||
|
||||
@@ -344,22 +317,26 @@ fn build_inner_envelope_internal<CS: CipherSuite>(
|
||||
|
||||
fn recover_keys_internal<CS: CipherSuite>(
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
nonce: &[u8],
|
||||
nonce: GenericArray<u8, NonceLen>,
|
||||
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
|
||||
let mut keypair_seed = vec![0u8; <CS::KeGroup as KeGroup>::SkLen::USIZE];
|
||||
let mut keypair_seed = GenericArray::<_, <CS::KeGroup as KeGroup>::SkLen>::default();
|
||||
randomized_pwd_hasher
|
||||
.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
||||
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &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, _, _>(
|
||||
Some(&keypair_seed[..]),
|
||||
GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
|
||||
Some(keypair_seed.as_slice()),
|
||||
GenericArray::from(STR_OPAQUE_DERIVE_AUTH_KEY_PAIR),
|
||||
)?),
|
||||
)?;
|
||||
|
||||
Ok(client_static_keypair)
|
||||
}
|
||||
|
||||
fn construct_aad(id_u: &[u8], id_s: &[u8], server_s_pk: &[u8]) -> Vec<u8> {
|
||||
[server_s_pk, id_s, id_u].concat()
|
||||
fn construct_aad<'a>(
|
||||
id_u: impl Iterator<Item = &'a [u8]>,
|
||||
id_s: impl Iterator<Item = &'a [u8]>,
|
||||
server_s_pk: &'a [u8],
|
||||
) -> impl Iterator<Item = &'a [u8]> {
|
||||
chain!(Some(server_s_pk).into_iter(), id_s, id_u)
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ use std::error::Error;
|
||||
use displaydoc::Display;
|
||||
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Display, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Copy, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum InternalError<T = Infallible> {
|
||||
/// Custom [`SecretKey`](crate::keypair::SecretKey) error type
|
||||
Custom(T),
|
||||
@@ -129,7 +129,7 @@ impl From<voprf::errors::InternalError> for ProtocolError {
|
||||
}
|
||||
|
||||
/// Represents an error in protocol handling
|
||||
#[derive(Clone, Display, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Copy, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum ProtocolError<T = Infallible> {
|
||||
/// Internal error encountered
|
||||
LibraryError(InternalError<T>),
|
||||
|
||||
Regular → Executable
+49
-121
@@ -5,139 +5,67 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
macro_rules! impl_debug_eq_hash_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::fmt::Debug,)+)?
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("$name")
|
||||
.field("$field1", &self.$field1)
|
||||
$(.field("$field2", &self.$field2))*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)?
|
||||
$(where $($type: Eq,)+)?
|
||||
{}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
||||
$(where $($type: PartialEq,)+)?
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(&self.$field1, &other.$field1)
|
||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::hash::Hash,)+)?
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&self.$field1, state);
|
||||
$(core::hash::Hash::hash(&self.$field2, state);)*
|
||||
}
|
||||
}
|
||||
};
|
||||
(tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::fmt::Debug,)+)?
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_tuple("$name")
|
||||
.field(&self.$field1)
|
||||
$(.field(&self.$field2))*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)?
|
||||
$(where $($type: Eq,)+)?
|
||||
{}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
||||
$(where $($type: PartialEq,)+)?
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(&self.$field1, &other.$field1)
|
||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::hash::Hash,)+)?
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&self.$field1, state);
|
||||
$(core::hash::Hash::hash(&self.$field2, state);)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_clone_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)?
|
||||
$(where $($type: Clone,)+)?
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
$field1: self.$field1.clone(),
|
||||
$($field2: self.$field2.clone(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)?
|
||||
$(where $($type: Clone,)+)?
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(
|
||||
self.$field1.clone(),
|
||||
$(self.$field2.clone(),)*
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
||||
/// Macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
||||
macro_rules! impl_serialize_and_deserialize_for {
|
||||
($t:ident) => {
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<CS: CipherSuite> serde::Serialize for $t<CS> {
|
||||
($item:ident$( where $($path:ty: $bound1:path $(| $bound2:path)*),+$(,)?)?$(; $error:expr)?) => {
|
||||
#[cfg(feature = "serde")]
|
||||
impl<CS: CipherSuite> serde_::Serialize for $item<CS>
|
||||
$(where
|
||||
$($path: $bound1 $(+ $bound2)*),+
|
||||
)?
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
S: serde_::Serializer,
|
||||
{
|
||||
use serde::ser::Error;
|
||||
|
||||
if serializer.is_human_readable() {
|
||||
serializer
|
||||
.serialize_str(&base64::encode(&self.serialize().map_err(Error::custom)?))
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.serialize().map_err(Error::custom)?)
|
||||
}
|
||||
serializer.serialize_bytes(&self.serialize()$(.map_err($error)?)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t<CS> {
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, CS: CipherSuite> serde_::Deserialize<'de> for $item<CS>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
D: serde_::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
use serde_::de::Error;
|
||||
|
||||
if deserializer.is_human_readable() {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
Self::deserialize(&base64::decode(s).map_err(Error::custom)?)
|
||||
} else {
|
||||
Self::deserialize(<&[u8]>::deserialize(deserializer)?)
|
||||
struct ByteVisitor<CS: CipherSuite>(core::marker::PhantomData<CS>);
|
||||
|
||||
impl<'de, CS: CipherSuite> serde_::de::Visitor<'de> for ByteVisitor<CS>
|
||||
{
|
||||
type Value = $item<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: Error,
|
||||
{
|
||||
$item::<CS>::deserialize(value).map_err(|_| {
|
||||
Error::invalid_value(
|
||||
serde_::de::Unexpected::Bytes(value),
|
||||
&core::concat!(
|
||||
"invalid byte sequence for ",
|
||||
core::stringify!($t)
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
.map_err(Error::custom)
|
||||
|
||||
deserializer
|
||||
.deserialize_bytes(ByteVisitor::<CS>(core::marker::PhantomData))
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,5 +37,7 @@ pub trait KeGroup: Sized + Clone {
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
pub mod p256;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub mod ristretto255;
|
||||
#[cfg(feature = "x25519")]
|
||||
pub mod x25519;
|
||||
|
||||
@@ -11,39 +11,39 @@ use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
use generic_array::typenum::{U32, U33};
|
||||
use generic_array::GenericArray;
|
||||
use p256_::elliptic_curve::group::GroupEncoding;
|
||||
use p256_::elliptic_curve::sec1::ToEncodedPoint;
|
||||
use p256_::elliptic_curve::{PublicKey, SecretKey};
|
||||
use p256_::NistP256;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
impl KeGroup for p256_::ProjectivePoint {
|
||||
impl KeGroup for PublicKey<NistP256> {
|
||||
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)
|
||||
Self::from_sec1_bytes(element_bits).map_err(|_| 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()
|
||||
SecretKey::<NistP256>::random(rng).to_bytes()
|
||||
}
|
||||
|
||||
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self {
|
||||
Self::generator() * p256_::Scalar::from_bytes_reduced(sk)
|
||||
SecretKey::<NistP256>::from_bytes(sk).unwrap().public_key()
|
||||
}
|
||||
|
||||
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
|
||||
GenericArray::clone_from_slice(self.to_encoded_point(true).as_bytes())
|
||||
}
|
||||
|
||||
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen> {
|
||||
(self * &p256_::Scalar::from_bytes_reduced(sk)).to_arr()
|
||||
(self.to_projective()
|
||||
* SecretKey::<NistP256>::from_bytes(sk)
|
||||
.unwrap()
|
||||
.to_secret_scalar()
|
||||
.as_ref())
|
||||
.to_affine()
|
||||
.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl KeGroup for RistrettoPoint {
|
||||
self.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::PkLen> {
|
||||
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::SkLen> {
|
||||
(self * Scalar::from_bits(*sk.as_ref())).to_arr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,152 +5,47 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Key Exchange group implementation for x25519
|
||||
//! 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};
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
|
||||
/// The implementation of such a subgroup for Ristretto
|
||||
impl KeGroup for MontgomeryPoint {
|
||||
impl KeGroup for PublicKey {
|
||||
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()))
|
||||
Ok(Self::from(<[u8; 32]>::from(*element_bits)))
|
||||
}
|
||||
|
||||
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Self::SkLen> {
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
|
||||
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)
|
||||
}
|
||||
rng.fill_bytes(&mut 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());
|
||||
if scalar_bytes != [0u8; 32] {
|
||||
break StaticSecret::from(scalar_bytes).to_bytes().into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn public_key(sk: &GenericArray<u8, Self::SkLen>) -> Self {
|
||||
X25519_BASEPOINT * Scalar::from_bits(*sk.as_ref())
|
||||
Self::from(&StaticSecret::from(<[u8; 32]>::from(*sk)))
|
||||
}
|
||||
|
||||
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(())
|
||||
fn diffie_hellman(&self, sk: &GenericArray<u8, Self::SkLen>) -> GenericArray<u8, Self::SkLen> {
|
||||
StaticSecret::from(<[u8; 32]>::from(*sk))
|
||||
.diffie_hellman(self)
|
||||
.to_bytes()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+46
-23
@@ -12,7 +12,8 @@ use crate::{
|
||||
hash::Hash,
|
||||
keypair::{PrivateKey, PublicKey, SecretKey},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use digest::Digest;
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@@ -25,23 +26,26 @@ pub type GenerateKe2Result<K, D, G> = (
|
||||
pub type GenerateKe2Result<K, D, G> = (
|
||||
<K as KeyExchange<D, G>>::KE2State,
|
||||
<K as KeyExchange<D, G>>::KE2Message,
|
||||
Vec<u8>,
|
||||
generic_array::GenericArray<u8, <D as digest::Digest>::OutputSize>,
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
);
|
||||
#[cfg(not(test))]
|
||||
pub type GenerateKe3Result<K, D, G> = (Vec<u8>, <K as KeyExchange<D, G>>::KE3Message);
|
||||
pub type GenerateKe3Result<K, D, G> = (
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
<K as KeyExchange<D, G>>::KE3Message,
|
||||
);
|
||||
#[cfg(test)]
|
||||
pub type GenerateKe3Result<K, D, G> = (
|
||||
Vec<u8>,
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
<K as KeyExchange<D, G>>::KE3Message,
|
||||
Vec<u8>,
|
||||
generic_array::GenericArray<u8, <D as digest::Digest>::OutputSize>,
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
);
|
||||
|
||||
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 KE1Message: FromBytes + ToBytes + Zeroize + Clone;
|
||||
type KE2Message: FromBytes + ToBytes + Clone;
|
||||
type KE3Message: FromBytes + ToBytes + Clone;
|
||||
|
||||
@@ -50,44 +54,63 @@ pub trait KeyExchange<D: Hash, G: KeGroup> {
|
||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
fn generate_ke2<R: RngCore + CryptoRng, S: SecretKey<G>>(
|
||||
fn generate_ke2<'a, 'b, 'c, 'd, R: RngCore + CryptoRng, S: SecretKey<G>>(
|
||||
rng: &mut R,
|
||||
l1_bytes: Vec<u8>,
|
||||
l2_bytes: Vec<u8>,
|
||||
l1_bytes: impl Iterator<Item = &'a [u8]>,
|
||||
l2_bytes: impl Iterator<Item = &'b [u8]>,
|
||||
ke1_message: Self::KE1Message,
|
||||
client_s_pk: PublicKey<G>,
|
||||
server_s_sk: S,
|
||||
id_u: Vec<u8>,
|
||||
id_s: Vec<u8>,
|
||||
context: Vec<u8>,
|
||||
id_u: impl Iterator<Item = &'c [u8]>,
|
||||
id_s: impl Iterator<Item = &'d [u8]>,
|
||||
context: &[u8],
|
||||
) -> Result<GenerateKe2Result<Self, D, G>, ProtocolError<S::Error>>;
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
fn generate_ke3(
|
||||
l2_component: Vec<u8>,
|
||||
fn generate_ke3<'a, 'b, 'c, 'd>(
|
||||
l2_component: impl Iterator<Item = &'a [u8]>,
|
||||
ke2_message: Self::KE2Message,
|
||||
ke1_state: &Self::KE1State,
|
||||
serialized_credential_request: &[u8],
|
||||
serialized_credential_request: impl Iterator<Item = &'b [u8]>,
|
||||
server_s_pk: PublicKey<G>,
|
||||
client_s_sk: PrivateKey<G>,
|
||||
id_u: Vec<u8>,
|
||||
id_s: Vec<u8>,
|
||||
context: Vec<u8>,
|
||||
id_u: impl Iterator<Item = &'c [u8]>,
|
||||
id_s: impl Iterator<Item = &'d [u8]>,
|
||||
context: &[u8],
|
||||
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn finish_ke(
|
||||
ke3_message: Self::KE3Message,
|
||||
ke2_state: &Self::KE2State,
|
||||
) -> Result<Vec<u8>, ProtocolError>;
|
||||
) -> Result<GenericArray<u8, D::OutputSize>, ProtocolError>;
|
||||
|
||||
fn ke2_message_size() -> usize;
|
||||
}
|
||||
|
||||
pub trait FromBytes: Sized {
|
||||
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError>;
|
||||
fn from_bytes(input: &[u8]) -> Result<Self, ProtocolError>;
|
||||
}
|
||||
|
||||
pub trait ToBytes {
|
||||
fn to_bytes(&self) -> Vec<u8>;
|
||||
type Len: ArrayLength<u8>;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len>;
|
||||
}
|
||||
|
||||
#[allow(dead_code, type_alias_bounds)]
|
||||
pub type Ke1StateLen<CS: CipherSuite> =
|
||||
<<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State as ToBytes>::Len;
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type Ke1MessageLen<CS: CipherSuite> =
|
||||
<<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message as ToBytes>::Len;
|
||||
#[allow(type_alias_bounds)]
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type Ke2StateLen<CS: CipherSuite> =
|
||||
<<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State as ToBytes>::Len;
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type Ke2MessageLen<CS: CipherSuite> =
|
||||
<<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message as ToBytes>::Len;
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type Ke3MessageLen<CS: CipherSuite> =
|
||||
<<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message as ToBytes>::Len;
|
||||
|
||||
Regular → Executable
+218
-193
@@ -7,7 +7,6 @@
|
||||
|
||||
//! An implementation of the Triple Diffie-Hellman key exchange protocol
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
errors::{
|
||||
utils::{check_slice_size, check_slice_size_atleast},
|
||||
InternalError, ProtocolError,
|
||||
@@ -18,20 +17,21 @@ use crate::{
|
||||
traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes},
|
||||
},
|
||||
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
|
||||
serialization::serialize,
|
||||
serialization::{Serialize, UpdateExt},
|
||||
};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::array::IntoIter;
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::Add;
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::{Digest, FixedOutput};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::{
|
||||
typenum::{Unsigned, U32},
|
||||
typenum::{Sum, Unsigned, U1, U2, U32},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use hkdf::Hkdf;
|
||||
use hkdf::{Hkdf, HkdfExtract};
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
@@ -56,55 +56,69 @@ static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
||||
pub struct TripleDH;
|
||||
|
||||
/// The client state produced after the first key exchange message
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct Ke1State<KG: KeGroup> {
|
||||
client_e_sk: PrivateKey<KG>,
|
||||
client_nonce: GenericArray<u8, NonceLen>,
|
||||
}
|
||||
|
||||
impl_clone_for!(
|
||||
struct Ke1State<KG: KeGroup>,
|
||||
[client_e_sk, client_nonce],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
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))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
|
||||
pub struct Ke1Message<KG: KeGroup> {
|
||||
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
|
||||
pub(crate) client_e_pk: PublicKey<KG>,
|
||||
}
|
||||
|
||||
/// The server state produced after the second key exchange message
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||
pub struct Ke2State<HashLen: ArrayLength<u8>> {
|
||||
km3: GenericArray<u8, HashLen>,
|
||||
hashed_transcript: GenericArray<u8, HashLen>,
|
||||
session_key: GenericArray<u8, HashLen>,
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct Ke2State<D: Hash> {
|
||||
km3: GenericArray<u8, D::OutputSize>,
|
||||
hashed_transcript: GenericArray<u8, D::OutputSize>,
|
||||
session_key: GenericArray<u8, D::OutputSize>,
|
||||
}
|
||||
|
||||
/// The second key exchange message
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||
pub struct Ke2Message<KG: KeGroup, HashLen: ArrayLength<u8>> {
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Ke2Message<D: Hash, KG: KeGroup> {
|
||||
server_nonce: GenericArray<u8, NonceLen>,
|
||||
server_e_pk: PublicKey<KG>,
|
||||
mac: GenericArray<u8, HashLen>,
|
||||
mac: GenericArray<u8, D::OutputSize>,
|
||||
}
|
||||
|
||||
/// The third key exchange message
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||
pub struct Ke3Message<HashLen: ArrayLength<u8>> {
|
||||
mac: GenericArray<u8, HashLen>,
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Ke3Message<D: Hash> {
|
||||
mac: GenericArray<u8, D::OutputSize>,
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -112,17 +126,33 @@ pub struct Ke3Message<HashLen: ArrayLength<u8>> {
|
||||
// ========================== //
|
||||
////////////////////////////////
|
||||
|
||||
impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
|
||||
impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH
|
||||
where
|
||||
// Ke1State: KeSk + Nonce
|
||||
KG::SkLen: Add<NonceLen>,
|
||||
Sum<KG::SkLen, NonceLen>: ArrayLength<u8>,
|
||||
// Ke1Message: Nonce + KePk
|
||||
NonceLen: Add<KG::PkLen>,
|
||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8>,
|
||||
// Ke2State: (Hash + Hash) + Hash
|
||||
D::OutputSize: Add<D::OutputSize>,
|
||||
Sum<D::OutputSize, D::OutputSize>: ArrayLength<u8> + Add<D::OutputSize>,
|
||||
Sum<Sum<D::OutputSize, D::OutputSize>, D::OutputSize>: ArrayLength<u8>,
|
||||
// Ke2Message: (Nonce + KePk) + Hash
|
||||
NonceLen: Add<KG::PkLen>,
|
||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8> + Add<D::OutputSize>,
|
||||
Sum<Sum<NonceLen, KG::PkLen>, D::OutputSize>: ArrayLength<u8>,
|
||||
{
|
||||
type KE1State = Ke1State<KG>;
|
||||
type KE2State = Ke2State<<D as FixedOutput>::OutputSize>;
|
||||
type KE2State = Ke2State<D>;
|
||||
type KE1Message = Ke1Message<KG>;
|
||||
type KE2Message = Ke2Message<KG, <D as FixedOutput>::OutputSize>;
|
||||
type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>;
|
||||
type KE2Message = Ke2Message<D, KG>;
|
||||
type KE3Message = Ke3Message<D>;
|
||||
|
||||
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
||||
let client_e_kp = KeyPair::<KG>::generate_random(rng)?;
|
||||
let client_e_kp = KeyPair::<KG>::generate_random(rng);
|
||||
let client_nonce = generate_nonce::<R>(rng);
|
||||
|
||||
let ke1_message = Ke1Message {
|
||||
@@ -140,29 +170,32 @@ impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn generate_ke2<R: RngCore + CryptoRng, S: SecretKey<KG>>(
|
||||
fn generate_ke2<'a, 'b, 'c, 'd, R: RngCore + CryptoRng, S: SecretKey<KG>>(
|
||||
rng: &mut R,
|
||||
serialized_credential_request: Vec<u8>,
|
||||
l2_bytes: Vec<u8>,
|
||||
serialized_credential_request: impl Iterator<Item = &'a [u8]>,
|
||||
l2_bytes: impl Iterator<Item = &'b [u8]>,
|
||||
ke1_message: Self::KE1Message,
|
||||
client_s_pk: PublicKey<KG>,
|
||||
server_s_sk: S,
|
||||
id_u: Vec<u8>,
|
||||
id_s: Vec<u8>,
|
||||
context: Vec<u8>,
|
||||
id_u: impl Iterator<Item = &'c [u8]>,
|
||||
id_s: impl Iterator<Item = &'d [u8]>,
|
||||
context: &[u8],
|
||||
) -> Result<GenerateKe2Result<Self, D, KG>, ProtocolError<S::Error>> {
|
||||
let server_e_kp =
|
||||
KeyPair::<KG>::generate_random(rng).map_err(|_| InternalError::InvalidKeypairError)?;
|
||||
let server_e_kp = KeyPair::<KG>::generate_random(rng);
|
||||
let server_nonce = generate_nonce::<R>(rng);
|
||||
|
||||
let mut transcript_hasher = D::new()
|
||||
.chain(STR_RFC)
|
||||
.chain(&serialize(&context, 2).map_err(ProtocolError::into_custom)?)
|
||||
.chain(&id_u)
|
||||
.chain(&serialized_credential_request[..])
|
||||
.chain(&id_s)
|
||||
.chain(&l2_bytes[..])
|
||||
.chain(&server_nonce[..])
|
||||
.chain_iter(
|
||||
Serialize::<U2>::from(context)
|
||||
.map_err(ProtocolError::into_custom)?
|
||||
.iter(),
|
||||
)
|
||||
.chain_iter(id_u.into_iter())
|
||||
.chain_iter(serialized_credential_request)
|
||||
.chain_iter(id_s.into_iter())
|
||||
.chain_iter(l2_bytes)
|
||||
.chain(server_nonce)
|
||||
.chain(&server_e_kp.public().to_arr());
|
||||
|
||||
let result = derive_3dh_keys::<D, KG, S>(
|
||||
@@ -203,25 +236,25 @@ impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn generate_ke3(
|
||||
l2_component: Vec<u8>,
|
||||
fn generate_ke3<'a, 'b, 'c, 'd>(
|
||||
l2_component: impl Iterator<Item = &'a [u8]>,
|
||||
ke2_message: Self::KE2Message,
|
||||
ke1_state: &Self::KE1State,
|
||||
serialized_credential_request: &[u8],
|
||||
serialized_credential_request: impl Iterator<Item = &'b [u8]>,
|
||||
server_s_pk: PublicKey<KG>,
|
||||
client_s_sk: PrivateKey<KG>,
|
||||
id_u: Vec<u8>,
|
||||
id_s: Vec<u8>,
|
||||
context: Vec<u8>,
|
||||
id_u: impl Iterator<Item = &'c [u8]>,
|
||||
id_s: impl Iterator<Item = &'d [u8]>,
|
||||
context: &[u8],
|
||||
) -> Result<GenerateKe3Result<Self, D, KG>, ProtocolError> {
|
||||
let mut transcript_hasher = D::new()
|
||||
.chain(STR_RFC)
|
||||
.chain(&serialize(&context, 2)?)
|
||||
.chain(&id_u)
|
||||
.chain(&serialized_credential_request)
|
||||
.chain(&id_s)
|
||||
.chain(&l2_component[..])
|
||||
.chain(&ke2_message.to_bytes_without_info_or_mac());
|
||||
.chain_iter(Serialize::<U2>::from(context)?.iter())
|
||||
.chain_iter(id_u)
|
||||
.chain_iter(serialized_credential_request)
|
||||
.chain_iter(id_s)
|
||||
.chain_iter(l2_component)
|
||||
.chain_iter(ke2_message.to_bytes_without_info_or_mac());
|
||||
|
||||
let result = derive_3dh_keys::<D, KG, PrivateKey<KG>>(
|
||||
TripleDHComponents {
|
||||
@@ -239,18 +272,18 @@ impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
|
||||
Hmac::<D>::new_from_slice(&result.1).map_err(|_| InternalError::HmacError)?;
|
||||
server_mac.update(&transcript_hasher.clone().finalize());
|
||||
|
||||
if server_mac.verify(&ke2_message.mac).is_err() {
|
||||
return Err(ProtocolError::InvalidLoginError);
|
||||
}
|
||||
server_mac
|
||||
.verify(&ke2_message.mac)
|
||||
.map_err(|_| ProtocolError::InvalidLoginError)?;
|
||||
|
||||
transcript_hasher.update(ke2_message.mac.to_vec());
|
||||
transcript_hasher.update(&ke2_message.mac);
|
||||
|
||||
let mut client_mac =
|
||||
Hmac::<D>::new_from_slice(&result.2).map_err(|_| InternalError::HmacError)?;
|
||||
client_mac.update(&transcript_hasher.finalize());
|
||||
|
||||
Ok((
|
||||
result.0.to_vec(),
|
||||
result.0,
|
||||
Ke3Message {
|
||||
mac: client_mac.finalize().into_bytes(),
|
||||
},
|
||||
@@ -265,16 +298,16 @@ impl<D: Hash, KG: KeGroup> KeyExchange<D, KG> for TripleDH {
|
||||
fn finish_ke(
|
||||
ke3_message: Self::KE3Message,
|
||||
ke2_state: &Self::KE2State,
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
) -> Result<GenericArray<u8, D::OutputSize>, ProtocolError> {
|
||||
let mut client_mac =
|
||||
Hmac::<D>::new_from_slice(&ke2_state.km3).map_err(|_| InternalError::HmacError)?;
|
||||
client_mac.update(&ke2_state.hashed_transcript);
|
||||
|
||||
if client_mac.verify(&ke3_message.mac).is_err() {
|
||||
return Err(ProtocolError::InvalidLoginError);
|
||||
}
|
||||
client_mac
|
||||
.verify(&ke3_message.mac)
|
||||
.map_err(|_| ProtocolError::InvalidLoginError)?;
|
||||
|
||||
Ok(ke2_state.session_key.to_vec())
|
||||
Ok(ke2_state.session_key.clone())
|
||||
}
|
||||
|
||||
fn ke2_message_size() -> usize {
|
||||
@@ -311,7 +344,7 @@ type TripleDHDerivationResult<D> = (
|
||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||
Vec<u8>,
|
||||
GenericArray<u8, <D as FixedOutput>::OutputSize>,
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
@@ -327,18 +360,21 @@ 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> = [
|
||||
let mut hkdf = HkdfExtract::<D>::new(None);
|
||||
|
||||
hkdf.input_ikm(
|
||||
&dh.sk1
|
||||
.diffie_hellman(dh.pk1)
|
||||
.map_err(InternalError::into_custom)?[..],
|
||||
&dh.sk2.diffie_hellman(dh.pk2)?[..],
|
||||
.map_err(InternalError::into_custom)?,
|
||||
);
|
||||
hkdf.input_ikm(&dh.sk2.diffie_hellman(dh.pk2)?);
|
||||
hkdf.input_ikm(
|
||||
&dh.sk3
|
||||
.diffie_hellman(dh.pk3)
|
||||
.map_err(InternalError::into_custom)?[..],
|
||||
]
|
||||
.concat();
|
||||
.map_err(InternalError::into_custom)?,
|
||||
);
|
||||
|
||||
let extracted_ikm = Hkdf::<D>::new(None, &ikm);
|
||||
let (_, extracted_ikm) = hkdf.finalize();
|
||||
let handshake_secret = derive_secrets::<D>(
|
||||
&extracted_ikm,
|
||||
STR_HANDSHAKE_SECRET,
|
||||
@@ -352,20 +388,10 @@ fn derive_3dh_keys<D: Hash, KG: KeGroup, S: SecretKey<KG>>(
|
||||
)
|
||||
.map_err(ProtocolError::into_custom)?;
|
||||
|
||||
let km2 = hkdf_expand_label::<D>(
|
||||
&handshake_secret,
|
||||
STR_SERVER_MAC,
|
||||
b"",
|
||||
<D as Digest>::OutputSize::USIZE,
|
||||
)
|
||||
.map_err(ProtocolError::into_custom)?;
|
||||
let km3 = hkdf_expand_label::<D>(
|
||||
&handshake_secret,
|
||||
STR_CLIENT_MAC,
|
||||
b"",
|
||||
<D as Digest>::OutputSize::USIZE,
|
||||
)
|
||||
.map_err(ProtocolError::into_custom)?;
|
||||
let km2 = hkdf_expand_label::<D>(&handshake_secret, STR_SERVER_MAC, b"")
|
||||
.map_err(ProtocolError::into_custom)?;
|
||||
let km3 = hkdf_expand_label::<D>(&handshake_secret, STR_CLIENT_MAC, b"")
|
||||
.map_err(ProtocolError::into_custom)?;
|
||||
|
||||
Ok((
|
||||
GenericArray::clone_from_slice(&session_key),
|
||||
@@ -380,33 +406,35 @@ fn hkdf_expand_label<D: Hash>(
|
||||
secret: &[u8],
|
||||
label: &[u8],
|
||||
context: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
) -> Result<GenericArray<u8, D::OutputSize>, ProtocolError> {
|
||||
let h = Hkdf::<D>::from_prk(secret).map_err(|_| InternalError::HkdfError)?;
|
||||
hkdf_expand_label_extracted(&h, label, context, length)
|
||||
hkdf_expand_label_extracted(&h, label, context)
|
||||
}
|
||||
|
||||
fn hkdf_expand_label_extracted<D: Hash>(
|
||||
hkdf: &Hkdf<D>,
|
||||
label: &[u8],
|
||||
context: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
let mut okm = vec![0u8; length];
|
||||
) -> Result<GenericArray<u8, D::OutputSize>, ProtocolError> {
|
||||
let mut okm = GenericArray::default();
|
||||
|
||||
let mut hkdf_label: Vec<u8> = Vec::new();
|
||||
let length_u16: u16 =
|
||||
u16::try_from(D::OutputSize::USIZE).map_err(|_| ProtocolError::SerializationError)?;
|
||||
let label = Serialize::<U1>::from_label(STR_OPAQUE, label)?;
|
||||
let label = label.to_array_3();
|
||||
let context = Serialize::<U1>::from(context)?;
|
||||
let context = context.to_array_2();
|
||||
|
||||
let length_u16: u16 = u16::try_from(length).map_err(|_| ProtocolError::SerializationError)?;
|
||||
hkdf_label.extend_from_slice(&length_u16.to_be_bytes());
|
||||
let hkdf_label = [
|
||||
&length_u16.to_be_bytes(),
|
||||
label[0],
|
||||
label[1],
|
||||
label[2],
|
||||
context[0],
|
||||
context[1],
|
||||
];
|
||||
|
||||
let mut opaque_label: Vec<u8> = Vec::new();
|
||||
opaque_label.extend_from_slice(STR_OPAQUE);
|
||||
opaque_label.extend_from_slice(label);
|
||||
hkdf_label.extend_from_slice(&serialize(&opaque_label, 1)?);
|
||||
|
||||
hkdf_label.extend_from_slice(&serialize(context, 1)?);
|
||||
|
||||
hkdf.expand(&hkdf_label, &mut okm)
|
||||
hkdf.expand_multi_info(&hkdf_label, &mut okm)
|
||||
.map_err(|_| InternalError::HkdfError)?;
|
||||
Ok(okm)
|
||||
}
|
||||
@@ -415,27 +443,22 @@ fn derive_secrets<D: Hash>(
|
||||
hkdf: &Hkdf<D>,
|
||||
label: &[u8],
|
||||
hashed_derivation_transcript: &[u8],
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
hkdf_expand_label_extracted::<D>(
|
||||
hkdf,
|
||||
label,
|
||||
hashed_derivation_transcript,
|
||||
<D as Digest>::OutputSize::USIZE,
|
||||
)
|
||||
) -> Result<GenericArray<u8, D::OutputSize>, ProtocolError> {
|
||||
hkdf_expand_label_extracted::<D>(hkdf, label, hashed_derivation_transcript)
|
||||
}
|
||||
|
||||
// Generate a random nonce up to NonceLen::USIZE bytes.
|
||||
fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
|
||||
let mut nonce_bytes = vec![0u8; NonceLen::USIZE];
|
||||
let mut nonce_bytes = GenericArray::default();
|
||||
rng.fill_bytes(&mut nonce_bytes);
|
||||
GenericArray::clone_from_slice(&nonce_bytes)
|
||||
nonce_bytes
|
||||
}
|
||||
|
||||
// Serialization and deserialization implementations
|
||||
|
||||
impl<KG: KeGroup> FromBytes for Ke1State<KG> {
|
||||
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let key_len = <KG as KeGroup>::PkLen::USIZE;
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let key_len = KG::SkLen::USIZE;
|
||||
|
||||
let nonce_len = NonceLen::USIZE;
|
||||
let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?;
|
||||
@@ -449,15 +472,21 @@ impl<KG: KeGroup> FromBytes for Ke1State<KG> {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
impl<KG: KeGroup> ToBytes for Ke1State<KG>
|
||||
where
|
||||
// Ke1State: KeSk + Nonce
|
||||
KG::SkLen: Add<NonceLen>,
|
||||
Sum<KG::SkLen, NonceLen>: ArrayLength<u8>,
|
||||
{
|
||||
type Len = Sum<KG::SkLen, NonceLen>;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.client_e_sk.to_arr().concat(self.client_nonce)
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> FromBytes for Ke1Message<KG> {
|
||||
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
fn from_bytes(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let nonce_len = NonceLen::USIZE;
|
||||
let checked_nonce = check_slice_size(
|
||||
ke1_message_bytes,
|
||||
@@ -472,15 +501,22 @@ impl<KG: KeGroup> FromBytes for Ke1Message<KG> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> ToBytes for Ke1Message<KG> {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
|
||||
impl<KG: KeGroup> ToBytes for Ke1Message<KG>
|
||||
where
|
||||
// Ke1Message: Nonce + KePk
|
||||
NonceLen: Add<KG::PkLen>,
|
||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8>,
|
||||
{
|
||||
type Len = Sum<NonceLen, KG::PkLen>;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.client_nonce.concat(self.client_e_pk.to_arr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
|
||||
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let hash_len = HashLen::USIZE;
|
||||
impl<D: Hash> FromBytes for Ke2State<D> {
|
||||
fn from_bytes(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let hash_len = D::OutputSize::USIZE;
|
||||
let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?;
|
||||
|
||||
Ok(Self {
|
||||
@@ -493,19 +529,25 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<HashLen: ArrayLength<u8>> ToBytes for Ke2State<HashLen> {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
[
|
||||
&self.km3[..],
|
||||
&self.hashed_transcript[..],
|
||||
&self.session_key[..],
|
||||
]
|
||||
.concat()
|
||||
impl<D: Hash> ToBytes for Ke2State<D>
|
||||
where
|
||||
// Ke2State: (Hash + Hash) + Hash
|
||||
D::OutputSize: Add<D::OutputSize>,
|
||||
Sum<D::OutputSize, D::OutputSize>: ArrayLength<u8> + Add<D::OutputSize>,
|
||||
Sum<Sum<D::OutputSize, D::OutputSize>, D::OutputSize>: ArrayLength<u8>,
|
||||
{
|
||||
type Len = Sum<Sum<D::OutputSize, D::OutputSize>, D::OutputSize>;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.km3
|
||||
.clone()
|
||||
.concat(self.hashed_transcript.clone())
|
||||
.concat(self.session_key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<KG, HashLen> {
|
||||
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
impl<KG: KeGroup, D: Hash> FromBytes for Ke2Message<D, KG> {
|
||||
fn from_bytes(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
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")?;
|
||||
@@ -517,12 +559,12 @@ impl<KG: KeGroup, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<KG, HashLen
|
||||
)?;
|
||||
let checked_mac = check_slice_size(
|
||||
&unchecked_server_e_pk[key_len..],
|
||||
HashLen::USIZE,
|
||||
D::OutputSize::USIZE,
|
||||
"ke1_message mac",
|
||||
)?;
|
||||
|
||||
// Check the public key bytes
|
||||
let server_e_pk = KeyPair::<CS::KeGroup>::check_public_key(PublicKey::from_bytes(
|
||||
let server_e_pk = KeyPair::<KG>::check_public_key(PublicKey::from_bytes(
|
||||
&unchecked_server_e_pk[..key_len],
|
||||
)?)?;
|
||||
|
||||
@@ -534,21 +576,33 @@ impl<KG: KeGroup, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<KG, 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<D: Hash, KG: KeGroup> ToBytes for Ke2Message<D, KG>
|
||||
where
|
||||
// Ke2Message: (Nonce + KePk) + Hash
|
||||
NonceLen: Add<KG::PkLen>,
|
||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8> + Add<D::OutputSize>,
|
||||
Sum<Sum<NonceLen, KG::PkLen>, D::OutputSize>: ArrayLength<u8>,
|
||||
{
|
||||
type Len = Sum<Sum<NonceLen, KG::PkLen>, D::OutputSize>;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.server_nonce
|
||||
.concat(self.server_e_pk.to_arr())
|
||||
.concat(self.mac.clone())
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
impl<D: Hash, KG: KeGroup> Ke2Message<D, KG> {
|
||||
fn to_bytes_without_info_or_mac(&self) -> impl Iterator<Item = &[u8]> {
|
||||
// MSRV: array `into_iter` isn't available in 1.51
|
||||
#[allow(deprecated)]
|
||||
IntoIter::new([self.server_nonce.as_slice(), self.server_e_pk.as_slice()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<HashLen: ArrayLength<u8>> FromBytes for Ke3Message<HashLen> {
|
||||
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let checked_bytes = check_slice_size(bytes, HashLen::USIZE, "ke3_message")?;
|
||||
impl<D: Hash> FromBytes for Ke3Message<D> {
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let checked_bytes = check_slice_size(bytes, D::OutputSize::USIZE, "ke3_message")?;
|
||||
|
||||
Ok(Self {
|
||||
mac: GenericArray::clone_from_slice(checked_bytes),
|
||||
@@ -556,39 +610,10 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke3Message<HashLen> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<HashLen: ArrayLength<u8>> ToBytes for Ke3Message<HashLen> {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.mac.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
// Zeroize on drop implementations
|
||||
|
||||
// This can't be derived because of the use of a generic parameter
|
||||
impl<KG: KeGroup> Zeroize for Ke1State<KG> {
|
||||
fn zeroize(&mut self) {
|
||||
self.client_e_sk.zeroize();
|
||||
self.client_nonce.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> Drop for Ke1State<KG> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<HashLen: ArrayLength<u8>> Zeroize for Ke2State<HashLen> {
|
||||
fn zeroize(&mut self) {
|
||||
self.km3.zeroize();
|
||||
self.hashed_transcript.zeroize();
|
||||
self.session_key.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<HashLen: ArrayLength<u8>> Drop for Ke2State<HashLen> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
impl<D: Hash> ToBytes for Ke3Message<D> {
|
||||
type Len = D::OutputSize;
|
||||
|
||||
fn to_bytes(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.mac.clone()
|
||||
}
|
||||
}
|
||||
|
||||
+148
-220
@@ -11,9 +11,8 @@
|
||||
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
use core::ops::Deref;
|
||||
use derive_where::DeriveWhere;
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
@@ -21,65 +20,24 @@ use zeroize::Zeroize;
|
||||
|
||||
/// A Keypair trait with public-private verification
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound(
|
||||
deserialize = "S: serde::Deserialize<'de>",
|
||||
serialize = "S: serde::Serialize"
|
||||
))
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(
|
||||
bound(
|
||||
deserialize = "S: serde_::Deserialize<'de>",
|
||||
serialize = "S: serde_::Serialize"
|
||||
),
|
||||
crate = "serde_"
|
||||
)
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Zeroize(drop))]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; S)]
|
||||
pub struct KeyPair<KG: KeGroup, S: SecretKey<KG> = PrivateKey<KG>> {
|
||||
pk: PublicKey<KG>,
|
||||
sk: S,
|
||||
}
|
||||
|
||||
impl<KG: KeGroup, S: SecretKey<KG>> Clone for KeyPair<KG, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
pk: self.pk.clone(),
|
||||
sk: self.sk.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.field("sk", &self.sk)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
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<KG: KeGroup, S: SecretKey<KG> + Eq> Eq for KeyPair<KG, 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);
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a generic parameter
|
||||
impl<KG: KeGroup, S: SecretKey<KG>> Zeroize for KeyPair<KG, S> {
|
||||
fn zeroize(&mut self) {
|
||||
self.pk.zeroize();
|
||||
self.sk.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup, S: SecretKey<KG>> Drop for KeyPair<KG, S> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
|
||||
/// The public key component
|
||||
pub fn public(&self) -> &PublicKey<KG> {
|
||||
@@ -113,20 +71,18 @@ impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
|
||||
|
||||
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,
|
||||
) -> Result<Self, InternalError> {
|
||||
pub(crate) fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let sk = KG::random_sk(rng);
|
||||
let pk = KG::public_key(&sk);
|
||||
Ok(Self {
|
||||
Self {
|
||||
pk: PublicKey(Key(pk.to_arr())),
|
||||
sk: PrivateKey(Key(sk)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<KG: KeGroup + Debug> KeyPair<KG> {
|
||||
impl<KG: KeGroup> KeyPair<KG> {
|
||||
/// Test-only strategy returning a proptest Strategy based on
|
||||
/// generate_random
|
||||
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
|
||||
@@ -138,7 +94,7 @@ impl<KG: KeGroup + Debug> KeyPair<KG> {
|
||||
any::<[u8; 32]>()
|
||||
.prop_filter_map("valid random keypair", |seed| {
|
||||
let mut rng = StdRng::from_seed(seed);
|
||||
Some(Self::generate_random(&mut rng).unwrap())
|
||||
Some(Self::generate_random(&mut rng))
|
||||
})
|
||||
.no_shrink()
|
||||
.boxed()
|
||||
@@ -147,52 +103,14 @@ impl<KG: KeGroup + Debug> KeyPair<KG> {
|
||||
|
||||
/// A minimalist key type built around a \[u8; 32\]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct Key<L: ArrayLength<u8>>(GenericArray<u8, L>);
|
||||
|
||||
impl<L: ArrayLength<u8>> Clone for Key<L> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ArrayLength<u8>> Debug for Key<L> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_tuple("Key").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ArrayLength<u8>> Eq for Key<L> {}
|
||||
|
||||
impl<L: ArrayLength<u8>> PartialEq for Key<L> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ArrayLength<u8>> core::hash::Hash for Key<L> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a generic parameter
|
||||
impl<L: ArrayLength<u8>> Zeroize for Key<L> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ArrayLength<u8>> Drop for Key<L> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ArrayLength<u8>> Deref for Key<L> {
|
||||
type Target = GenericArray<u8, L>;
|
||||
|
||||
@@ -210,32 +128,16 @@ 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)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct PrivateKey<KG: KeGroup>(Key<KG::SkLen>);
|
||||
|
||||
impl_clone_for!(
|
||||
tuple PrivateKey<KG: KeGroup>,
|
||||
[0],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
tuple PrivateKey<KG: KeGroup>,
|
||||
[0],
|
||||
);
|
||||
|
||||
// This can't be derived because of the use of a generic parameter
|
||||
impl<KG: KeGroup> Zeroize for PrivateKey<KG> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> Drop for PrivateKey<KG> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> Deref for PrivateKey<KG> {
|
||||
type Target = Key<KG::SkLen>;
|
||||
|
||||
@@ -264,15 +166,20 @@ impl<KG: KeGroup> PrivateKey<KG> {
|
||||
pub trait SecretKey<KG: KeGroup>: Clone + Sized + Zeroize {
|
||||
/// Custom error type that can be passed down to `InternalError::Custom`
|
||||
type Error;
|
||||
/// Serialization size in bytes.
|
||||
type Len: ArrayLength<u8>;
|
||||
|
||||
/// Diffie-Hellman key exchange implementation
|
||||
fn diffie_hellman(&self, pk: PublicKey<KG>) -> Result<Vec<u8>, InternalError<Self::Error>>;
|
||||
fn diffie_hellman(
|
||||
&self,
|
||||
pk: PublicKey<KG>,
|
||||
) -> Result<GenericArray<u8, KG::PkLen>, InternalError<Self::Error>>;
|
||||
|
||||
/// Returns public key from private key
|
||||
fn public_key(&self) -> Result<PublicKey<KG>, InternalError<Self::Error>>;
|
||||
|
||||
/// Serialization into bytes
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
fn serialize(&self) -> GenericArray<u8, Self::Len>;
|
||||
|
||||
/// Deserialization from bytes
|
||||
fn deserialize(input: &[u8]) -> Result<Self, InternalError<Self::Error>>;
|
||||
@@ -280,18 +187,22 @@ pub trait SecretKey<KG: KeGroup>: Clone + Sized + Zeroize {
|
||||
|
||||
impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
|
||||
type Error = core::convert::Infallible;
|
||||
type Len = KG::SkLen;
|
||||
|
||||
fn diffie_hellman(&self, pk: PublicKey<KG>) -> Result<Vec<u8>, InternalError> {
|
||||
fn diffie_hellman(
|
||||
&self,
|
||||
pk: PublicKey<KG>,
|
||||
) -> Result<GenericArray<u8, KG::PkLen>, InternalError> {
|
||||
let pk = KG::from_pk_slice(&pk)?;
|
||||
Ok(pk.diffie_hellman(self).to_vec())
|
||||
Ok(pk.diffie_hellman(self))
|
||||
}
|
||||
|
||||
fn public_key(&self) -> Result<PublicKey<KG>, InternalError> {
|
||||
Ok(PublicKey(Key(KG::public_key(&self.0).to_arr())))
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.to_vec()
|
||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.to_arr()
|
||||
}
|
||||
|
||||
fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
@@ -300,32 +211,15 @@ impl<KG: KeGroup> SecretKey<KG> for PrivateKey<KG> {
|
||||
}
|
||||
|
||||
/// Wrapper around a Key to enforce that it's a public one.
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[repr(transparent)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_::Deserialize, serde_::Serialize),
|
||||
serde(bound = "", crate = "serde_")
|
||||
)]
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct PublicKey<KG: KeGroup>(Key<KG::PkLen>);
|
||||
|
||||
impl_clone_for!(
|
||||
tuple PublicKey<KG: KeGroup>,
|
||||
[0],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
tuple PublicKey<KG: KeGroup>,
|
||||
[0],
|
||||
);
|
||||
|
||||
// This can't be derived because of the use of a generic parameter
|
||||
impl<KG: KeGroup> Zeroize for PublicKey<KG> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> Drop for PublicKey<KG> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<KG: KeGroup> Deref for PublicKey<KG> {
|
||||
type Target = Key<KG::PkLen>;
|
||||
|
||||
@@ -355,85 +249,109 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::errors::*;
|
||||
use core::slice::from_raw_parts;
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use generic_array::typenum::Unsigned;
|
||||
use proptest::prelude::*;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
#[test]
|
||||
fn test_zeroize_key() -> Result<(), ProtocolError> {
|
||||
let key_len = <RistrettoPoint as KeGroup>::PkLen::USIZE;
|
||||
let mut key = Key::<<RistrettoPoint as KeGroup>::PkLen>(GenericArray::clone_from_slice(
|
||||
&alloc::vec![
|
||||
fn inner<G: KeGroup>() -> Result<(), ProtocolError> {
|
||||
let key_len = G::PkLen::USIZE;
|
||||
let mut key = Key::<G::PkLen>(GenericArray::clone_from_slice(&alloc::vec![
|
||||
1u8;
|
||||
key_len
|
||||
],
|
||||
));
|
||||
let ptr = key.as_ptr();
|
||||
]));
|
||||
let ptr = key.as_ptr();
|
||||
|
||||
Zeroize::zeroize(&mut key);
|
||||
Zeroize::zeroize(&mut key);
|
||||
|
||||
let bytes = unsafe { from_raw_parts(ptr, key_len) };
|
||||
assert!(bytes.iter().all(|&x| x == 0));
|
||||
let bytes = unsafe { from_raw_parts(ptr, key_len) };
|
||||
assert!(bytes.iter().all(|&x| x == 0));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<curve25519_dalek::ristretto::RistrettoPoint>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<p256_::PublicKey>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zeroize_keypair() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
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;
|
||||
fn test_zeroize_keypair() {
|
||||
fn inner<G: KeGroup>() {
|
||||
let mut rng = OsRng;
|
||||
let mut keypair = KeyPair::<G>::generate_random(&mut rng);
|
||||
let pk_ptr = keypair.pk.as_ptr();
|
||||
let sk_ptr = keypair.sk.as_ptr();
|
||||
let pk_len = G::PkLen::USIZE;
|
||||
let sk_len = G::SkLen::USIZE;
|
||||
|
||||
Zeroize::zeroize(&mut keypair);
|
||||
Zeroize::zeroize(&mut keypair);
|
||||
|
||||
let pk_bytes = unsafe { from_raw_parts(pk_ptr, pk_len) };
|
||||
let sk_bytes = unsafe { from_raw_parts(sk_ptr, sk_len) };
|
||||
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));
|
||||
assert!(pk_bytes.iter().all(|&x| x == 0));
|
||||
assert!(sk_bytes.iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<curve25519_dalek::ristretto::RistrettoPoint>();
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<p256_::PublicKey>();
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_ristretto_check(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
|
||||
let pk = kp.public();
|
||||
prop_assert!(KeyPair::<RistrettoPoint>::check_public_key(pk.clone()).is_ok());
|
||||
}
|
||||
macro_rules! test {
|
||||
($mod:ident, $point:ty) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_ristretto_pub_from_priv(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
|
||||
let pk = kp.public();
|
||||
let sk = kp.private();
|
||||
prop_assert_eq!(&sk.public_key()?, pk);
|
||||
}
|
||||
proptest! {
|
||||
#[test]
|
||||
fn check(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
let pk = kp.public();
|
||||
prop_assert!(KeyPair::<$point>::check_public_key(pk.clone()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ristretto_dh(kp1 in KeyPair::<RistrettoPoint>::uniform_keypair_strategy(),
|
||||
kp2 in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
|
||||
#[test]
|
||||
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
let pk = kp.public();
|
||||
let sk = kp.private();
|
||||
prop_assert_eq!(&sk.public_key()?, pk);
|
||||
}
|
||||
|
||||
let dh1 = kp2.private().diffie_hellman(kp1.public().clone())?;
|
||||
let dh2 = kp1.private().diffie_hellman(kp2.public().clone())?;
|
||||
#[test]
|
||||
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy(),
|
||||
kp2 in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
|
||||
prop_assert_eq!(dh1, dh2);
|
||||
}
|
||||
let dh1 = kp2.private().diffie_hellman(kp1.public().clone())?;
|
||||
let dh2 = kp1.private().diffie_hellman(kp2.public().clone())?;
|
||||
|
||||
#[test]
|
||||
fn test_private_key_slice(kp in KeyPair::<RistrettoPoint>::uniform_keypair_strategy()) {
|
||||
let sk_bytes = kp.private().to_vec();
|
||||
prop_assert_eq!(dh1, dh2);
|
||||
}
|
||||
|
||||
let kp2 = KeyPair::<RistrettoPoint>::from_private_key_slice(&sk_bytes)?;
|
||||
let kp2_private_bytes = kp2.private().to_vec();
|
||||
#[test]
|
||||
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
let sk_bytes = kp.private().to_vec();
|
||||
|
||||
prop_assert_eq!(sk_bytes, kp2_private_bytes);
|
||||
}
|
||||
let kp2 = KeyPair::<$point>::from_private_key_slice(&sk_bytes)?;
|
||||
let kp2_private_bytes = kp2.private().to_vec();
|
||||
|
||||
prop_assert_eq!(sk_bytes, kp2_private_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
test!(ristretto, curve25519_dalek::ristretto::RistrettoPoint);
|
||||
#[cfg(feature = "p256")]
|
||||
test!(p256, p256_::PublicKey);
|
||||
|
||||
#[test]
|
||||
fn remote_key() {
|
||||
use crate::{
|
||||
@@ -443,37 +361,48 @@ mod tests {
|
||||
ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
||||
ServerRegistrationStartResult, ServerSetup,
|
||||
};
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
use curve25519_dalek::ristretto::RistrettoPoint as KeCurve;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
use p256_::PublicKey as KeCurve;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
struct Default;
|
||||
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = RistrettoPoint;
|
||||
type KeGroup = RistrettoPoint;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type OprfGroup = KeCurve;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = KeCurve;
|
||||
type KeyExchange = crate::key_exchange::tripledh::TripleDH;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
type Hash = sha2::Sha512;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
#[derive(Clone, Zeroize)]
|
||||
struct RemoteKey(PrivateKey<RistrettoPoint>);
|
||||
struct RemoteKey(PrivateKey<KeCurve>);
|
||||
|
||||
impl SecretKey<RistrettoPoint> for RemoteKey {
|
||||
impl SecretKey<KeCurve> for RemoteKey {
|
||||
type Error = core::convert::Infallible;
|
||||
type Len = <KeCurve as KeGroup>::SkLen;
|
||||
|
||||
fn diffie_hellman(
|
||||
&self,
|
||||
pk: PublicKey<RistrettoPoint>,
|
||||
) -> Result<Vec<u8>, InternalError<Self::Error>> {
|
||||
pk: PublicKey<KeCurve>,
|
||||
) -> Result<GenericArray<u8, <KeCurve as KeGroup>::PkLen>, InternalError<Self::Error>>
|
||||
{
|
||||
self.0.diffie_hellman(pk)
|
||||
}
|
||||
|
||||
fn public_key(&self) -> Result<PublicKey<RistrettoPoint>, InternalError<Self::Error>> {
|
||||
fn public_key(&self) -> Result<PublicKey<KeCurve>, InternalError<Self::Error>> {
|
||||
self.0.public_key()
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||
self.0.serialize()
|
||||
}
|
||||
|
||||
@@ -484,12 +413,11 @@ mod tests {
|
||||
|
||||
const PASSWORD: &str = "password";
|
||||
|
||||
let sk = RistrettoPoint::random_sk(&mut OsRng);
|
||||
let sk = KeCurve::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).unwrap();
|
||||
let server_setup = ServerSetup::<Default, RemoteKey>::new_with_key(&mut OsRng, keypair);
|
||||
|
||||
let ClientRegistrationStartResult {
|
||||
message,
|
||||
|
||||
+220
-73
@@ -26,7 +26,7 @@
|
||||
//! * a slow hashing function.
|
||||
//!
|
||||
//! We will use the following choices in this example:
|
||||
//! ```
|
||||
//! ```ignore
|
||||
//! use opaque_ke::CipherSuite;
|
||||
//! struct Default;
|
||||
//! impl CipherSuite for Default {
|
||||
@@ -51,6 +51,7 @@
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # use opaque_ke::ServerSetup;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -58,6 +59,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! let mut rng = OsRng;
|
||||
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
@@ -84,6 +93,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -91,6 +101,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use opaque_ke::ClientRegistration;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! let mut client_rng = OsRng;
|
||||
@@ -115,6 +133,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -122,6 +141,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -130,7 +157,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,
|
||||
@@ -153,6 +180,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -160,6 +188,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -167,7 +203,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,
|
||||
@@ -192,6 +228,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -199,6 +236,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -206,7 +251,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(
|
||||
@@ -235,6 +280,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -242,6 +288,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! use opaque_ke::ClientLogin;
|
||||
//! let mut client_rng = OsRng;
|
||||
@@ -269,6 +323,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -276,6 +331,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -283,16 +346,16 @@
|
||||
//! # 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",
|
||||
//! # )?;
|
||||
//! use opaque_ke::{ServerLogin, ServerLoginStartParameters};
|
||||
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
|
||||
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server_login_start_result = ServerLogin::start(
|
||||
//! &mut server_rng,
|
||||
@@ -323,6 +386,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -330,6 +394,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -337,17 +409,17 @@
|
||||
//! # 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",
|
||||
//! # )?;
|
||||
//! # let password_file =
|
||||
//! # ServerRegistration::<Default>::deserialize(
|
||||
//! # &password_file_bytes[..],
|
||||
//! # &password_file_bytes,
|
||||
//! # )?;
|
||||
//! # let server_login_start_result =
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
||||
@@ -369,6 +441,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -376,6 +449,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -383,17 +464,17 @@
|
||||
//! # 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",
|
||||
//! # )?;
|
||||
//! # let password_file =
|
||||
//! # ServerRegistration::<Default>::deserialize(
|
||||
//! # &password_file_bytes[..],
|
||||
//! # &password_file_bytes,
|
||||
//! # )?;
|
||||
//! # let server_login_start_result =
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
||||
@@ -445,6 +526,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -452,6 +534,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -459,7 +549,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
|
||||
@@ -468,14 +558,14 @@
|
||||
//! 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",
|
||||
//! # )?;
|
||||
//! # let password_file =
|
||||
//! # ServerRegistration::<Default>::deserialize(
|
||||
//! # &password_file_bytes[..],
|
||||
//! # &password_file_bytes,
|
||||
//! # )?;
|
||||
//! # let server_login_start_result =
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
||||
@@ -523,6 +613,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -530,6 +621,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -537,7 +636,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(
|
||||
@@ -545,14 +644,14 @@
|
||||
//! 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",
|
||||
//! # )?;
|
||||
//! # let password_file =
|
||||
//! # ServerRegistration::<Default>::deserialize(
|
||||
//! # &password_file_bytes[..],
|
||||
//! # &password_file_bytes,
|
||||
//! # )?;
|
||||
//! # let server_login_start_result =
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
||||
@@ -589,6 +688,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -596,6 +696,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -603,23 +711,23 @@
|
||||
//! # 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(),
|
||||
//! )),
|
||||
//! Identifiers {
|
||||
//! client: Some(b"Alice_the_Cryptographer"),
|
||||
//! server: Some(b"Facebook"),
|
||||
//! },
|
||||
//! None,
|
||||
//! ),
|
||||
//! )?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! The same identifiers must also be supplied using [ServerLoginStartParameters::WithIdentifiers] in [Server Login Start](#server-login-start):
|
||||
//! The same identifiers must also be supplied using [ServerLoginStartParameters] in [Server Login Start](#server-login-start):
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -628,6 +736,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -635,6 +744,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -642,16 +759,16 @@
|
||||
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
|
||||
//! # 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",
|
||||
//! # )?;
|
||||
//! # use opaque_ke::{ServerLogin, ServerLoginStartParameters};
|
||||
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
|
||||
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let server_login_start_result = ServerLogin::start(
|
||||
//! &mut server_rng,
|
||||
@@ -659,12 +776,13 @@
|
||||
//! Some(password_file),
|
||||
//! client_login_start_result.message,
|
||||
//! b"[email protected]",
|
||||
//! ServerLoginStartParameters::WithIdentifiers(
|
||||
//! Identifiers::ClientAndServerIdentifiers(
|
||||
//! b"Alice_the_Cryptographer".to_vec(),
|
||||
//! b"Facebook".to_vec(),
|
||||
//! ),
|
||||
//! ),
|
||||
//! ServerLoginStartParameters {
|
||||
//! context: None,
|
||||
//! identifiers: Identifiers {
|
||||
//! client: Some(b"Alice_the_Cryptographer"),
|
||||
//! server: Some(b"Facebook"),
|
||||
//! },
|
||||
//! },
|
||||
//! )?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
@@ -678,6 +796,7 @@
|
||||
//! # };
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -685,6 +804,14 @@
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
@@ -692,28 +819,28 @@
|
||||
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
|
||||
//! # 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",
|
||||
//! # )?;
|
||||
//! # let password_file =
|
||||
//! # ServerRegistration::<Default>::deserialize(
|
||||
//! # &password_file_bytes[..],
|
||||
//! # &password_file_bytes,
|
||||
//! # )?;
|
||||
//! # let server_login_start_result =
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?;
|
||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
|
||||
//! let client_login_finish_result = client_login_start_result.state.finish(
|
||||
//! server_login_start_result.message,
|
||||
//! ClientLoginFinishParameters::new(
|
||||
//! None,
|
||||
//! Some(Identifiers::ClientAndServerIdentifiers(
|
||||
//! b"Alice_the_Cryptographer".to_vec(),
|
||||
//! b"Facebook".to_vec(),
|
||||
//! )),
|
||||
//! Identifiers {
|
||||
//! client: Some(b"Alice_the_Cryptographer"),
|
||||
//! server: Some(b"Facebook"),
|
||||
//! },
|
||||
//! None,
|
||||
//! ),
|
||||
//! )?;
|
||||
@@ -723,7 +850,7 @@
|
||||
//! Failing to supply the same pair of custom identifiers in any of the three steps above will result in an error in attempting to complete
|
||||
//! the protocol!
|
||||
//!
|
||||
//! Note that if only one of the client and server identifiers are present, then [Identifiers::ClientIdentifier] and [Identifiers::ServerIdentifier] can be
|
||||
//! Note that if only one of the client and server identifiers are present, then [Identifiers] can be
|
||||
//! used to specify them individually.
|
||||
//!
|
||||
//! ## Key Exchange Context
|
||||
@@ -731,7 +858,7 @@
|
||||
//! A key exchange protocol typically allows for the specifying of shared "context" information between the two parties before the exchange is complete,
|
||||
//! so as to bind the integrity of application-specific data or configuration parameters to the security of the key exchange.
|
||||
//! During the login phase, the client and server can specify this context using:
|
||||
//! - The second login message, where the server can populate [ServerLoginStartParameters::WithContext], and
|
||||
//! - The second login message, where the server can populate [ServerLoginStartParameters], and
|
||||
//! - The third login message, where the client can populate [ClientLoginFinishParameters].
|
||||
//!
|
||||
//! For both of these messages, the `WithContextAndIdentifiers` variant can be used to specify these fields in addition to
|
||||
@@ -751,51 +878,60 @@
|
||||
//! [`SecretKey`](keypair::SecretKey`) trait. This allows [`ServerSetup`] to be constructed using an existing keypair
|
||||
//! without exposing the bytes of the private key to this library.
|
||||
//! ```
|
||||
//! # use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # use generic_array::{GenericArray, typenum::U32};
|
||||
//! # use opaque_ke::{CipherSuite, errors::{InternalError}, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup};
|
||||
//! # use generic_array::{GenericArray, typenum::U0};
|
||||
//! # use opaque_ke::{CipherSuite, errors::{InternalError}, key_exchange::group::KeGroup, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup};
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use zeroize::Zeroize;
|
||||
//! # struct Default;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = RistrettoPoint;
|
||||
//! # 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(not(feature = "ristretto255"))]
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type OprfGroup = p256_::ProjectivePoint;
|
||||
//! # type KeGroup = p256_::PublicKey;
|
||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! # #[derive(Debug)]
|
||||
//! # struct YourRemoteKeyError;
|
||||
//! # #[derive(Clone, Zeroize)]
|
||||
//! # struct YourRemoteKey(PrivateKey<RistrettoPoint>);
|
||||
//! # struct YourRemoteKey(PrivateKey<<Default as CipherSuite>::KeGroup>);
|
||||
//! # impl YourRemoteKey {
|
||||
//! # fn diffie_hellman(&self, pk: &[u8]) -> Result<Vec<u8>, YourRemoteKeyError> { todo!() }
|
||||
//! # fn public_key(&self) -> Result<GenericArray<u8, U32>, YourRemoteKeyError> { Ok(GenericArray::default()) }
|
||||
//! # fn diffie_hellman(&self, pk: &[u8]) -> Result<GenericArray<u8, <<Default as CipherSuite>::KeGroup as KeGroup>::PkLen>, YourRemoteKeyError> { todo!() }
|
||||
//! # fn public_key(&self) -> Result<GenericArray<u8, <<Default as CipherSuite>::KeGroup as KeGroup>::PkLen>, YourRemoteKeyError> { Ok(GenericArray::default()) }
|
||||
//! # }
|
||||
//! impl SecretKey<RistrettoPoint> for YourRemoteKey {
|
||||
//! impl SecretKey<<Default as CipherSuite>::KeGroup> for YourRemoteKey {
|
||||
//! type Error = YourRemoteKeyError;
|
||||
//! type Len = U0;
|
||||
//!
|
||||
//! fn diffie_hellman(
|
||||
//! &self,
|
||||
//! pk: PublicKey<RistrettoPoint>,
|
||||
//! ) -> Result<Vec<u8>, InternalError<Self::Error>> {
|
||||
//! pk: PublicKey<<Default as CipherSuite>::KeGroup>,
|
||||
//! ) -> Result<GenericArray<u8, <<Default as CipherSuite>::KeGroup as KeGroup>::PkLen>, InternalError<Self::Error>> {
|
||||
//! YourRemoteKey::diffie_hellman(self, &pk.to_arr()).map_err(InternalError::Custom)
|
||||
//! }
|
||||
//!
|
||||
//! fn public_key(
|
||||
//! &self
|
||||
//! ) -> Result<PublicKey<RistrettoPoint>, InternalError<Self::Error>> {
|
||||
//! ) -> Result<PublicKey<<Default as CipherSuite>::KeGroup>, InternalError<Self::Error>> {
|
||||
//! YourRemoteKey::public_key(self).map(PublicKey::from_arr)
|
||||
//! .map_err(InternalError::Custom)
|
||||
//! }
|
||||
//!
|
||||
//! fn serialize(&self) -> Vec<u8> {
|
||||
//! // if you use serde and the "serialize" crate feature, you won't need this
|
||||
//! fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||
//! // if you use Serde and the "serde" crate feature, you won't need this
|
||||
//! todo!()
|
||||
//! }
|
||||
//!
|
||||
//! fn deserialize(input: &[u8]) -> Result<Self, InternalError<Self::Error>> {
|
||||
//! // if you use serde and the "serialize" crate feature, you won't need this
|
||||
//! // if you use Serde and the "serde" crate feature, you won't need this
|
||||
//! todo!()
|
||||
//! }
|
||||
//! }
|
||||
@@ -814,28 +950,34 @@
|
||||
//! password file records will be against offline dictionary and precomputation attacks; see
|
||||
//! [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for more details.
|
||||
//!
|
||||
//! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! [serde](https://serde.rs/).
|
||||
//!
|
||||
//! - The `u32_backend` and `u64_backend` features are re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting
|
||||
//! the corresponding backend for the curve arithmetic used. The `u64_backend` feature is included as the default.
|
||||
//! - The backend features are re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting
|
||||
//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default.
|
||||
//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. Any `ristretto255_*`
|
||||
//! backend feature will enable the `ristretto255` feature, which can be used too, but keep in mind that `curve25519-dalek`
|
||||
//! will fail to compile without a selected backend. This enabled to use `curve25519_dalek::ristretto::RistrettoPoint` as a
|
||||
//! `KeGroup` and `OprfGroup`.
|
||||
//!
|
||||
//! - The `p256` feature enables the use of `p256::ProjectivePoint` as a `Group` for `CipherSuite`. Note that this
|
||||
//! is currently an experimental feature ⚠️, and is not yet ready for production use.
|
||||
//! - The `x25519` feature is similar to the `ristretto255` feature and requires to select a backend like `x25519_u64`, other
|
||||
//! backends are the same as in `ristretto255_*`. This enables `x25519_dalek::PublicKey` as a `KeGroup`.
|
||||
//!
|
||||
//! - The `ristretto255_simd` feature is re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas,
|
||||
//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly.
|
||||
//!
|
||||
//! - The `p256` feature enables the use of `p256::PublicKey` as a `KeGroup` and `p256::ProjectivePoint` as a `OprfGroup` for
|
||||
//! `CipherSuite`. Note that this is currently an experimental feature ⚠️, and is not yet ready for production use.
|
||||
//!
|
||||
//! - The `bench` feature is used only for running performance benchmarks for this implementation.
|
||||
//!
|
||||
|
||||
#![cfg_attr(not(feature = "bench"), deny(missing_docs))]
|
||||
#![deny(unsafe_code)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(not(any(feature = "u64_backend", feature = "u32_backend",)))]
|
||||
compile_error!(
|
||||
"no dalek arithmetic backend cargo feature enabled! \
|
||||
please enable one of: u64_backend, u32_backend"
|
||||
);
|
||||
#![warn(clippy::cargo, missing_docs)]
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@@ -868,6 +1010,11 @@ pub use crate::messages::{
|
||||
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
||||
RegistrationResponse, RegistrationUpload,
|
||||
};
|
||||
pub use crate::messages::{
|
||||
CredentialFinalizationLen, CredentialRequestLen, CredentialResponseLen, RegistrationRequestLen,
|
||||
RegistrationResponseLen, RegistrationUploadLen,
|
||||
};
|
||||
pub use crate::opaque::ServerRegistrationLen;
|
||||
pub use crate::opaque::{
|
||||
ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup,
|
||||
};
|
||||
|
||||
Regular → Executable
+219
-128
@@ -9,21 +9,28 @@
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::Envelope,
|
||||
envelope::{Envelope, EnvelopeLen},
|
||||
errors::{
|
||||
utils::{check_slice_size, check_slice_size_atleast},
|
||||
ProtocolError,
|
||||
},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{FromBytes, KeyExchange, ToBytes},
|
||||
traits::{FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes},
|
||||
tripledh::NonceLen,
|
||||
},
|
||||
keypair::{KeyPair, PublicKey, SecretKey},
|
||||
opaque::ServerSetup,
|
||||
opaque::{MaskedResponse, MaskedResponseLen, ServerSetup},
|
||||
};
|
||||
use core::array::IntoIter;
|
||||
use core::ops::Add;
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::{Digest, FixedOutput};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::{
|
||||
typenum::{Sum, Unsigned},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use digest::Digest;
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use voprf::group::Group;
|
||||
|
||||
@@ -33,13 +40,21 @@ use voprf::group::Group;
|
||||
////////////////////////////
|
||||
|
||||
/// The message sent by the client to the server, to initiate registration
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; CS::OprfGroup)]
|
||||
pub struct RegistrationRequest<CS: CipherSuite> {
|
||||
/// blinded password information
|
||||
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(RegistrationRequest);
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// registration attempt
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; CS::OprfGroup)]
|
||||
pub struct RegistrationResponse<CS: CipherSuite> {
|
||||
/// The server's oprf output
|
||||
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
|
||||
@@ -47,8 +62,18 @@ pub struct RegistrationResponse<CS: CipherSuite> {
|
||||
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(
|
||||
RegistrationResponse
|
||||
where
|
||||
// RegistrationResponse: KgPk + KePk
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The final message from the client, containing sealed cryptographic
|
||||
/// identifiers
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))]
|
||||
pub struct RegistrationUpload<CS: CipherSuite> {
|
||||
/// The "envelope" generated by the user, containing sealed
|
||||
/// cryptographic identifiers
|
||||
@@ -59,33 +84,98 @@ pub struct RegistrationUpload<CS: CipherSuite> {
|
||||
pub(crate) client_s_pk: PublicKey<CS::KeGroup>,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(
|
||||
RegistrationUpload
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> | Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The message sent by the user to the server, to initiate registration
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone, Zeroize)]
|
||||
#[derive_where(
|
||||
Debug, Eq, Hash, PartialEq;
|
||||
CS::OprfGroup,
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
|
||||
)]
|
||||
pub struct CredentialRequest<CS: CipherSuite> {
|
||||
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
|
||||
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(
|
||||
CredentialRequest
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// login attempt
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(
|
||||
Debug, Eq, Hash, PartialEq;
|
||||
CS::OprfGroup,
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
||||
)]
|
||||
pub struct CredentialResponse<CS: CipherSuite> {
|
||||
/// the server's oprf output
|
||||
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
|
||||
pub(crate) masking_nonce: Vec<u8>,
|
||||
pub(crate) masked_response: Vec<u8>,
|
||||
pub(crate) masking_nonce: GenericArray<u8, NonceLen>,
|
||||
pub(crate) masked_response: MaskedResponse<CS>,
|
||||
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(
|
||||
CredentialResponse
|
||||
where
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>:
|
||||
ArrayLength<u8> | Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> | Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The answer sent by the client to the server, upon reception of the
|
||||
/// sealed envelope
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(
|
||||
Debug, Eq, Hash, PartialEq;
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message,
|
||||
)]
|
||||
pub struct CredentialFinalization<CS: CipherSuite> {
|
||||
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message,
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(CredentialFinalization);
|
||||
|
||||
////////////////////////////////
|
||||
// High-level Implementations //
|
||||
// ========================== //
|
||||
////////////////////////////////
|
||||
|
||||
/// Length of [`RegistrationRequest`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type RegistrationRequestLen<CS: CipherSuite> = <CS::OprfGroup as Group>::ElemLen;
|
||||
|
||||
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
||||
/// Only used for testing purposes
|
||||
#[cfg(test)]
|
||||
@@ -96,8 +186,8 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
|
||||
}
|
||||
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||
Ok(self.blinded_element.serialize())
|
||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationRequestLen<CS>> {
|
||||
self.blinded_element.value().to_arr()
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -108,14 +198,23 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`RegistrationResponse`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type RegistrationResponseLen<CS: CipherSuite> =
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, <CS::KeGroup as KeGroup>::PkLen>;
|
||||
|
||||
impl<CS: CipherSuite> RegistrationResponse<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||
Ok([
|
||||
self.evaluation_element.serialize(),
|
||||
self.server_s_pk.to_vec(),
|
||||
]
|
||||
.concat())
|
||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationResponseLen<CS>>
|
||||
where
|
||||
// RegistrationResponse: KgPk + KePk
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
self.evaluation_element
|
||||
.value()
|
||||
.to_arr()
|
||||
.concat(self.server_s_pk.to_arr())
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -147,15 +246,30 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`RegistrationUpload`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type RegistrationUploadLen<CS: CipherSuite> = Sum<
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||
/// Serialization into bytes
|
||||
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())
|
||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationUploadLen<CS>>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
self.client_s_pk
|
||||
.to_arr()
|
||||
.concat(self.masking_key.clone())
|
||||
.concat(self.envelope.serialize())
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -181,25 +295,43 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||
rng: &mut R,
|
||||
server_setup: &ServerSetup<CS, S>,
|
||||
) -> Self {
|
||||
let mut masking_key = alloc::vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
|
||||
let mut masking_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
|
||||
Self {
|
||||
envelope: Envelope::<CS>::dummy(),
|
||||
masking_key: GenericArray::clone_from_slice(&masking_key),
|
||||
masking_key,
|
||||
client_s_pk: server_setup.fake_keypair.public().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`CredentialRequest`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type CredentialRequestLen<CS: CipherSuite> =
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, Ke1MessageLen<CS>>;
|
||||
|
||||
impl<CS: CipherSuite> CredentialRequest<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||
Ok([
|
||||
self.blinded_element.serialize(),
|
||||
self.ke1_message.to_bytes(),
|
||||
]
|
||||
.concat())
|
||||
pub fn serialize(&self) -> GenericArray<u8, CredentialRequestLen<CS>>
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
self.blinded_element
|
||||
.value()
|
||||
.to_arr()
|
||||
.concat(self.ke1_message.to_bytes())
|
||||
}
|
||||
|
||||
pub(crate) fn serialize_iter<'a>(
|
||||
blinded_element: &'a GenericArray<u8, <CS::OprfGroup as Group>::ElemLen>,
|
||||
ke1_message: &'a GenericArray<u8, Ke1MessageLen<CS>>,
|
||||
) -> impl Iterator<Item = &'a [u8]> {
|
||||
// MSRV: array `into_iter` isn't available in 1.51
|
||||
#[allow(deprecated)]
|
||||
IntoIter::new([blinded_element.as_slice(), ke1_message])
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -220,7 +352,7 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
|
||||
}
|
||||
|
||||
let ke1_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message::from_bytes::<CS>(
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message::from_bytes(
|
||||
&checked_slice[elem_len..],
|
||||
)?;
|
||||
|
||||
@@ -239,26 +371,51 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`CredentialResponse`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type CredentialResponseLen<CS: CipherSuite> =
|
||||
Sum<CredentialResponseWithoutKeLen<CS>, Ke2MessageLen<CS>>;
|
||||
|
||||
#[allow(type_alias_bounds)]
|
||||
pub(crate) type CredentialResponseWithoutKeLen<CS: CipherSuite> =
|
||||
Sum<Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
|
||||
|
||||
impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
/// Serialization into bytes
|
||||
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())
|
||||
pub fn serialize(&self) -> GenericArray<u8, CredentialResponseLen<CS>>
|
||||
where
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>:
|
||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
self.evaluation_element
|
||||
.value()
|
||||
.to_arr()
|
||||
.concat(self.masking_nonce)
|
||||
.concat(self.masked_response.serialize())
|
||||
.concat(self.ke2_message.to_bytes())
|
||||
}
|
||||
|
||||
pub(crate) fn serialize_without_ke(
|
||||
beta: &CS::OprfGroup,
|
||||
masking_nonce: &[u8],
|
||||
masked_response: &[u8],
|
||||
) -> Vec<u8> {
|
||||
[&beta.to_arr(), masking_nonce, masked_response].concat()
|
||||
pub(crate) fn serialize_without_ke<'a>(
|
||||
beta: &'a GenericArray<u8, <CS::OprfGroup as Group>::ElemLen>,
|
||||
masking_nonce: &'a GenericArray<u8, NonceLen>,
|
||||
masked_response: &'a MaskedResponse<CS>,
|
||||
) -> impl Iterator<Item = &'a [u8]> {
|
||||
// MSRV: array `into_iter` isn't available in 1.51
|
||||
#[allow(deprecated)]
|
||||
IntoIter::new([beta.as_slice(), masking_nonce.as_slice()])
|
||||
.into_iter()
|
||||
.chain(masked_response.iter())
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -287,12 +444,13 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
return Err(ProtocolError::IdentityGroupElementError);
|
||||
}
|
||||
|
||||
let masking_nonce = checked_slice[elem_len..elem_len + nonce_len].to_vec();
|
||||
let masked_response = checked_slice
|
||||
[elem_len + nonce_len..elem_len + nonce_len + masked_response_len]
|
||||
.to_vec();
|
||||
let masking_nonce =
|
||||
GenericArray::clone_from_slice(&checked_slice[elem_len..elem_len + nonce_len]);
|
||||
let masked_response = MaskedResponse::deserialize(
|
||||
&checked_slice[elem_len + nonce_len..elem_len + nonce_len + masked_response_len],
|
||||
);
|
||||
let ke2_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message::from_bytes::<CS>(
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message::from_bytes(
|
||||
&checked_slice[elem_len + nonce_len + masked_response_len..],
|
||||
)?;
|
||||
|
||||
@@ -310,94 +468,27 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self {
|
||||
Self {
|
||||
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
||||
masking_nonce: self.masking_nonce.clone(),
|
||||
masking_nonce: self.masking_nonce,
|
||||
masked_response: self.masked_response.clone(),
|
||||
ke2_message: self.ke2_message.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`CredentialFinalization`] in bytes for serialization.
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type CredentialFinalizationLen<CS: CipherSuite> = Ke3MessageLen<CS>;
|
||||
|
||||
impl<CS: CipherSuite> CredentialFinalization<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||
Ok(self.ke3_message.to_bytes())
|
||||
pub fn serialize(&self) -> GenericArray<u8, CredentialFinalizationLen<CS>> {
|
||||
self.ke3_message.to_bytes()
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let ke3_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message::from_bytes::<CS>(
|
||||
input,
|
||||
)?;
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message::from_bytes(input)?;
|
||||
Ok(Self { ke3_message })
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// Trait Implementations //
|
||||
// ===================== //
|
||||
///////////////////////////
|
||||
|
||||
impl_clone_for!(
|
||||
struct RegistrationRequest<CS: CipherSuite>,
|
||||
[blinded_element],
|
||||
);
|
||||
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>,
|
||||
[evaluation_element, server_s_pk],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct RegistrationResponse<CS: CipherSuite>,
|
||||
[evaluation_element, server_s_pk],
|
||||
[CS::OprfGroup, CS::Hash],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(RegistrationResponse);
|
||||
|
||||
impl_clone_for!(
|
||||
struct RegistrationUpload<CS: CipherSuite>,
|
||||
[envelope, masking_key, client_s_pk],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct RegistrationUpload<CS: CipherSuite>,
|
||||
[envelope, masking_key, client_s_pk],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(RegistrationUpload);
|
||||
|
||||
impl_clone_for!(
|
||||
struct CredentialRequest<CS: CipherSuite>,
|
||||
[blinded_element, ke1_message],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct CredentialRequest<CS: CipherSuite>,
|
||||
[blinded_element, ke1_message],
|
||||
[
|
||||
CS::OprfGroup,
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message
|
||||
],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(CredentialRequest);
|
||||
|
||||
impl_clone_for!(
|
||||
struct CredentialResponse<CS: CipherSuite>,
|
||||
[evaluation_element, masking_nonce, masked_response, ke2_message],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct CredentialResponse<CS: CipherSuite>,
|
||||
[evaluation_element, masking_nonce, masked_response, ke2_message],
|
||||
[
|
||||
CS::OprfGroup,
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
||||
],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(CredentialResponse);
|
||||
|
||||
impl_clone_for!(struct CredentialFinalization<CS: CipherSuite>, [ke3_message]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct CredentialFinalization<CS: CipherSuite>,
|
||||
[ke3_message],
|
||||
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(CredentialFinalization);
|
||||
|
||||
Regular → Executable
+392
-414
File diff suppressed because it is too large
Load Diff
+151
-27
@@ -6,26 +6,33 @@
|
||||
// of this source tree.
|
||||
|
||||
use crate::errors::ProtocolError;
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::Update;
|
||||
use generic_array::{
|
||||
typenum::{U0, U2},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use hmac::Mac;
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
pub(crate) fn i2osp(input: usize, length: usize) -> Result<alloc::vec::Vec<u8>, ProtocolError> {
|
||||
let sizeof_usize = core::mem::size_of::<usize>();
|
||||
pub(crate) fn i2osp<L: ArrayLength<u8>>(
|
||||
input: usize,
|
||||
) -> Result<GenericArray<u8, L>, ProtocolError> {
|
||||
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
|
||||
|
||||
// Check if input >= 256^length
|
||||
if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 {
|
||||
if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 {
|
||||
return Err(ProtocolError::SerializationError);
|
||||
}
|
||||
|
||||
if length <= sizeof_usize {
|
||||
return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec());
|
||||
if L::USIZE <= SIZEOF_USIZE {
|
||||
return Ok(GenericArray::clone_from_slice(
|
||||
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||
));
|
||||
}
|
||||
|
||||
let mut output = alloc::vec![0u8; length];
|
||||
output.splice(
|
||||
length - sizeof_usize..length,
|
||||
input.to_be_bytes().iter().cloned(),
|
||||
);
|
||||
let mut output = GenericArray::default();
|
||||
output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes());
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
@@ -40,17 +47,94 @@ pub(crate) fn os2ip(input: &[u8]) -> Result<usize, ProtocolError> {
|
||||
Ok(usize::from_be_bytes(output_array))
|
||||
}
|
||||
|
||||
// Computes I2OSP(len(input), max_bytes) || input
|
||||
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result<Vec<u8>, ProtocolError> {
|
||||
Ok([&i2osp(input.len(), max_bytes)?, input].concat())
|
||||
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output without allocation.
|
||||
pub(crate) struct Serialize<
|
||||
'a,
|
||||
L1: ArrayLength<u8>,
|
||||
L2: ArrayLength<u8> = U0,
|
||||
L3: ArrayLength<u8> = U0,
|
||||
> {
|
||||
octet: GenericArray<u8, L1>,
|
||||
input: Input<'a, L2, L3>,
|
||||
}
|
||||
|
||||
enum Input<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> {
|
||||
Owned(GenericArray<u8, L1>),
|
||||
Borrowed(&'a [u8]),
|
||||
Label(([&'a [u8]; 2], PhantomData<L2>)),
|
||||
}
|
||||
|
||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>, L3: ArrayLength<u8>> Serialize<'a, L1, L2, L3> {
|
||||
// Variation of `serialize` that takes a borrowed `input
|
||||
pub(crate) fn from(input: &'a [u8]) -> Result<Serialize<'a, L1, L2>, ProtocolError> {
|
||||
Ok(Serialize {
|
||||
octet: i2osp::<L1>(input.len())?,
|
||||
input: Input::Borrowed(input),
|
||||
})
|
||||
}
|
||||
|
||||
// Variation of `serialize` that takes an owned `input`
|
||||
pub(crate) fn from_owned(
|
||||
input: GenericArray<u8, L2>,
|
||||
) -> Result<Serialize<'a, L1, L2>, ProtocolError> {
|
||||
Ok(Serialize {
|
||||
octet: i2osp::<L1>(input.len())?,
|
||||
input: Input::Owned(input),
|
||||
})
|
||||
}
|
||||
|
||||
// Variation of `serialize` that takes a label
|
||||
pub(crate) fn from_label(
|
||||
opaque: &'a [u8],
|
||||
label: &'a [u8],
|
||||
) -> Result<Serialize<'a, L1, U0, U2>, ProtocolError> {
|
||||
Ok(Serialize {
|
||||
octet: i2osp::<L1>(opaque.len() + label.len())?,
|
||||
input: Input::Label(([opaque, label], PhantomData)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &[u8]> {
|
||||
// Some magic to make it output the same type in all branches.
|
||||
Some(self.octet.as_slice())
|
||||
.into_iter()
|
||||
.chain(match &self.input {
|
||||
Input::Owned(bytes) => Some(bytes.as_slice()),
|
||||
Input::Borrowed(bytes) => Some(*bytes),
|
||||
Input::Label(_) => None,
|
||||
})
|
||||
.chain(if let Input::Label((iter, _)) = &self.input {
|
||||
Some(iter[0]).into_iter().chain(Some(iter[1]).into_iter())
|
||||
} else {
|
||||
None.into_iter().chain(None)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> Serialize<'a, L1, L2, U0> {
|
||||
pub(crate) fn to_array_2(&self) -> [&[u8]; 2] {
|
||||
let input = match &self.input {
|
||||
Input::Borrowed(value) => value,
|
||||
Input::Owned(value) => value.as_slice(),
|
||||
_ => unreachable!("unexpected `Serialize` constructed with wrong generics"),
|
||||
};
|
||||
|
||||
[self.octet.as_slice(), input]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> Serialize<'a, L1, L2, U2> {
|
||||
pub(crate) fn to_array_3(&self) -> [&[u8]; 3] {
|
||||
match self.input {
|
||||
Input::Label((label, _)) => [self.octet.as_slice(), label[0], label[1]],
|
||||
_ => unreachable!("unexpected `Serialize` constructed with wrong generics"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tokenizes an input of the format I2OSP(len(input), max_bytes) || input, outputting
|
||||
// (input, remainder)
|
||||
pub(crate) fn tokenize(
|
||||
input: &[u8],
|
||||
size_bytes: usize,
|
||||
) -> Result<(Vec<u8>, Vec<u8>), ProtocolError> {
|
||||
pub(crate) fn tokenize(input: &[u8], size_bytes: usize) -> Result<(&[u8], &[u8]), ProtocolError> {
|
||||
if size_bytes > core::mem::size_of::<usize>() || input.len() < size_bytes {
|
||||
return Err(ProtocolError::SerializationError);
|
||||
}
|
||||
@@ -61,29 +145,69 @@ pub(crate) fn tokenize(
|
||||
}
|
||||
|
||||
Ok((
|
||||
input[size_bytes..size_bytes + size].to_vec(),
|
||||
input[size_bytes + size..].to_vec(),
|
||||
&input[size_bytes..size_bytes + size],
|
||||
&input[size_bytes + size..],
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) trait UpdateExt {
|
||||
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Update> UpdateExt for T {
|
||||
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self {
|
||||
let mut self_ = self;
|
||||
|
||||
for bytes in iter {
|
||||
self_ = self_.chain(bytes);
|
||||
}
|
||||
|
||||
self_
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait MacExt {
|
||||
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>);
|
||||
}
|
||||
|
||||
impl<T: Mac> MacExt for T {
|
||||
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>) {
|
||||
for bytes in iter {
|
||||
self.update(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The purpose of this macro is to simplify [`concat`](alloc::slice::Concat::concat)ing
|
||||
/// slices into an [`Iterator`] to avoid allocation
|
||||
macro_rules! chain {
|
||||
(
|
||||
$item1:expr,
|
||||
$($item2:expr),+$(,)?
|
||||
) => {
|
||||
$item1$(.chain($item2))+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
assert!(i2osp(0, 1).is_ok());
|
||||
assert!(i2osp::<U1>(0).is_ok());
|
||||
|
||||
assert!(i2osp(255, 1).is_ok());
|
||||
assert!(i2osp(256, 1).is_err());
|
||||
assert!(i2osp(257, 1).is_err());
|
||||
assert!(i2osp::<U1>(255).is_ok());
|
||||
assert!(i2osp::<U1>(256).is_err());
|
||||
assert!(i2osp::<U1>(257).is_err());
|
||||
|
||||
assert!(i2osp(256 * 256 - 1, 2).is_ok());
|
||||
assert!(i2osp(256 * 256, 2).is_err());
|
||||
assert!(i2osp(256 * 256 + 1, 2).is_err());
|
||||
assert!(i2osp::<U2>(256 * 256 - 1).is_ok());
|
||||
assert!(i2osp::<U2>(256 * 256).is_err());
|
||||
assert!(i2osp::<U2>(256 * 256 + 1).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+494
-285
@@ -7,441 +7,650 @@
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::{Envelope, InnerEnvelopeMode},
|
||||
envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode},
|
||||
errors::*,
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{Ke1MessageLen, Ke2MessageLen},
|
||||
},
|
||||
key_exchange::{
|
||||
traits::{FromBytes, KeyExchange, ToBytes},
|
||||
tripledh::{NonceLen, TripleDH},
|
||||
},
|
||||
keypair::KeyPair,
|
||||
serialization::{i2osp, os2ip, serialize},
|
||||
messages::CredentialResponseWithoutKeLen,
|
||||
opaque::MaskedResponseLen,
|
||||
serialization::{i2osp, os2ip, Serialize},
|
||||
*,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use alloc::vec;
|
||||
#[cfg(test)]
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Add;
|
||||
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use generic_array::typenum::Unsigned;
|
||||
use digest::FixedOutput;
|
||||
use generic_array::{
|
||||
typenum::{Sum, Unsigned, U2},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use proptest::{collection::vec, prelude::*};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use voprf::group::Group;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
struct Default;
|
||||
impl CipherSuite for Default {
|
||||
type OprfGroup = RistrettoPoint;
|
||||
type KeGroup = RistrettoPoint;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
struct Ristretto255;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
impl CipherSuite for Ristretto255 {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha512;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
const HASH_SIZE: usize = 64; // Because of SHA512
|
||||
const MAC_SIZE: usize = 64; // Because of SHA512
|
||||
#[cfg(feature = "p256")]
|
||||
struct P256;
|
||||
#[cfg(feature = "p256")]
|
||||
impl CipherSuite for P256 {
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::PublicKey;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
}
|
||||
|
||||
fn random_ristretto_point() -> RistrettoPoint {
|
||||
fn random_point<CS: CipherSuite>() -> CS::KeGroup {
|
||||
let mut rng = OsRng;
|
||||
let mut random_bits = [0u8; 64];
|
||||
rng.fill_bytes(&mut random_bits);
|
||||
|
||||
// This is because RistrettoPoint is on an obsolete sha2 version
|
||||
let mut bits = [0u8; 64];
|
||||
let mut hasher = sha2::Sha512::new();
|
||||
hasher.update(&random_bits[..]);
|
||||
bits.copy_from_slice(&hasher.finalize());
|
||||
|
||||
RistrettoPoint::from_uniform_bytes(&bits)
|
||||
let sk = CS::KeGroup::random_sk(&mut rng);
|
||||
CS::KeGroup::public_key(&sk)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_registration_roundtrip() -> Result<(), ProtocolError> {
|
||||
let pw = b"hunter2";
|
||||
let mut rng = OsRng;
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let pw = b"hunter2";
|
||||
let mut rng = OsRng;
|
||||
|
||||
let blind_result =
|
||||
&voprf::NonVerifiableClient::<RistrettoPoint, sha2::Sha512>::blind(pw.to_vec(), &mut rng)?;
|
||||
let blind_result =
|
||||
&voprf::NonVerifiableClient::<CS::OprfGroup, CS::Hash>::blind(pw.to_vec(), &mut rng)?;
|
||||
|
||||
let bytes: Vec<u8> = [
|
||||
serialize(&blind_result.state.serialize(), 2)?,
|
||||
serialize(&blind_result.message.serialize(), 2)?,
|
||||
]
|
||||
.concat();
|
||||
let bytes: Vec<u8> = chain!(
|
||||
Serialize::<U2>::from(&blind_result.state.serialize())?.iter(),
|
||||
Serialize::<U2>::from(&blind_result.message.serialize())?.iter(),
|
||||
)
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let reg = ClientRegistration::<CS>::deserialize(&bytes)?;
|
||||
let reg_bytes = reg.serialize()?;
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
let reg = ClientRegistration::<Default>::deserialize(&bytes[..])?;
|
||||
let reg_bytes = reg.serialize()?;
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
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;
|
||||
let mut masking_key = [0u8; HASH_SIZE];
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
{
|
||||
// If we don't have envelope and client_pk, the server registration just
|
||||
// contains the prf key
|
||||
let mut rng = OsRng;
|
||||
let mut masking_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
|
||||
// Construct a mock envelope
|
||||
let mut mock_envelope_bytes = Vec::new();
|
||||
mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::USIZE]); // empty nonce
|
||||
// Construct a mock envelope
|
||||
let mut mock_envelope_bytes = Vec::new();
|
||||
mock_envelope_bytes.extend_from_slice(&[0; NonceLen::USIZE]); // empty nonce
|
||||
// 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
|
||||
mock_envelope_bytes
|
||||
.extend_from_slice(&GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default()); // length-MAC_SIZE hmac
|
||||
|
||||
let mock_client_kp = KeyPair::<CS::KeGroup>::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::<CS>::deserialize(&bytes)?;
|
||||
let reg_bytes = reg.serialize();
|
||||
assert_eq!(*reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
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[..])?;
|
||||
let reg_bytes = reg.serialize()?;
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registration_request_roundtrip() -> Result<(), ProtocolError> {
|
||||
let pt = random_ristretto_point();
|
||||
let pt_bytes = pt.to_arr().to_vec();
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let pt = random_point::<CS>();
|
||||
let pt_bytes = pt.to_arr().to_vec();
|
||||
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(pt_bytes.as_slice());
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&pt_bytes);
|
||||
|
||||
let r1 = RegistrationRequest::<Default>::deserialize(input.as_slice())?;
|
||||
let r1_bytes = r1.serialize()?;
|
||||
assert_eq!(input, r1_bytes);
|
||||
let r1 = RegistrationRequest::<CS>::deserialize(&input)?;
|
||||
let r1_bytes = r1.serialize();
|
||||
assert_eq!(input, *r1_bytes);
|
||||
|
||||
// Assert that identity group element is rejected
|
||||
let identity = RistrettoPoint::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
// Assert that identity group element is rejected
|
||||
let identity = CS::OprfGroup::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
|
||||
assert!(
|
||||
match RegistrationRequest::<Default>::deserialize(identity_bytes.as_slice()) {
|
||||
assert!(matches!(
|
||||
RegistrationRequest::<CS>::deserialize(&identity_bytes),
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
))) => true,
|
||||
_ => false,
|
||||
}
|
||||
);
|
||||
)))
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 pubkey_bytes = skp.public().to_arr();
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// RegistrationResponse: KgPk + KePk
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
let pt = random_point::<CS>();
|
||||
let beta_bytes = pt.to_arr();
|
||||
let mut rng = OsRng;
|
||||
let skp = KeyPair::<CS::KeGroup>::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 mut input = Vec::new();
|
||||
input.extend_from_slice(&beta_bytes);
|
||||
input.extend_from_slice(&pubkey_bytes);
|
||||
|
||||
let r2 = RegistrationResponse::<Default>::deserialize(input.as_slice())?;
|
||||
let r2_bytes = r2.serialize()?;
|
||||
assert_eq!(input, r2_bytes);
|
||||
let r2 = RegistrationResponse::<CS>::deserialize(&input)?;
|
||||
let r2_bytes = r2.serialize();
|
||||
assert_eq!(input, *r2_bytes);
|
||||
|
||||
// Assert that identity group element is rejected
|
||||
let identity = RistrettoPoint::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
// Assert that identity group element is rejected
|
||||
let identity = CS::OprfGroup::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
|
||||
assert!(match RegistrationResponse::<Default>::deserialize(
|
||||
&[identity_bytes, pubkey_bytes.to_vec()].concat()
|
||||
) {
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
))) => true,
|
||||
_ => false,
|
||||
});
|
||||
assert!(matches!(
|
||||
RegistrationResponse::<CS>::deserialize(
|
||||
&[identity_bytes, pubkey_bytes.to_vec()].concat()
|
||||
),
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
)))
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let skp = KeyPair::<<Default as CipherSuite>::OprfGroup>::generate_random(&mut rng)?;
|
||||
let pubkey_bytes = skp.public().to_arr();
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let skp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
let pubkey_bytes = skp.public().to_arr();
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
rng.fill_bytes(&mut key);
|
||||
let mut nonce = [0u8; 32];
|
||||
rng.fill_bytes(&mut nonce);
|
||||
let mut key = [0u8; 32];
|
||||
rng.fill_bytes(&mut key);
|
||||
let mut nonce = [0u8; NonceLen::USIZE];
|
||||
rng.fill_bytes(&mut nonce);
|
||||
|
||||
let mut masking_key = vec![0u8; <sha2::Sha512 as Digest>::OutputSize::USIZE];
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
let mut masking_key = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
|
||||
let randomized_pwd_hasher = hkdf::Hkdf::new(None, &key);
|
||||
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 (envelope, _, _) = Envelope::<CS>::seal_raw(
|
||||
randomized_pwd_hasher,
|
||||
nonce.into(),
|
||||
Some(pubkey_bytes.as_slice()).into_iter(),
|
||||
InnerEnvelopeMode::Internal,
|
||||
)
|
||||
.unwrap();
|
||||
let envelope_bytes = envelope.serialize();
|
||||
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&pubkey_bytes[..]);
|
||||
input.extend_from_slice(&masking_key[..]);
|
||||
input.extend_from_slice(&envelope_bytes);
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&pubkey_bytes);
|
||||
input.extend_from_slice(&masking_key);
|
||||
input.extend_from_slice(&envelope_bytes);
|
||||
|
||||
let r3 = RegistrationUpload::<Default>::deserialize(&input[..])?;
|
||||
let r3_bytes = r3.serialize()?;
|
||||
assert_eq!(input, r3_bytes);
|
||||
let r3 = RegistrationUpload::<CS>::deserialize(&input)?;
|
||||
let r3_bytes = r3.serialize();
|
||||
assert_eq!(input, *r3_bytes);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn credential_request_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let alpha = random_ristretto_point();
|
||||
let alpha_bytes = alpha.to_arr().to_vec();
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let alpha = random_point::<CS>();
|
||||
let alpha_bytes = alpha.to_arr();
|
||||
|
||||
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 client_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
let mut client_nonce = [0u8; NonceLen::USIZE];
|
||||
rng.fill_bytes(&mut client_nonce);
|
||||
|
||||
let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
|
||||
let ke1m: Vec<u8> = [client_nonce.as_ref(), client_e_kp.public()].concat();
|
||||
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&alpha_bytes);
|
||||
input.extend_from_slice(&ke1m[..]);
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&alpha_bytes);
|
||||
input.extend_from_slice(&ke1m);
|
||||
|
||||
let l1 = CredentialRequest::<Default>::deserialize(input.as_slice())?;
|
||||
let l1_bytes = l1.serialize()?;
|
||||
assert_eq!(input, l1_bytes);
|
||||
let l1 = CredentialRequest::<CS>::deserialize(&input)?;
|
||||
let l1_bytes = l1.serialize();
|
||||
assert_eq!(input, *l1_bytes);
|
||||
|
||||
// Assert that identity group element is rejected
|
||||
let identity = RistrettoPoint::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
// Assert that identity group element is rejected
|
||||
let identity = CS::OprfGroup::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
|
||||
assert!(match CredentialRequest::<Default>::deserialize(
|
||||
&[identity_bytes, ke1m.to_vec()].concat()
|
||||
) {
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
))) => true,
|
||||
_ => false,
|
||||
});
|
||||
assert!(matches!(
|
||||
CredentialRequest::<CS>::deserialize(&[identity_bytes, ke1m.to_vec()].concat()),
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
)))
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn credential_response_roundtrip() -> Result<(), ProtocolError> {
|
||||
let pt = random_ristretto_point();
|
||||
let pt_bytes = pt.to_arr().to_vec();
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>:
|
||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
let pt = random_point::<CS>();
|
||||
let pt_bytes = pt.to_arr();
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng;
|
||||
|
||||
let mut masking_nonce = vec![0u8; 32];
|
||||
rng.fill_bytes(&mut masking_nonce);
|
||||
let mut masking_nonce = [0u8; 32];
|
||||
rng.fill_bytes(&mut masking_nonce);
|
||||
|
||||
let mut masked_response =
|
||||
vec![0u8; <RistrettoPoint as Group>::ElemLen::USIZE + Envelope::<Default>::len()];
|
||||
rng.fill_bytes(&mut masked_response);
|
||||
let mut masked_response =
|
||||
vec![0u8; <CS::OprfGroup as Group>::ElemLen::USIZE + Envelope::<CS>::len()];
|
||||
rng.fill_bytes(&mut masked_response);
|
||||
|
||||
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];
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
let mut mac = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut mac);
|
||||
let mut server_nonce = [0u8; NonceLen::USIZE];
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
|
||||
let ke2m: Vec<u8> = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat();
|
||||
let ke2m: Vec<u8> = [server_nonce.as_ref(), server_e_kp.public(), &mac].concat();
|
||||
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(pt_bytes.as_slice());
|
||||
input.extend_from_slice(&masking_nonce);
|
||||
input.extend_from_slice(&masked_response);
|
||||
input.extend_from_slice(&ke2m[..]);
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&pt_bytes);
|
||||
input.extend_from_slice(&masking_nonce);
|
||||
input.extend_from_slice(&masked_response);
|
||||
input.extend_from_slice(&ke2m);
|
||||
|
||||
let l2 = CredentialResponse::<Default>::deserialize(&input)?;
|
||||
let l2_bytes = l2.serialize()?;
|
||||
assert_eq!(input, l2_bytes);
|
||||
let l2 = CredentialResponse::<CS>::deserialize(&input)?;
|
||||
let l2_bytes = l2.serialize();
|
||||
assert_eq!(input, *l2_bytes);
|
||||
|
||||
// Assert that identity group element is rejected
|
||||
let identity = RistrettoPoint::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
// Assert that identity group element is rejected
|
||||
let identity = CS::OprfGroup::identity();
|
||||
let identity_bytes = identity.to_arr().to_vec();
|
||||
|
||||
assert!(match CredentialResponse::<Default>::deserialize(
|
||||
&[
|
||||
identity_bytes,
|
||||
masking_nonce.to_vec(),
|
||||
masked_response,
|
||||
ke2m.to_vec()
|
||||
]
|
||||
.concat()
|
||||
) {
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
))) => true,
|
||||
_ => false,
|
||||
});
|
||||
assert!(matches!(
|
||||
CredentialResponse::<CS>::deserialize(
|
||||
&[
|
||||
identity_bytes,
|
||||
masking_nonce.to_vec(),
|
||||
masked_response,
|
||||
ke2m.to_vec()
|
||||
]
|
||||
.concat()
|
||||
),
|
||||
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||
voprf::errors::InternalError::PointError,
|
||||
)))
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn credential_finalization_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let mut mac = [0u8; MAC_SIZE];
|
||||
rng.fill_bytes(&mut mac);
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let mut mac = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut mac);
|
||||
|
||||
let input: Vec<u8> = [&mac[..]].concat();
|
||||
let input = mac;
|
||||
|
||||
let l3 = CredentialFinalization::<Default>::deserialize(&input)?;
|
||||
let l3_bytes = l3.serialize()?;
|
||||
assert_eq!(input, l3_bytes);
|
||||
let l3 = CredentialFinalization::<CS>::deserialize(&input)?;
|
||||
let l3_bytes = l3.serialize();
|
||||
assert_eq!(input.as_slice(), l3_bytes.as_slice());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_login_roundtrip() -> Result<(), ProtocolError> {
|
||||
let pw = b"hunter2";
|
||||
let mut rng = OsRng;
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
let pw = b"hunter2";
|
||||
let mut rng = OsRng;
|
||||
|
||||
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 client_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
let mut client_nonce = [0; 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();
|
||||
let l1_data = [
|
||||
client_e_kp.private().to_arr().to_vec(),
|
||||
client_nonce.to_vec(),
|
||||
]
|
||||
.concat();
|
||||
|
||||
let blind_result =
|
||||
&voprf::NonVerifiableClient::<RistrettoPoint, sha2::Sha512>::blind(pw.to_vec(), &mut rng)?;
|
||||
let blind_result =
|
||||
voprf::NonVerifiableClient::<CS::OprfGroup, CS::Hash>::blind(pw.to_vec(), &mut rng)?;
|
||||
|
||||
let credential_request = CredentialRequest::<CS> {
|
||||
blinded_element: blind_result.message,
|
||||
ke1_message:
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message::from_bytes(
|
||||
&[client_nonce.as_ref(), client_e_kp.public()].concat(),
|
||||
)?,
|
||||
};
|
||||
|
||||
let bytes: Vec<u8> = chain!(
|
||||
Serialize::<U2>::from(&blind_result.state.serialize())?.iter(),
|
||||
Serialize::<U2>::from(&credential_request.serialize())?.iter(),
|
||||
Serialize::<U2>::from(&l1_data)?.iter(),
|
||||
)
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect();
|
||||
let reg = ClientLogin::<CS>::deserialize(&bytes)?;
|
||||
let reg_bytes = reg.serialize()?;
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
let bytes: Vec<u8> = [
|
||||
serialize(&blind_result.state.serialize(), 2)?,
|
||||
serialize(&serialized_credential_request, 2)?,
|
||||
serialize(&l1_data, 2)?,
|
||||
]
|
||||
.concat();
|
||||
let reg = ClientLogin::<Default>::deserialize(&bytes[..])?;
|
||||
let reg_bytes = reg.serialize()?;
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ke1_message_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
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 client_e_kp = KeyPair::<CS::KeGroup>::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[..])?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, ke1m);
|
||||
let ke1m = [client_nonce.as_slice(), client_e_kp.public()].concat();
|
||||
let reg =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message::from_bytes(&ke1m)?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(*reg_bytes, ke1m);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
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];
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
let mut mac = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut mac);
|
||||
let mut server_nonce = vec![0u8; NonceLen::USIZE];
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
|
||||
let ke2m: Vec<u8> = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat();
|
||||
let ke2m: Vec<u8> = [server_nonce.as_slice(), server_e_kp.public(), &mac].concat();
|
||||
|
||||
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE2Message::from_bytes::<
|
||||
Default,
|
||||
>(&ke2m[..])?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, ke2m);
|
||||
let reg =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message::from_bytes(&ke2m)?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(*reg_bytes, ke2m);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let mut mac = [0u8; MAC_SIZE];
|
||||
rng.fill_bytes(&mut mac);
|
||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||
let mut rng = OsRng;
|
||||
let mut mac = GenericArray::<_, <CS::Hash as Digest>::OutputSize>::default();
|
||||
rng.fill_bytes(&mut mac);
|
||||
|
||||
let ke3m: Vec<u8> = [&mac[..]].concat();
|
||||
let ke3m: Vec<u8> = [mac].concat();
|
||||
|
||||
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE3Message::from_bytes::<
|
||||
Default,
|
||||
>(&ke3m[..])?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, ke3m);
|
||||
let reg =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message::from_bytes(&ke3m)?;
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(*reg_bytes, ke3m);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
inner::<Ristretto255>()?;
|
||||
#[cfg(feature = "p256")]
|
||||
inner::<P256>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..core::mem::size_of::<usize>())) {
|
||||
use generic_array::typenum::{U0, U1, U2, U3, U4, U5, U6, U7};
|
||||
|
||||
#[test]
|
||||
fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..core::mem::size_of::<usize>())) {
|
||||
assert_eq!(i2osp(os2ip(&bytes).unwrap(), bytes.len()).unwrap(), bytes);
|
||||
let input = os2ip(&bytes).unwrap();
|
||||
|
||||
let output = match bytes.len() {
|
||||
0 => i2osp::<U0>(input).unwrap().to_vec(),
|
||||
1 => i2osp::<U1>(input).unwrap().to_vec(),
|
||||
2 => i2osp::<U2>(input).unwrap().to_vec(),
|
||||
3 => i2osp::<U3>(input).unwrap().to_vec(),
|
||||
4 => i2osp::<U4>(input).unwrap().to_vec(),
|
||||
5 => i2osp::<U5>(input).unwrap().to_vec(),
|
||||
6 => i2osp::<U6>(input).unwrap().to_vec(),
|
||||
7 => i2osp::<U7>(input).unwrap().to_vec(),
|
||||
_ => unreachable!("unexpected size")
|
||||
};
|
||||
|
||||
assert_eq!(output, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_registration_request(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationRequest::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
macro_rules! test {
|
||||
($mod:ident, $CS:ty) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_nocrash_registration_request(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationRequest::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_registration_response(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationResponse::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_registration_upload(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationUpload::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_request(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialRequest::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_response(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialResponse::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_finalization(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialFinalization::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_client_registration(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ClientRegistration::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_server_registration(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ServerRegistration::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_client_login(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ClientLogin::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_server_login(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ServerLogin::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_registration_response(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationResponse::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_registration_upload(bytes in vec(any::<u8>(), 0..200)) {
|
||||
RegistrationUpload::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_request(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialRequest::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_response(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialResponse::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_credential_finalization(bytes in vec(any::<u8>(), 0..500)) {
|
||||
CredentialFinalization::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_client_registration(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ClientRegistration::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_server_registration(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ServerRegistration::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_client_login(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ClientLogin::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_server_login(bytes in vec(any::<u8>(), 0..700)) {
|
||||
ServerLogin::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
}
|
||||
#[cfg(feature = "ristretto255")]
|
||||
test!(ristretto255, Ristretto255);
|
||||
#[cfg(feature = "p256")]
|
||||
test!(p256, P256);
|
||||
|
||||
+5
-8
@@ -8,10 +8,7 @@
|
||||
//! Trait specifying a slow hashing function
|
||||
|
||||
use crate::{errors::InternalError, hash::Hash};
|
||||
use alloc::vec::Vec;
|
||||
use digest::Digest;
|
||||
#[cfg(feature = "slow-hash")]
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::GenericArray;
|
||||
|
||||
/// Used for the slow hashing function in OPAQUE
|
||||
@@ -20,7 +17,7 @@ pub trait SlowHash<D: Hash>: Default {
|
||||
fn hash(
|
||||
&self,
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalError>;
|
||||
) -> Result<GenericArray<u8, <D as Digest>::OutputSize>, InternalError>;
|
||||
}
|
||||
|
||||
/// A no-op hash which simply returns its input
|
||||
@@ -31,8 +28,8 @@ impl<D: Hash> SlowHash<D> for NoOpHash {
|
||||
fn hash(
|
||||
&self,
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalError> {
|
||||
Ok(input.to_vec())
|
||||
) -> Result<GenericArray<u8, <D as Digest>::OutputSize>, InternalError> {
|
||||
Ok(input)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +38,8 @@ impl<D: Hash> SlowHash<D> for argon2::Argon2<'_> {
|
||||
fn hash(
|
||||
&self,
|
||||
input: GenericArray<u8, <D as Digest>::OutputSize>,
|
||||
) -> Result<Vec<u8>, InternalError> {
|
||||
let mut output = alloc::vec![0u8; <D as Digest>::OutputSize::USIZE];
|
||||
) -> Result<GenericArray<u8, <D as Digest>::OutputSize>, InternalError> {
|
||||
let mut output = GenericArray::default();
|
||||
self.hash_password_into(&input, &[0; argon2::MIN_SALT_LEN], &mut output)
|
||||
.map_err(|_| InternalError::SlowHashError)?;
|
||||
Ok(output)
|
||||
|
||||
Regular → Executable
+1105
-565
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ use rand::{CryptoRng, Error, RngCore};
|
||||
/// This generates a cyclic sequence (i.e. cycles over an initial buffer)
|
||||
///
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CycleRng {
|
||||
v: Vec<u8>,
|
||||
}
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ fn parse_vector_types(input: &str) -> String {
|
||||
for caps in re.captures_iter(input) {
|
||||
let vector_type = format!(
|
||||
"\"{}\": [\n {} \n]",
|
||||
caps["type"].to_string(),
|
||||
&caps["type"],
|
||||
parse_ciphersuites(chunks[count])
|
||||
);
|
||||
vector_types.push(vector_type);
|
||||
@@ -44,8 +44,8 @@ fn parse_ciphersuites(input: &str) -> String {
|
||||
for caps in re.captures_iter(input) {
|
||||
let ciphersuite = format!(
|
||||
"{{ \"{}, {}\": {{ {} }} }}",
|
||||
caps["group"].to_string(),
|
||||
caps["hash"].to_string(),
|
||||
&caps["group"],
|
||||
&caps["hash"],
|
||||
parse_params(chunks[count])
|
||||
);
|
||||
ciphersuites.push(ciphersuite);
|
||||
|
||||
Regular → Executable
+238
-137
@@ -6,11 +6,29 @@
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, opaque::*,
|
||||
slow_hash::NoOpHash, tests::mock_rng::CycleRng, *,
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::EnvelopeLen,
|
||||
errors::*,
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{Ke1MessageLen, Ke2MessageLen},
|
||||
tripledh::{NonceLen, TripleDH},
|
||||
},
|
||||
messages::{
|
||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||
RegistrationResponseLen, RegistrationUploadLen,
|
||||
},
|
||||
opaque::*,
|
||||
slow_hash::NoOpHash,
|
||||
tests::mock_rng::CycleRng,
|
||||
*,
|
||||
};
|
||||
use alloc::{string::ToString, vec, vec::Vec};
|
||||
use core::ops::Add;
|
||||
use digest::FixedOutput;
|
||||
use generic_array::{typenum::Sum, ArrayLength};
|
||||
use json::JsonValue;
|
||||
use voprf::group::Group;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug)]
|
||||
@@ -130,29 +148,23 @@ fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_password_file_bytes<CS: CipherSuite>(
|
||||
parameters: &OpaqueTestVectorParameters,
|
||||
) -> Result<Vec<u8>, ProtocolError> {
|
||||
fn get_password_file_bytes<CS: CipherSuite>(parameters: &OpaqueTestVectorParameters) -> Vec<u8>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
{
|
||||
let password_file = ServerRegistration::<CS>::finish(
|
||||
RegistrationUpload::deserialize(¶meters.registration_upload[..]).unwrap(),
|
||||
RegistrationUpload::deserialize(¶meters.registration_upload).unwrap(),
|
||||
);
|
||||
|
||||
password_file.serialize()
|
||||
}
|
||||
|
||||
fn parse_identifiers(
|
||||
client_identity: &Option<Vec<u8>>,
|
||||
server_identity: &Option<Vec<u8>>,
|
||||
) -> Option<Identifiers> {
|
||||
match (client_identity, server_identity) {
|
||||
(None, None) => None,
|
||||
(Some(x), None) => Some(Identifiers::ClientIdentifier(x.clone())),
|
||||
(None, Some(y)) => Some(Identifiers::ServerIdentifier(y.clone())),
|
||||
(Some(x), Some(y)) => Some(Identifiers::ClientAndServerIdentifiers(
|
||||
x.clone(),
|
||||
y.clone(),
|
||||
)),
|
||||
}
|
||||
password_file.serialize().to_vec()
|
||||
}
|
||||
|
||||
macro_rules! json_to_test_vectors {
|
||||
@@ -175,32 +187,36 @@ 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, "Real", "ristretto255, SHA512",);
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512",);
|
||||
|
||||
let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "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");
|
||||
assert!(
|
||||
!(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()),
|
||||
"Parsing error"
|
||||
);
|
||||
|
||||
struct Ristretto255Sha512NoSlowHash;
|
||||
impl CipherSuite for Ristretto255Sha512NoSlowHash {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha512;
|
||||
type SlowHash = NoOpHash;
|
||||
}
|
||||
|
||||
test_registration_request::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_registration_response::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_registration_upload::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke1::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke2::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke3::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_server_login_finish::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_fake_vectors::<Ristretto255Sha512NoSlowHash>(&ristretto_fake_tvs)?;
|
||||
}
|
||||
|
||||
struct Ristretto255Sha512NoSlowHash;
|
||||
impl CipherSuite for Ristretto255Sha512NoSlowHash {
|
||||
type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeGroup = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha512;
|
||||
type SlowHash = NoOpHash;
|
||||
}
|
||||
|
||||
test_registration_request::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_registration_response::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_registration_upload::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke1::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke2::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_ke3::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_server_login_finish::<Ristretto255Sha512NoSlowHash>(&ristretto_real_tvs)?;
|
||||
test_fake_vectors::<Ristretto255Sha512NoSlowHash>(&ristretto_fake_tvs)?;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
{
|
||||
let p256_real_tvs =
|
||||
@@ -208,14 +224,15 @@ fn tests() -> Result<(), ProtocolError> {
|
||||
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");
|
||||
}
|
||||
assert!(
|
||||
!(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()),
|
||||
"Parsing error"
|
||||
);
|
||||
|
||||
struct P256Sha256NoSlowHash;
|
||||
impl CipherSuite for P256Sha256NoSlowHash {
|
||||
type OprfGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::ProjectivePoint;
|
||||
type KeGroup = p256_::PublicKey;
|
||||
type KeyExchange = TripleDH;
|
||||
type Hash = sha2::Sha256;
|
||||
type SlowHash = NoOpHash;
|
||||
@@ -243,7 +260,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(())
|
||||
@@ -251,19 +268,24 @@ fn test_registration_request<CS: CipherSuite>(
|
||||
|
||||
fn test_registration_response<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> Result<(), ProtocolError> {
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// RegistrationResponse: KgPk + KePk
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server_setup = ServerSetup::<CS>::deserialize(
|
||||
&[
|
||||
¶meters.oprf_seed[..],
|
||||
¶meters.server_private_key[..],
|
||||
¶meters.dummy_private_key[..],
|
||||
parameters.oprf_seed.as_slice(),
|
||||
¶meters.server_private_key,
|
||||
¶meters.dummy_private_key,
|
||||
]
|
||||
.concat(),
|
||||
)?;
|
||||
let server_registration_start_result = ServerRegistration::<CS>::start(
|
||||
&server_setup,
|
||||
RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(),
|
||||
RegistrationRequest::deserialize(¶meters.registration_request).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
)?;
|
||||
assert_eq!(
|
||||
@@ -272,7 +294,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(())
|
||||
@@ -280,7 +302,17 @@ fn test_registration_response<CS: CipherSuite>(
|
||||
|
||||
fn test_registration_upload<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> Result<(), ProtocolError> {
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.blind_registration.to_vec());
|
||||
let client_registration_start_result =
|
||||
@@ -289,11 +321,14 @@ fn test_registration_upload<CS: CipherSuite>(
|
||||
let mut finish_registration_rng = CycleRng::new(parameters.envelope_nonce.to_vec());
|
||||
let result = client_registration_start_result.state.finish(
|
||||
&mut finish_registration_rng,
|
||||
RegistrationResponse::deserialize(¶meters.registration_response[..]).unwrap(),
|
||||
match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) {
|
||||
None => ClientRegistrationFinishParameters::default(),
|
||||
Some(ids) => ClientRegistrationFinishParameters::new(Some(ids), None),
|
||||
},
|
||||
RegistrationResponse::deserialize(¶meters.registration_response).unwrap(),
|
||||
ClientRegistrationFinishParameters::new(
|
||||
Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
server: parameters.server_identity.as_deref(),
|
||||
},
|
||||
None,
|
||||
),
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.auth_key),
|
||||
@@ -305,7 +340,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),
|
||||
@@ -316,18 +351,23 @@ fn test_registration_upload<CS: CipherSuite>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> {
|
||||
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let client_login_start = [
|
||||
¶meters.blind_login[..],
|
||||
¶meters.client_private_keyshare[..],
|
||||
¶meters.client_nonce[..],
|
||||
parameters.blind_login.as_slice(),
|
||||
¶meters.client_private_keyshare,
|
||||
¶meters.client_nonce,
|
||||
]
|
||||
.concat();
|
||||
|
||||
println!(
|
||||
"¶meters.blind_login[..]: {:?}",
|
||||
hex::encode(¶meters.blind_login[..])
|
||||
"parameters.blind_login: {:?}",
|
||||
hex::encode(¶meters.blind_login)
|
||||
);
|
||||
|
||||
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
||||
@@ -335,32 +375,59 @@ fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
|
||||
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(())
|
||||
}
|
||||
|
||||
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> {
|
||||
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server_setup = ServerSetup::<CS>::deserialize(
|
||||
&[
|
||||
¶meters.oprf_seed[..],
|
||||
¶meters.server_private_key[..],
|
||||
¶meters.dummy_private_key[..],
|
||||
parameters.oprf_seed.as_slice(),
|
||||
¶meters.server_private_key,
|
||||
¶meters.dummy_private_key,
|
||||
]
|
||||
.concat(),
|
||||
)?;
|
||||
|
||||
let record = ServerRegistration::<CS>::deserialize(
|
||||
&get_password_file_bytes::<CS>(¶meters)?[..],
|
||||
)?;
|
||||
let record =
|
||||
ServerRegistration::<CS>::deserialize(&get_password_file_bytes::<CS>(parameters))?;
|
||||
|
||||
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
|
||||
[
|
||||
¶meters.masking_nonce[..],
|
||||
¶meters.server_private_keyshare[..],
|
||||
¶meters.server_nonce[..],
|
||||
parameters.masking_nonce.as_slice(),
|
||||
¶meters.server_private_keyshare,
|
||||
¶meters.server_nonce,
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
@@ -368,14 +435,14 @@ fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
|
||||
&mut server_private_keyshare_and_nonce_rng,
|
||||
&server_setup,
|
||||
Some(record),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1[..]).unwrap(),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) {
|
||||
None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
|
||||
Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
|
||||
parameters.context.to_vec(),
|
||||
ids,
|
||||
),
|
||||
ServerLoginStartParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
server: parameters.server_identity.as_deref(),
|
||||
},
|
||||
},
|
||||
)?;
|
||||
assert_eq!(
|
||||
@@ -392,18 +459,25 @@ 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(())
|
||||
}
|
||||
|
||||
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> {
|
||||
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||
where
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let client_login_start = [
|
||||
¶meters.blind_login[..],
|
||||
¶meters.client_private_keyshare[..],
|
||||
¶meters.client_nonce[..],
|
||||
parameters.blind_login.as_slice(),
|
||||
¶meters.client_private_keyshare,
|
||||
¶meters.client_nonce,
|
||||
]
|
||||
.concat();
|
||||
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
||||
@@ -411,17 +485,15 @@ fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
|
||||
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
||||
|
||||
let client_login_finish_result = client_login_start_result.state.finish(
|
||||
CredentialResponse::<CS>::deserialize(¶meters.KE2[..])?,
|
||||
match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) {
|
||||
None => {
|
||||
ClientLoginFinishParameters::new(Some(parameters.context.clone()), None, None)
|
||||
}
|
||||
Some(ids) => ClientLoginFinishParameters::new(
|
||||
Some(parameters.context.clone()),
|
||||
Some(ids),
|
||||
None,
|
||||
),
|
||||
},
|
||||
CredentialResponse::<CS>::deserialize(¶meters.KE2)?,
|
||||
ClientLoginFinishParameters::new(
|
||||
Some(¶meters.context.clone()),
|
||||
Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
server: parameters.server_identity.as_deref(),
|
||||
},
|
||||
None,
|
||||
),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
@@ -438,7 +510,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),
|
||||
@@ -450,26 +522,41 @@ fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P
|
||||
|
||||
fn test_server_login_finish<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> Result<(), ProtocolError> {
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server_setup = ServerSetup::<CS>::deserialize(
|
||||
&[
|
||||
¶meters.oprf_seed[..],
|
||||
¶meters.server_private_key[..],
|
||||
¶meters.dummy_private_key[..],
|
||||
parameters.oprf_seed.as_slice(),
|
||||
¶meters.server_private_key,
|
||||
¶meters.dummy_private_key,
|
||||
]
|
||||
.concat(),
|
||||
)?;
|
||||
|
||||
let record = ServerRegistration::<CS>::deserialize(
|
||||
&get_password_file_bytes::<CS>(¶meters)?[..],
|
||||
)?;
|
||||
let record =
|
||||
ServerRegistration::<CS>::deserialize(&get_password_file_bytes::<CS>(parameters))?;
|
||||
|
||||
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
|
||||
[
|
||||
¶meters.masking_nonce[..],
|
||||
¶meters.server_private_keyshare[..],
|
||||
¶meters.server_nonce[..],
|
||||
parameters.masking_nonce.as_slice(),
|
||||
¶meters.server_private_keyshare,
|
||||
¶meters.server_nonce,
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
@@ -477,20 +564,20 @@ fn test_server_login_finish<CS: CipherSuite>(
|
||||
&mut server_private_keyshare_and_nonce_rng,
|
||||
&server_setup,
|
||||
Some(record),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1[..]).unwrap(),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) {
|
||||
None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
|
||||
Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
|
||||
parameters.context.to_vec(),
|
||||
ids,
|
||||
),
|
||||
ServerLoginStartParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
server: parameters.server_identity.as_deref(),
|
||||
},
|
||||
},
|
||||
)?;
|
||||
|
||||
let server_login_result = server_login_start_result
|
||||
.state
|
||||
.finish(CredentialFinalization::deserialize(¶meters.KE3[..])?)?;
|
||||
.finish(CredentialFinalization::deserialize(¶meters.KE3)?)?;
|
||||
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.session_key),
|
||||
@@ -502,23 +589,37 @@ fn test_server_login_finish<CS: CipherSuite>(
|
||||
|
||||
fn test_fake_vectors<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> Result<(), ProtocolError> {
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// MaskedResponse: (Nonce + Hash) + KePk
|
||||
NonceLen: Add<<CS::Hash as FixedOutput>::OutputSize>,
|
||||
Sum<NonceLen, <CS::Hash as FixedOutput>::OutputSize>:
|
||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<CS::OprfGroup as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<CS::OprfGroup as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server_setup = ServerSetup::<CS>::deserialize(
|
||||
&[
|
||||
¶meters.oprf_seed[..],
|
||||
¶meters.server_private_key[..],
|
||||
¶meters.dummy_private_key[..],
|
||||
parameters.oprf_seed.as_slice(),
|
||||
¶meters.server_private_key,
|
||||
¶meters.dummy_private_key,
|
||||
]
|
||||
.concat(),
|
||||
)?;
|
||||
|
||||
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
|
||||
[
|
||||
¶meters.dummy_masking_key[..],
|
||||
¶meters.masking_nonce[..],
|
||||
¶meters.server_private_keyshare[..],
|
||||
¶meters.server_nonce[..],
|
||||
parameters.dummy_masking_key.as_slice(),
|
||||
¶meters.masking_nonce,
|
||||
¶meters.server_private_keyshare,
|
||||
¶meters.server_nonce,
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
@@ -526,19 +627,19 @@ fn test_fake_vectors<CS: CipherSuite>(
|
||||
&mut server_private_keyshare_and_nonce_rng,
|
||||
&server_setup,
|
||||
None,
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1[..]).unwrap(),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) {
|
||||
None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
|
||||
Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
|
||||
parameters.context.to_vec(),
|
||||
ids,
|
||||
),
|
||||
ServerLoginStartParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
server: parameters.server_identity.as_deref(),
|
||||
},
|
||||
},
|
||||
)?;
|
||||
assert_eq!(
|
||||
hex::encode(¶meters.KE2),
|
||||
hex::encode(server_login_start_result.message.serialize()?)
|
||||
hex::encode(server_login_start_result.message.serialize())
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user