@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
@@ -227,11 +227,11 @@ jobs:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
- name: Install nightly toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
@@ -241,6 +241,28 @@ jobs:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
taplo:
|
||||
name: Taplo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/.crates.toml
|
||||
~/.cargo/.crates2.json
|
||||
~/.cargo/bin/taplo
|
||||
key: taplo
|
||||
|
||||
- name: Install Taplo
|
||||
run: cargo install taplo-cli
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run Taplo
|
||||
run: taplo fmt --check
|
||||
|
||||
deny-check:
|
||||
name: cargo-deny check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+59
-27
@@ -1,38 +1,64 @@
|
||||
[package]
|
||||
name = "opaque-ke"
|
||||
version = "2.0.0-pre.1"
|
||||
repository = "https://github.com/novifinancial/opaque-ke"
|
||||
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
||||
authors = ["Kevin Lewi <[email protected]>", "François Garillot <[email protected]>"]
|
||||
categories = ["no-std"]
|
||||
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
|
||||
authors = ["Kevin Lewi <[email protected]>", "François Garillot <[email protected]>"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
edition = "2021"
|
||||
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
name = "opaque-ke"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/novifinancial/opaque-ke"
|
||||
rust-version = "1.56"
|
||||
version = "2.0.0-pre.1"
|
||||
|
||||
[features]
|
||||
default = ["ristretto255_u64", "serde"]
|
||||
slow-hash = ["argon2"]
|
||||
p256 = ["p256_", "voprf/p256"]
|
||||
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"]
|
||||
ristretto255_fiat_u32 = [
|
||||
"curve25519-dalek/fiat_u32_backend",
|
||||
"ristretto255",
|
||||
"voprf/ristretto255_fiat_u32",
|
||||
]
|
||||
ristretto255_fiat_u64 = [
|
||||
"curve25519-dalek/fiat_u64_backend",
|
||||
"ristretto255",
|
||||
"voprf/ristretto255_fiat_u64",
|
||||
]
|
||||
ristretto255_simd = [
|
||||
"curve25519-dalek/simd_backend",
|
||||
"ristretto255",
|
||||
"voprf/ristretto255_simd",
|
||||
]
|
||||
ristretto255_u32 = [
|
||||
"curve25519-dalek/u32_backend",
|
||||
"ristretto255",
|
||||
"voprf/ristretto255_u32",
|
||||
]
|
||||
ristretto255_u64 = [
|
||||
"curve25519-dalek/u64_backend",
|
||||
"ristretto255",
|
||||
"voprf/ristretto255_u64",
|
||||
]
|
||||
serde = ["serde_", "generic-array/serde", "voprf/serde"]
|
||||
slow-hash = ["argon2"]
|
||||
std = ["getrandom", "rand/std", "rand/std_rng", "voprf/std"]
|
||||
x25519 = []
|
||||
x25519_fiat_u32 = ["x25519", "x25519-dalek/fiat_u32_backend"]
|
||||
x25519_fiat_u64 = ["x25519", "x25519-dalek/fiat_u64_backend"]
|
||||
# x25519-dalek isn't properly re-exposing `simd_backend`.
|
||||
x25519_simd = [
|
||||
"curve25519-dalek/simd_backend",
|
||||
"x25519",
|
||||
"x25519-dalek/nightly",
|
||||
]
|
||||
x25519_u32 = ["x25519", "x25519-dalek/u32_backend"]
|
||||
x25519_u64 = ["x25519", "x25519-dalek/u64_backend"]
|
||||
|
||||
[dependencies]
|
||||
argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true }
|
||||
argon2 = { version = "0.3", default-features = false, features = [
|
||||
"alloc",
|
||||
], optional = true }
|
||||
constant_time_eq = "0.1"
|
||||
curve25519-dalek = { version = "3", default-features = false, optional = true }
|
||||
derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] }
|
||||
@@ -42,11 +68,17 @@ generic-array = "0.14"
|
||||
getrandom = { version = "0.2", optional = true }
|
||||
hkdf = "0.12"
|
||||
hmac = "0.12"
|
||||
p256_ = { package = "p256", version = "0.10", default-features = false, features = ["arithmetic"], optional = true }
|
||||
p256_ = { package = "p256", version = "0.10", default-features = false, features = [
|
||||
"arithmetic",
|
||||
], optional = true }
|
||||
rand = { version = "0.8", default-features = false }
|
||||
serde_ = { version = "1", package = "serde", default-features = false, features = ["derive"], optional = true }
|
||||
serde_ = { version = "1", package = "serde", default-features = false, features = [
|
||||
"derive",
|
||||
], optional = true }
|
||||
subtle = { version = "2.3", default-features = false }
|
||||
voprf = { git = "https://github.com/novifinancial/voprf", rev = "55ef981a3f9a12eddd8c372ffdf51818011343ee", default-features = false, features = ["danger"] }
|
||||
voprf = { git = "https://github.com/novifinancial/voprf", rev = "55ef981a3f9a12eddd8c372ffdf51818011343ee", default-features = false, features = [
|
||||
"danger",
|
||||
] }
|
||||
x25519-dalek = { version = "1", default-features = false, optional = true }
|
||||
zeroize = { version = "1", features = ["zeroize_derive"] }
|
||||
|
||||
@@ -61,12 +93,12 @@ criterion = "0.3"
|
||||
hex = "0.4"
|
||||
json = "0.12"
|
||||
lazy_static = "1"
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
proptest = "1"
|
||||
regex = "1"
|
||||
rustyline = "9"
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
|
||||
[[bench]]
|
||||
name = "opaque"
|
||||
harness = false
|
||||
name = "opaque"
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
# dependencies not shared by any other crates, would be ignored, as the target
|
||||
# list here is effectively saying which targets you are building for.
|
||||
targets = [
|
||||
# The triple can be any string, but only the target triples built in to
|
||||
# rustc (as of 1.40) can be checked against actual config expressions
|
||||
#{ triple = "x86_64-unknown-linux-musl" },
|
||||
# You can also specify which target_features you promise are enabled for a
|
||||
# particular target. target_features are currently not validated against
|
||||
# the actual valid features supported by the target architecture.
|
||||
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
|
||||
# The triple can be any string, but only the target triples built in to
|
||||
# rustc (as of 1.40) can be checked against actual config expressions
|
||||
#{ triple = "x86_64-unknown-linux-musl" },
|
||||
# You can also specify which target_features you promise are enabled for a
|
||||
# particular target. target_features are currently not validated against
|
||||
# the actual valid features supported by the target architecture.
|
||||
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check advisories`
|
||||
@@ -48,7 +48,7 @@ notice = "deny"
|
||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||
# output a note when they are encountered.
|
||||
ignore = [
|
||||
#"RUSTSEC-0000-0000",
|
||||
#"RUSTSEC-0000-0000",
|
||||
]
|
||||
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
||||
# lower than the range specified will be ignored. Note that ignored advisories
|
||||
@@ -70,15 +70,15 @@ unlicensed = "deny"
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
# [possible values: any SPDX 3.7 short identifier (+ optional exception)].
|
||||
allow = [
|
||||
#"MIT",
|
||||
#"Apache-2.0",
|
||||
#"Apache-2.0 WITH LLVM-exception",
|
||||
#"MIT",
|
||||
#"Apache-2.0",
|
||||
#"Apache-2.0 WITH LLVM-exception",
|
||||
]
|
||||
# List of explictly disallowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
# [possible values: any SPDX 3.7 short identifier (+ optional exception)].
|
||||
deny = [
|
||||
#"Nokia",
|
||||
#"Nokia",
|
||||
]
|
||||
# Lint level for licenses considered copyleft
|
||||
copyleft = "warn"
|
||||
@@ -102,9 +102,9 @@ confidence-threshold = 0.8
|
||||
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
||||
# aren't accepted for every possible crate as with the normal allow list
|
||||
exceptions = [
|
||||
# Each entry is the crate and version constraint, and its specific allow
|
||||
# list
|
||||
#{ allow = ["Zlib"], name = "adler32", version = "*" },
|
||||
# Each entry is the crate and version constraint, and its specific allow
|
||||
# list
|
||||
#{ allow = ["Zlib"], name = "adler32", version = "*" },
|
||||
]
|
||||
|
||||
# Some crates don't have (easily) machine readable licensing information,
|
||||
@@ -123,8 +123,8 @@ exceptions = [
|
||||
# and the crate will be checked normally, which may produce warnings or errors
|
||||
# depending on the rest of your configuration
|
||||
#license-files = [
|
||||
# Each entry is a crate relative path, and the (opaque) hash of its contents
|
||||
#{ path = "LICENSE", hash = 0xbd0eed23 }
|
||||
# Each entry is a crate relative path, and the (opaque) hash of its contents
|
||||
#{ path = "LICENSE", hash = 0xbd0eed23 }
|
||||
#]
|
||||
|
||||
[licenses.private]
|
||||
@@ -135,7 +135,7 @@ ignore = false
|
||||
# is only published to private registries, and ignore is true, the crate will
|
||||
# not have its license(s) checked
|
||||
registries = [
|
||||
#"https://sekretz.com/registry
|
||||
#"https://sekretz.com/registry
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check bans`.
|
||||
@@ -152,24 +152,24 @@ multiple-versions = "warn"
|
||||
highlight = "all"
|
||||
# List of crates that are allowed. Use with care!
|
||||
allow = [
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
]
|
||||
# List of crates to deny
|
||||
deny = [
|
||||
# Each entry the name of a crate and a version range. If version is
|
||||
# not specified, all versions will be matched.
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
# Each entry the name of a crate and a version range. If version is
|
||||
# not specified, all versions will be matched.
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
]
|
||||
# Certain crates/versions that will be skipped when doing duplicate detection.
|
||||
skip = [
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
]
|
||||
# Similarly to `skip` allows you to skip certain crates during duplicate
|
||||
# detection. Unlike skip, it also includes the entire tree of transitive
|
||||
# dependencies starting at the specified crate, up to a certain depth, which is
|
||||
# by default infinite
|
||||
skip-tree = [
|
||||
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
|
||||
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check sources`.
|
||||
|
||||
+28
-27
@@ -8,43 +8,42 @@
|
||||
//! Demonstrates an implementation of a server-side secured digital locker using
|
||||
//! the client's OPAQUE export key, over a command-line interface
|
||||
//!
|
||||
//! A client can password-protect a secret message to be stored in a digital locker,
|
||||
//! controlled by the server. The locker's contents are only revealed to the holder
|
||||
//! of the password when attempting to open the locker.
|
||||
//! A client can password-protect a secret message to be stored in a digital
|
||||
//! locker, controlled by the server. The locker's contents are only revealed to
|
||||
//! the holder of the password when attempting to open the locker.
|
||||
//!
|
||||
//! The client-server interactions are executed in a three-step protocol
|
||||
//! within the account_registration (for password registration) and
|
||||
//! account_login (for password login) functions. These steps
|
||||
//! must be performed in the specific sequence outlined in each of these
|
||||
//! functions.
|
||||
//! The client-server interactions are executed in a three-step protocol within
|
||||
//! the account_registration (for password registration) and account_login (for
|
||||
//! password login) functions. These steps must be performed in the specific
|
||||
//! sequence outlined in each of these functions.
|
||||
//!
|
||||
//! The CipherSuite trait allows the application to configure the
|
||||
//! primitives used by OPAQUE, but must be kept consistent across the steps
|
||||
//! of the protocol.
|
||||
//! The CipherSuite trait allows the application to configure the primitives
|
||||
//! used by OPAQUE, but must be kept consistent across the steps of the
|
||||
//! protocol.
|
||||
//!
|
||||
//! In a more realistic client-server interaction, the client must send
|
||||
//! messages over "the wire" to the server. These bytes are serialized
|
||||
//! and explicitly annotated in the below functions.
|
||||
//! In a more realistic client-server interaction, the client must send messages
|
||||
//! over "the wire" to the server. These bytes are serialized and explicitly
|
||||
//! annotated in the below functions.
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
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;
|
||||
|
||||
use opaque_ke::ciphersuite::CipherSuite;
|
||||
use opaque_ke::rand::rngs::OsRng;
|
||||
use opaque_ke::rand::RngCore;
|
||||
use opaque_ke::{
|
||||
ciphersuite::CipherSuite,
|
||||
rand::{rngs::OsRng, RngCore},
|
||||
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
||||
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
||||
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
||||
ServerLoginStartParameters, ServerRegistration, ServerSetup,
|
||||
ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
||||
};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
||||
// The ciphersuite trait allows to specify the underlying primitives
|
||||
// that will be used in the OPAQUE protocol
|
||||
// The ciphersuite trait allows to specify the underlying primitives that will
|
||||
// be used in the OPAQUE protocol
|
||||
#[allow(dead_code)]
|
||||
struct Default;
|
||||
|
||||
@@ -84,7 +83,8 @@ fn encrypt(key: &[u8], plaintext: &[u8]) -> Vec<u8> {
|
||||
[nonce_bytes.to_vec(), ciphertext].concat()
|
||||
}
|
||||
|
||||
// Decrypt using a key and a ciphertext (nonce included) to recover the original plaintext
|
||||
// Decrypt using a key and a ciphertext (nonce included) to recover the original
|
||||
// plaintext
|
||||
fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec<u8> {
|
||||
let cipher = ChaCha20Poly1305::new(Key::from_slice(&key[..32]));
|
||||
cipher
|
||||
@@ -95,7 +95,8 @@ fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec<u8> {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// Password-based registration and encryption of client secret message between a client and server
|
||||
// Password-based registration and encryption of client secret message between a
|
||||
// client and server
|
||||
fn register_locker(
|
||||
server_setup: &ServerSetup<Default>,
|
||||
locker_id: usize,
|
||||
@@ -200,7 +201,7 @@ fn open_locker(
|
||||
let encrypted_locker_contents =
|
||||
encrypt(&server_login_finish_result.session_key, &locker.contents);
|
||||
|
||||
// Client decrypts contents of locker, first under the session key, and then under the export key
|
||||
// Client decrypts contents of locker, first under the session key, and then
|
||||
let plaintext = decrypt(
|
||||
&client_login_finish_result.export_key,
|
||||
&decrypt(
|
||||
|
||||
+29
-28
@@ -5,39 +5,39 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Demonstrates a simple client-server password-based login protocol
|
||||
//! using OPAQUE, over a command-line interface
|
||||
//! Demonstrates a simple client-server password-based login protocol using
|
||||
//! OPAQUE, over a command-line interface
|
||||
//!
|
||||
//! The client-server interactions are executed in a three-step protocol
|
||||
//! within the account_registration (for password registration) and
|
||||
//! account_login (for password login) functions. These steps
|
||||
//! must be performed in the specific sequence outlined in each of these
|
||||
//! functions.
|
||||
//! The client-server interactions are executed in a three-step protocol within
|
||||
//! the account_registration (for password registration) and account_login (for
|
||||
//! password login) functions. These steps must be performed in the specific
|
||||
//! sequence outlined in each of these functions.
|
||||
//!
|
||||
//! The CipherSuite trait allows the application to configure the
|
||||
//! primitives used by OPAQUE, but must be kept consistent across the steps
|
||||
//! of the protocol.
|
||||
//! The CipherSuite trait allows the application to configure the primitives
|
||||
//! used by OPAQUE, but must be kept consistent across the steps of the
|
||||
//! protocol.
|
||||
//!
|
||||
//! In a more realistic client-server interaction, the client must send
|
||||
//! messages over "the wire" to the server. These bytes are serialized
|
||||
//! and explicitly annotated in the below functions.
|
||||
//! In a more realistic client-server interaction, the client must send 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;
|
||||
use std::process::exit;
|
||||
|
||||
use generic_array::GenericArray;
|
||||
use opaque_ke::ciphersuite::CipherSuite;
|
||||
use opaque_ke::rand::rngs::OsRng;
|
||||
use opaque_ke::{
|
||||
ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters,
|
||||
ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
|
||||
CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
|
||||
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
|
||||
ServerRegistrationLen, ServerSetup,
|
||||
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
||||
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
||||
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
||||
ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
||||
};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
||||
// The ciphersuite trait allows to specify the underlying primitives
|
||||
// that will be used in the OPAQUE protocol
|
||||
// The ciphersuite trait allows to specify the underlying primitives that will
|
||||
// be used in the OPAQUE protocol
|
||||
#[allow(dead_code)]
|
||||
struct Default;
|
||||
|
||||
@@ -191,11 +191,12 @@ fn main() {
|
||||
{
|
||||
println!("\nLogin success!");
|
||||
} else {
|
||||
// Note that at this point, the client knows whether or not the login
|
||||
// succeeded. In this example, we simply rely on client-reported result
|
||||
// of login, but in a real client-server implementation, the server may not
|
||||
// know the outcome of login yet, and extra care must be taken to ensure
|
||||
// that the server can learn the outcome as well.
|
||||
// Note that at this point, the client knows whether or not the
|
||||
// login succeeded. In this example, we simply rely on
|
||||
// client-reported result of login, but in a real client-server
|
||||
// implementation, the server may not know the outcome of login yet,
|
||||
// and extra care must be taken to ensure that the server can learn
|
||||
// the outcome as well.
|
||||
println!("\nIncorrect password, please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Module"
|
||||
license_template_path = ".cargo/license.rs"
|
||||
newline_style = "Unix"
|
||||
unstable_features = true
|
||||
wrap_comments = true
|
||||
+16
-11
@@ -5,19 +5,23 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE
|
||||
//! Defines the CipherSuite trait to specify the underlying primitives for
|
||||
//! OPAQUE
|
||||
|
||||
use crate::hash::ProxyHash;
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::{hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash};
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
||||
use voprf::Group as OprfGroup;
|
||||
|
||||
use crate::hash::{Hash, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::KeyExchange;
|
||||
use crate::slow_hash::SlowHash;
|
||||
|
||||
/// Configures the underlying primitives used in OPAQUE
|
||||
/// * `OprfGroup`: a finite cyclic group along with a point representation, along
|
||||
/// with an extension trait PasswordToCurve that allows some customization on
|
||||
/// how to hash a password to a curve point. See `group::Group`.
|
||||
/// * `OprfGroup`: a finite cyclic group along with a point representation,
|
||||
/// along with an extension trait PasswordToCurve that allows some
|
||||
/// customization on how to hash a password to a curve point. See
|
||||
/// `group::Group`.
|
||||
/// * `KeGroup`: A `Group` used for the `KeyExchange`.
|
||||
/// * `KeyExchange`: The key exchange protocol to use in the login step
|
||||
/// * `Hash`: The main hashing function to use
|
||||
@@ -28,15 +32,16 @@ where
|
||||
<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||
Le<<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||
{
|
||||
/// A finite cyclic group along with a point representation along with
|
||||
/// an extension trait PasswordToCurve that allows some customization on
|
||||
/// how to hash a password to a curve point. See `group::Group`.
|
||||
/// A finite cyclic group along with a point representation along with an
|
||||
/// extension trait PasswordToCurve that allows some customization on how to
|
||||
/// hash a password to a curve point. See `group::Group`.
|
||||
type OprfGroup: OprfGroup;
|
||||
/// A `Group` used for the `KeyExchange`.
|
||||
type KeGroup: KeGroup;
|
||||
/// A key exchange protocol
|
||||
type KeyExchange: KeyExchange<Self::Hash, Self::KeGroup>;
|
||||
/// The main hash function use (for HKDF computations and hashing transcripts)
|
||||
/// The main hash function use (for HKDF computations and hashing
|
||||
/// transcripts)
|
||||
type Hash: Hash;
|
||||
/// A slow hashing function, typically used for password hashing
|
||||
type SlowHash: SlowHash<Self::Hash>;
|
||||
|
||||
+28
-30
@@ -5,31 +5,30 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
errors::{utils::check_slice_size, InternalError, ProtocolError},
|
||||
hash::{Hash, OutputSize, ProxyHash},
|
||||
key_exchange::group::KeGroup,
|
||||
keypair::{KeyPair, PublicKey},
|
||||
opaque::{bytestrings_from_identifiers, Identifiers},
|
||||
serialization::{MacExt, Serialize},
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use digest::Output;
|
||||
use generic_array::{
|
||||
sequence::Concat,
|
||||
typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256, U32},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256, U32};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use voprf::Group;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::errors::utils::check_slice_size;
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::keypair::{KeyPair, PublicKey};
|
||||
use crate::opaque::{bytestrings_from_identifiers, Identifiers};
|
||||
use crate::serialization::{MacExt, Serialize};
|
||||
|
||||
// Constant string used as salt for HKDF computation
|
||||
const STR_AUTH_KEY: [u8; 7] = *b"AuthKey";
|
||||
const STR_EXPORT_KEY: [u8; 9] = *b"ExportKey";
|
||||
@@ -54,16 +53,14 @@ 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>
|
||||
/// This struct is an instantiation of the envelope as described in <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>).
|
||||
/// 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.
|
||||
/// 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>).
|
||||
/// 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>
|
||||
@@ -77,9 +74,10 @@ where
|
||||
hmac: Output<CS::Hash>,
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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<'a, CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as CoreProxy>::Core: ProxyHash,
|
||||
@@ -155,8 +153,8 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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<'a>(
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
@@ -225,8 +223,8 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// 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<'a>(
|
||||
&self,
|
||||
randomized_pwd_hasher: Hkdf<CS::Hash>,
|
||||
|
||||
+5
-4
@@ -140,7 +140,8 @@ pub enum ProtocolError<T = Infallible> {
|
||||
/** This error occurs when the client detects that the server has
|
||||
reflected the OPRF value (beta == alpha) */
|
||||
ReflectedValueError,
|
||||
/// Identity group element was encountered during deserialization, which is invalid
|
||||
/** Identity group element was encountered during deserialization, which is
|
||||
invalid */
|
||||
IdentityGroupElementError,
|
||||
}
|
||||
|
||||
@@ -169,9 +170,9 @@ impl<T> From<InternalError<T>> for ProtocolError<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/rust-lang/rust/issues/64715 and remove this when
|
||||
// merged, and https://github.com/dtolnay/thiserror/issues/62 for why this
|
||||
// comes up in our doc tests.
|
||||
// See https://github.com/rust-lang/rust/issues/64715 and remove this when merged,
|
||||
// and https://github.com/dtolnay/thiserror/issues/62 for why this comes up in our
|
||||
// doc tests.
|
||||
impl<T> From<::core::convert::Infallible> for ProtocolError<T> {
|
||||
fn from(_: ::core::convert::Infallible) -> Self {
|
||||
unreachable!()
|
||||
|
||||
+3
-3
@@ -31,9 +31,9 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC
|
||||
// Associated types could be simplified when they are made as defaults:
|
||||
// https://github.com/rust-lang/rust/issues/29661
|
||||
/// Trait inheriting the requirements from digest::Digest for compatibility with
|
||||
/// HKDF and HMAC Associated types could be simplified when they are made as
|
||||
/// defaults: <https://github.com/rust-lang/rust/issues/29661>
|
||||
pub trait Hash:
|
||||
Digest
|
||||
+ OutputSizeUser<OutputSize = OutputSize<Self>>
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Includes the KeGroup trait and definitions for the
|
||||
//! key exchange groups
|
||||
//! Includes the KeGroup trait and definitions for the key exchange groups
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::errors::InternalError;
|
||||
|
||||
/// A group representation for use in the key exchange
|
||||
pub trait KeGroup: Sized + Clone {
|
||||
/// Length of the public key
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
//! Key Exchange group implementation for p256
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
use generic_array::typenum::{U32, U33};
|
||||
use generic_array::GenericArray;
|
||||
use p256_::elliptic_curve::group::GroupEncoding;
|
||||
@@ -17,6 +15,9 @@ use p256_::elliptic_curve::{PublicKey, SecretKey};
|
||||
use p256_::NistP256;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
|
||||
impl KeGroup for PublicKey<NistP256> {
|
||||
type PkLen = U33;
|
||||
type SkLen = U32;
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
//! Key Exchange group implementation for ristretto255
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
@@ -16,6 +14,9 @@ use generic_array::typenum::U32;
|
||||
use generic_array::GenericArray;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
|
||||
impl KeGroup for RistrettoPoint {
|
||||
type PkLen = U32;
|
||||
type SkLen = U32;
|
||||
@@ -36,7 +37,8 @@ impl KeGroup for RistrettoPoint {
|
||||
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
|
||||
}
|
||||
|
||||
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng
|
||||
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes
|
||||
// from rng
|
||||
#[cfg(test)]
|
||||
{
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
|
||||
//! Key Exchange group implementation for X25519
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
use generic_array::{typenum::U32, GenericArray};
|
||||
use generic_array::typenum::U32;
|
||||
use generic_array::GenericArray;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use x25519_dalek::{PublicKey, StaticSecret};
|
||||
|
||||
use super::KeGroup;
|
||||
use crate::errors::InternalError;
|
||||
|
||||
/// The implementation of such a subgroup for Ristretto
|
||||
impl KeGroup for PublicKey {
|
||||
type PkLen = U32;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Includes instantiations of key exchange protocols used in the
|
||||
//! login step for OPAQUE
|
||||
//! Includes instantiations of key exchange protocols used in the login step for
|
||||
//! OPAQUE
|
||||
|
||||
pub mod group;
|
||||
pub(crate) mod traits;
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::hash::ProxyHash;
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
errors::ProtocolError,
|
||||
hash::Hash,
|
||||
keypair::{PrivateKey, PublicKey, SecretKey},
|
||||
};
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::Output;
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
||||
@@ -20,6 +12,12 @@ use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::errors::ProtocolError;
|
||||
use crate::hash::{Hash, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::keypair::{PrivateKey, PublicKey, SecretKey};
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub type GenerateKe2Result<K, D, G> = (
|
||||
<K as KeyExchange<D, G>>::KE2State,
|
||||
|
||||
@@ -6,34 +6,29 @@
|
||||
// of this source tree.
|
||||
|
||||
//! An implementation of the Triple Diffie-Hellman key exchange protocol
|
||||
use crate::{
|
||||
errors::{
|
||||
utils::{check_slice_size, check_slice_size_atleast},
|
||||
InternalError, ProtocolError,
|
||||
},
|
||||
hash::{Hash, OutputSize, ProxyHash},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes},
|
||||
},
|
||||
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
|
||||
serialization::{Serialize, UpdateExt},
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{Digest, Output};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
||||
use generic_array::{
|
||||
typenum::{Sum, Unsigned, U1, U2, U32},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256, U32};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use hkdf::{Hkdf, HkdfExtract};
|
||||
use hmac::{Hmac, Mac};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::errors::utils::{check_slice_size, check_slice_size_atleast};
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{
|
||||
FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes,
|
||||
};
|
||||
use crate::keypair::{KeyPair, PrivateKey, PublicKey, SecretKey};
|
||||
use crate::serialization::{Serialize, UpdateExt};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
@@ -363,8 +358,9 @@ type TripleDHDerivationResult<D> = (Output<D>, Output<D>, Output<D>, Output<D>);
|
||||
|
||||
// Helper functions
|
||||
|
||||
// Internal function which takes the public and private components of the client and server keypairs, along
|
||||
// with some auxiliary metadata, to produce the session key and two MAC keys
|
||||
// Internal function which takes the public and private components of the client
|
||||
// and server keypairs, along with some auxiliary metadata, to produce the
|
||||
// session key and two MAC keys
|
||||
fn derive_3dh_keys<D: Hash, KG: KeGroup, S: SecretKey<KG>>(
|
||||
dh: TripleDHComponents<KG, S>,
|
||||
hashed_derivation_transcript: &[u8],
|
||||
|
||||
+28
-19
@@ -9,15 +9,17 @@
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use core::ops::Deref;
|
||||
|
||||
use derive_where::DeriveWhere;
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
|
||||
/// A Keypair trait with public-private verification
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
@@ -50,9 +52,9 @@ impl<KG: KeGroup, S: SecretKey<KG>> KeyPair<KG, S> {
|
||||
}
|
||||
|
||||
/// Check whether a public key is valid. This is meant to be applied on
|
||||
/// material provided through the network which fits the key
|
||||
/// representation (i.e. can be mapped to a curve point), but presents
|
||||
/// some risk - e.g. small subgroup check
|
||||
/// material provided through the network which fits the key representation
|
||||
/// (i.e. can be mapped to a curve point), but presents some risk - e.g.
|
||||
/// small subgroup check
|
||||
pub(crate) fn check_public_key(key: PublicKey<KG>) -> Result<PublicKey<KG>, InternalError> {
|
||||
KG::from_pk_slice(GenericArray::from_slice(&key.0)).map(|_| key)
|
||||
}
|
||||
@@ -87,10 +89,11 @@ impl<KG: KeGroup> KeyPair<KG> {
|
||||
/// generate_random
|
||||
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
|
||||
use proptest::prelude::*;
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
|
||||
// The no_shrink is because keypairs should be fixed -- shrinking would cause a different
|
||||
// keypair to be generated, which appears to not be very useful.
|
||||
// The no_shrink is because keypairs should be fixed -- shrinking would cause a
|
||||
// different keypair to be generated, which appears to not be very useful.
|
||||
any::<[u8; 32]>()
|
||||
.prop_filter_map("valid random keypair", |seed| {
|
||||
let mut rng = StdRng::from_seed(seed);
|
||||
@@ -119,7 +122,8 @@ impl<L: ArrayLength<u8>> Deref for Key<L> {
|
||||
}
|
||||
}
|
||||
|
||||
// Don't make it implement SizedBytes so that it's not constructible outside of this module.
|
||||
// Don't make it implement SizedBytes so that it's not constructible outside of
|
||||
// this module.
|
||||
impl<L: ArrayLength<u8>> Key<L> {
|
||||
/// Convert to bytes
|
||||
pub fn to_arr(&self) -> GenericArray<u8, L> {
|
||||
@@ -246,12 +250,14 @@ impl<KG: KeGroup> PublicKey<KG> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::errors::*;
|
||||
use core::slice::from_raw_parts;
|
||||
use std::vec;
|
||||
|
||||
use generic_array::typenum::Unsigned;
|
||||
use rand::rngs::OsRng;
|
||||
use std::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::errors::*;
|
||||
|
||||
#[test]
|
||||
fn test_zeroize_key() -> Result<(), ProtocolError> {
|
||||
@@ -304,10 +310,12 @@ mod tests {
|
||||
macro_rules! test {
|
||||
($mod:ident, $point:ty) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
use std::format;
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn check(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||
@@ -353,6 +361,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn remote_key() {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
use curve25519_dalek::ristretto::RistrettoPoint as KeCurve;
|
||||
#[cfg(not(feature = "ristretto255"))]
|
||||
use p256_::PublicKey as KeCurve;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::{
|
||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
|
||||
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
|
||||
@@ -360,11 +374,6 @@ mod tests {
|
||||
ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
||||
ServerRegistrationStartResult, ServerSetup,
|
||||
};
|
||||
#[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;
|
||||
|
||||
|
||||
+226
-166
@@ -5,10 +5,12 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol
|
||||
//! An implementation of the OPAQUE asymmetric password authentication key
|
||||
//! exchange protocol
|
||||
//!
|
||||
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-05](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-05.html),
|
||||
//! but this specification is subject to change, until the final version published by the IETF.
|
||||
//! but this specification is subject to change, until the final version
|
||||
//! published by the IETF.
|
||||
//!
|
||||
//! ### Minimum Supported Rust Version
|
||||
//!
|
||||
@@ -16,8 +18,9 @@
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives
|
||||
//! to be kept consistent throughout protocol execution. These include:
|
||||
//! OPAQUE is a protocol between a client and a server. They must first agree on
|
||||
//! a collection of primitives to be kept consistent throughout protocol
|
||||
//! execution. These include:
|
||||
//! * a finite cyclic group along with a point representation
|
||||
//! * for the OPRF and
|
||||
//! * for the key exchange
|
||||
@@ -40,12 +43,15 @@
|
||||
//! See [examples/simple_login.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/simple_login.rs)
|
||||
//! for a working example of a simple password-based login using OPAQUE.
|
||||
//!
|
||||
//! Note that our choice of slow hashing function in this example, `NoOpHash`, is selected only to ensure
|
||||
//! that the tests execute quickly. A real application should use an actual slow hashing function, such as `Argon2`,
|
||||
//! which can be enabled through the `slow-hash` feature. See more details in the [features](#features) section.
|
||||
//! Note that our choice of slow hashing function in this example, `NoOpHash`,
|
||||
//! is selected only to ensure that the tests execute quickly. A real
|
||||
//! application should use an actual slow hashing function, such as `Argon2`,
|
||||
//! which can be enabled through the `slow-hash` feature. See more details in
|
||||
//! the [features](#features) section.
|
||||
//!
|
||||
//! ## Setup
|
||||
//! To set up the protocol, the server begins by creating a `ServerSetup` object:
|
||||
//! To set up the protocol, the server begins by creating a `ServerSetup`
|
||||
//! object:
|
||||
//! ```
|
||||
//! # use opaque_ke::errors::ProtocolError;
|
||||
//! # use opaque_ke::CipherSuite;
|
||||
@@ -67,24 +73,33 @@
|
||||
//! # type Hash = sha2::Sha256;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! let mut rng = OsRng;
|
||||
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! The server must persist an instance of [ServerSetup] for the registration and login steps.
|
||||
//! The server must persist an instance of [ServerSetup] for the registration
|
||||
//! and login steps.
|
||||
//!
|
||||
//! ## Registration
|
||||
//! The registration protocol between the client and server consists of four steps along with three messages:
|
||||
//! [RegistrationRequest], [RegistrationResponse], and [RegistrationUpload]. A successful execution of the registration protocol results in the
|
||||
//! server producing a password file corresponding to a server-side identifier for the client, along with the password provided by
|
||||
//! the client. This password file is typically stored in a key-value database, where the keys consist of these server-side identifiers for each client,
|
||||
//! and the values consist of their corresponding password files, to be retrieved upon future login attempts made by the client.
|
||||
//! The registration protocol between the client and server consists of four
|
||||
//! steps along with three messages: [RegistrationRequest],
|
||||
//! [RegistrationResponse], and [RegistrationUpload]. A successful execution of
|
||||
//! the registration protocol results in the server producing a password file
|
||||
//! corresponding to a server-side identifier for the client, along with the
|
||||
//! password provided by the client. This password file is typically stored in a
|
||||
//! key-value database, where the keys consist of these server-side identifiers
|
||||
//! for each client, and the values consist of their corresponding password
|
||||
//! files, to be retrieved upon future login attempts made by the client.
|
||||
//!
|
||||
//! ### Client Registration Start
|
||||
//! In the first step of registration, the client chooses as input a registration password. The client runs [ClientRegistration::start]
|
||||
//! to produce a [ClientRegistrationStartResult], which consists of a [RegistrationRequest] to be sent to the server and
|
||||
//! a [ClientRegistration] which must be persisted on the client for the final step of client registration.
|
||||
//! In the first step of registration, the client chooses as input a
|
||||
//! registration password. The client runs [ClientRegistration::start] to
|
||||
//! produce a [ClientRegistrationStartResult], which consists of a
|
||||
//! [RegistrationRequest] to be sent to the server and a [ClientRegistration]
|
||||
//! which must be persisted on the client for the final step of client
|
||||
//! registration.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -110,20 +125,20 @@
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
//! # }
|
||||
//! use opaque_ke::ClientRegistration;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||
//! &mut client_rng,
|
||||
//! b"password",
|
||||
//! )?;
|
||||
//! let client_registration_start_result =
|
||||
//! ClientRegistration::<Default>::start(&mut client_rng, b"password")?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Registration Start
|
||||
//! In the second step of registration, the server takes as input a persisted instance of [ServerSetup], a [RegistrationRequest] from the client, and
|
||||
//! a server-side identifier for the client.
|
||||
//! The server runs [ServerRegistration::start] to produce a [ServerRegistrationStartResult], which consists of
|
||||
//! a [RegistrationResponse] to be returned to the client.
|
||||
//! In the second step of registration, the server takes as input a persisted
|
||||
//! instance of [ServerSetup], a [RegistrationRequest] from the client, and a
|
||||
//! server-side identifier for the client. The server runs
|
||||
//! [ServerRegistration::start] to produce a [ServerRegistrationStartResult],
|
||||
//! which consists of a [RegistrationResponse] to be returned to the client.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -167,11 +182,13 @@
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Registration Finish
|
||||
//! In the third step of registration, the client takes as input
|
||||
//! a [RegistrationResponse] from the server, and
|
||||
//! a [ClientRegistration] from the first step of registration.
|
||||
//! The client runs [ClientRegistration::finish] to produce a [ClientRegistrationFinishResult], which consists of a [RegistrationUpload]
|
||||
//! to be sent to the server and an `export_key` field which can be used optionally as described in the [Export Key](#export-key) section.
|
||||
//! In the third step of registration, the client takes as input a
|
||||
//! [RegistrationResponse] from the server, and a [ClientRegistration] from the
|
||||
//! first step of registration. The client runs [ClientRegistration::finish] to
|
||||
//! produce a [ClientRegistrationFinishResult], which consists of a
|
||||
//! [RegistrationUpload] to be sent to the server and an `export_key` field
|
||||
//! which can be used optionally as described in the [Export Key](#export-key)
|
||||
//! section.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -215,12 +232,13 @@
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Registration Finish
|
||||
//! In the fourth step of registration, the server takes as input
|
||||
//! a [RegistrationUpload] from the client, and
|
||||
//! a [ServerRegistration] from the second step.
|
||||
//! The server runs [ServerRegistration::finish] to produce a finalized [ServerRegistration].
|
||||
//! At this point, the client can be considered as successfully registered, and the server can invoke
|
||||
//! [ServerRegistration::serialize] to store the password file for use during the login protocol.
|
||||
//! In the fourth step of registration, the server takes as input a
|
||||
//! [RegistrationUpload] from the client, and a [ServerRegistration] from the
|
||||
//! second step. The server runs [ServerRegistration::finish] to produce a
|
||||
//! finalized [ServerRegistration]. At this point, the client can be considered
|
||||
//! as successfully registered, and the server can invoke
|
||||
//! [ServerRegistration::serialize] to store the password file for use during
|
||||
//! the login protocol.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -262,17 +280,20 @@
|
||||
//! ```
|
||||
//!
|
||||
//! ## Login
|
||||
//! The login protocol between a client and server also consists of four steps along with three messages:
|
||||
//! [CredentialRequest], [CredentialResponse], [CredentialFinalization]. The server is expected to have access to the password file
|
||||
//! corresponding to an output of the registration phase (see [Dummy Server Login](#dummy-server-login) for handling the scenario where
|
||||
//! no password file is available). The login protocol will execute successfully only if the same password
|
||||
//! was used in the registration phase that produced the password file that the server is testing against.
|
||||
//! The login protocol between a client and server also consists of four steps
|
||||
//! along with three messages: [CredentialRequest], [CredentialResponse],
|
||||
//! [CredentialFinalization]. The server is expected to have access to the
|
||||
//! password file corresponding to an output of the registration phase (see
|
||||
//! [Dummy Server Login](#dummy-server-login) for handling the scenario where no
|
||||
//! password file is available). The login protocol will execute successfully
|
||||
//! only if the same password was used in the registration phase that produced
|
||||
//! the password file that the server is testing against.
|
||||
//!
|
||||
//! ### Client Login Start
|
||||
//! In the first step of login, the client chooses as input a login password.
|
||||
//! The client runs [ClientLogin::start] to produce an output consisting of
|
||||
//! a [CredentialRequest] to be sent to the server, and
|
||||
//! a [ClientLogin] which must be persisted on the client for the final step of client login.
|
||||
//! The client runs [ClientLogin::start] to produce an output consisting of a
|
||||
//! [CredentialRequest] to be sent to the server, and a [ClientLogin] which must
|
||||
//! be persisted on the client for the final step of client login.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -300,22 +321,18 @@
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! use opaque_ke::ClientLogin;
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_login_start_result = ClientLogin::<Default>::start(
|
||||
//! &mut client_rng,
|
||||
//! b"password",
|
||||
//! )?;
|
||||
//! let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"password")?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Login Start
|
||||
//! In the second step of login, the server takes as input
|
||||
//! a persisted instance of [ServerSetup],
|
||||
//! the password file output from registration,
|
||||
//! a [CredentialRequest] from the client, and
|
||||
//! a server-side identifier for the client.
|
||||
//! The server runs [ServerLogin::start] to produce an output consisting of
|
||||
//! a [CredentialResponse] which is returned to the client, and
|
||||
//! a [ServerLogin] which must be persisted on the server for the final step of login.
|
||||
//! In the second step of login, the server takes as input a persisted instance
|
||||
//! of [ServerSetup], the password file output from registration, a
|
||||
//! [CredentialRequest] from the client, and a server-side identifier for the
|
||||
//! client. The server runs [ServerLogin::start] to produce an output consisting
|
||||
//! of a [CredentialResponse] which is returned to the client, and a
|
||||
//! [ServerLogin] which must be persisted on the server for the final step of
|
||||
//! login.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -368,17 +385,19 @@
|
||||
//! )?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! Note that if there is no corresponding password file found for the user,
|
||||
//! the server can use `None` in place of `Some(password_file)` in order to generate
|
||||
//! a [CredentialResponse] that is indistinguishable from a valid [CredentialResponse]
|
||||
//! returned for a registered client. This allows the server to prevent leaking information
|
||||
//! about whether or not a client has previously registered with the server.
|
||||
//! Note that if there is no corresponding password file found for the user, the
|
||||
//! server can use `None` in place of `Some(password_file)` in order to generate
|
||||
//! a [CredentialResponse] that is indistinguishable from a valid
|
||||
//! [CredentialResponse] returned for a registered client. This allows the
|
||||
//! server to prevent leaking information about whether or not a client has
|
||||
//! previously registered with the server.
|
||||
//!
|
||||
//! ### Client Login Finish
|
||||
//! In the third step of login, the client takes as input a [CredentialResponse] from the server.
|
||||
//! The client runs [ClientLogin::finish] and produces an output consisting of
|
||||
//! a [CredentialFinalization] to be sent to the server to complete the protocol,
|
||||
//! the `session_key` sequence of bytes which will match the server's session key upon a successful login.
|
||||
//! In the third step of login, the client takes as input a [CredentialResponse]
|
||||
//! from the server. The client runs [ClientLogin::finish] and produces an
|
||||
//! output consisting of a [CredentialFinalization] to be sent to the server to
|
||||
//! complete the protocol, the `session_key` sequence of bytes which will match
|
||||
//! the server's session key upon a successful login.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -433,8 +452,10 @@
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Login Finish
|
||||
//! In the fourth step of login, the server takes as input a [CredentialFinalization] from the client and runs [ServerLogin::finish] to
|
||||
//! produce an output consisting of the `session_key` sequence of bytes which will match the client's session key upon a successful login.
|
||||
//! In the fourth step of login, the server takes as input a
|
||||
//! [CredentialFinalization] from the client and runs [ServerLogin::finish] to
|
||||
//! produce an output consisting of the `session_key` sequence of bytes which
|
||||
//! will match the client's session key upon a successful login.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -495,31 +516,43 @@
|
||||
//! );
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! If the protocol completes successfully, then the server obtains a `server_login_finish_result.session_key` which is guaranteed to
|
||||
//! match `client_login_finish_result.session_key` (see the [Session Key](#session-key) section).
|
||||
//! Otherwise, on failure, the [ServerLogin::finish] algorithm outputs the error [InvalidLoginError](errors::ProtocolError::InvalidLoginError).
|
||||
//! If the protocol completes successfully, then the server obtains a
|
||||
//! `server_login_finish_result.session_key` which is guaranteed to match
|
||||
//! `client_login_finish_result.session_key` (see the [Session
|
||||
//! Key](#session-key) section). Otherwise, on failure, the
|
||||
//! [ServerLogin::finish] algorithm outputs the error
|
||||
//! [InvalidLoginError](errors::ProtocolError::InvalidLoginError).
|
||||
//!
|
||||
//! # Advanced Usage
|
||||
//!
|
||||
//! This implementation offers support for several optional features of OPAQUE, described below. They are not critical to the
|
||||
//! execution of the main protocol, but can provide additional security benefits which can be suitable for various applications that rely on
|
||||
//! OPAQUE for authentication.
|
||||
//! This implementation offers support for several optional features of OPAQUE,
|
||||
//! described below. They are not critical to the execution of the main
|
||||
//! protocol, but can provide additional security benefits which can be suitable
|
||||
//! for various applications that rely on OPAQUE for authentication.
|
||||
//!
|
||||
//! ## Session Key
|
||||
//!
|
||||
//! Upon a successful completion of the OPAQUE protocol (the client runs login with the same password used during registration),
|
||||
//! the client and server have access to a session key, which is a pseudorandomly distributed 32-byte string which only the client
|
||||
//! and server know. Multiple login runs using the same password for the same client will produce different session keys, distributed
|
||||
//! as uniformly random strings. Thus, the session key can be used to establish a secure channel between the client and server.
|
||||
//! Upon a successful completion of the OPAQUE protocol (the client runs login
|
||||
//! with the same password used during registration), the client and server have
|
||||
//! access to a session key, which is a pseudorandomly distributed 32-byte
|
||||
//! string which only the client and server know. Multiple login runs using the
|
||||
//! same password for the same client will produce different session keys,
|
||||
//! distributed as uniformly random strings. Thus, the session key can be used
|
||||
//! to establish a secure channel between the client and server.
|
||||
//!
|
||||
//! The session key can be accessed from the `session_key` field of [ClientLoginFinishResult] and [ServerLoginFinishResult]. See
|
||||
//! the combination of [Client Login Finish](#client-login-finish) and [Server Login Finish](#server-login-finish) for example usage.
|
||||
//! The session key can be accessed from the `session_key` field of
|
||||
//! [ClientLoginFinishResult] and [ServerLoginFinishResult]. See the combination
|
||||
//! of [Client Login Finish](#client-login-finish) and [Server Login
|
||||
//! Finish](#server-login-finish) for example usage.
|
||||
//!
|
||||
//! ## Checking Server Consistency
|
||||
//!
|
||||
//! A [ClientLoginFinishResult] contains the `server_s_pk` field, which is represents the static public key of the server that is established
|
||||
//! during the setup phase. This can be used by the client to verify the authenticity of the server it engages with during the login phase. In particular,
|
||||
//! the client can check that the static public key of the server supplied during registration (with the `server_s_pk` field of
|
||||
//! A [ClientLoginFinishResult] contains the `server_s_pk` field, which is
|
||||
//! represents the static public key of the server that is established during
|
||||
//! the setup phase. This can be used by the client to verify the authenticity
|
||||
//! of the server it engages with during the login phase. In particular, the
|
||||
//! client can check that the static public key of the server supplied during
|
||||
//! registration (with the `server_s_pk` field of
|
||||
//! [ClientRegistrationFinishResult]) matches this field during login.
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
@@ -590,26 +623,36 @@
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! Note that without this check over the consistency of the server's static public key, a malicious actor could impersonate the registration server if it were able to copy the password
|
||||
//! file output during registration! Therefore, it is recommended to perform the following check in the application layer if the client can obtain a copy of the server's static
|
||||
//! Note that without this check over the consistency of the server's static
|
||||
//! public key, a malicious actor could impersonate the registration server if
|
||||
//! it were able to copy the password file output during registration!
|
||||
//! Therefore, it is recommended to perform the following check in the
|
||||
//! application layer if the client can obtain a copy of the server's static
|
||||
//! public key beforehand.
|
||||
//!
|
||||
//!
|
||||
//! ## Export Key
|
||||
//!
|
||||
//! The export key is a pseudorandomly distributed 32-byte string output by both the
|
||||
//! [Client Registration Finish](#client-registration-finish) and [Client Login Finish](#client-login-finish) steps.
|
||||
//! The same export key string will be output by both functions only if the exact same password is passed to [ClientRegistration::start] and [ClientLogin::start].
|
||||
//! The export key is a pseudorandomly distributed 32-byte string output by both
|
||||
//! the [Client Registration Finish](#client-registration-finish) and [Client
|
||||
//! Login Finish](#client-login-finish) steps. The same export key string will
|
||||
//! be output by both functions only if the exact same password is passed to
|
||||
//! [ClientRegistration::start] and [ClientLogin::start].
|
||||
//!
|
||||
//! The export key retains as much secrecy as the password itself, and is similarly derived through an evaluation of the slow hashing function. Hence, only the parties which
|
||||
//! know the password the client uses during registration and login can recover this secret, as it is never exposed to the server. As a result, the export key
|
||||
//! can be used (separately from the OPAQUE protocol) to provide confidentiality and integrity to other data which only the client should be able to process.
|
||||
//! For instance, if the server is expected to maintain any client-side secrets which require a password to access, then this export key can be used to encrypt
|
||||
//! these secrets so that they remain hidden from the server (see [examples/digital_locker.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/digital_locker.rs)
|
||||
//! The export key retains as much secrecy as the password itself, and is
|
||||
//! similarly derived through an evaluation of the slow hashing function. Hence,
|
||||
//! only the parties which know the password the client uses during registration
|
||||
//! and login can recover this secret, as it is never exposed to the server. As
|
||||
//! a result, the export key can be used (separately from the OPAQUE protocol)
|
||||
//! to provide confidentiality and integrity to other data which only the client
|
||||
//! should be able to process. For instance, if the server is expected to
|
||||
//! maintain any client-side secrets which require a password to access, then
|
||||
//! this export key can be used to encrypt these secrets so that they remain
|
||||
//! hidden from the server (see [examples/digital_locker.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/digital_locker.rs)
|
||||
//! for a working example).
|
||||
//!
|
||||
//! You can access the export key from the `export_key` field of [ClientRegistrationFinishResult] and [ClientLoginFinishResult].
|
||||
//!
|
||||
//! You can access the export key from the `export_key` field of
|
||||
//! [ClientRegistrationFinishResult] and [ClientLoginFinishResult].
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -678,15 +721,21 @@
|
||||
//!
|
||||
//! ## Custom Identifiers
|
||||
//!
|
||||
//! Typically when applications use OPAQUE to authenticate a client to a server, the client has a registered username which is sent to the server to
|
||||
//! identify the corresponding password file established during registration. This username may or may not coincide with the server-side identifier;
|
||||
//! however, this username must be known to both the client and the server (whereas the server-side identifier does not need to be exposed to the client).
|
||||
//! The server may also have an identifier corresponding to an entity (e.g. Facebook).
|
||||
//! By default, neither of these public identifiers need to be supplied to the OPAQUE protocol.
|
||||
//! Typically when applications use OPAQUE to authenticate a client to a server,
|
||||
//! the client has a registered username which is sent to the server to identify
|
||||
//! the corresponding password file established during registration. This
|
||||
//! username may or may not coincide with the server-side identifier; however,
|
||||
//! this username must be known to both the client and the server (whereas the
|
||||
//! server-side identifier does not need to be exposed to the client). The
|
||||
//! server may also have an identifier corresponding to an entity (e.g.
|
||||
//! Facebook). By default, neither of these public identifiers need to be
|
||||
//! supplied to the OPAQUE protocol.
|
||||
//!
|
||||
//! But, for applications that wish to cryptographically bind these identities to
|
||||
//! the registered password file as well as the session key output by the login phase, these custom identifiers can be specified through
|
||||
//! [ClientRegistrationFinishParameters] in [Client Registration Finish](#client-registration-finish):
|
||||
//! But, for applications that wish to cryptographically bind these identities
|
||||
//! to the registered password file as well as the session key output by the
|
||||
//! login phase, these custom identifiers can be specified through
|
||||
//! [ClientRegistrationFinishParameters] in [Client Registration
|
||||
//! Finish](#client-registration-finish):
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -735,7 +784,8 @@
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! The same identifiers must also be supplied using [ServerLoginStartParameters] 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,
|
||||
@@ -795,7 +845,8 @@
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//!
|
||||
//! as well as [ClientLoginFinishParameters] in [Client Login Finish](#client-login-finish):
|
||||
//! as well as [ClientLoginFinishParameters] in [Client Login
|
||||
//! Finish](#client-login-finish):
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -856,36 +907,46 @@
|
||||
//!
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! 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!
|
||||
//! 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] can be
|
||||
//! used to specify them individually.
|
||||
//! 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
|
||||
//!
|
||||
//! 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], and
|
||||
//! - The third login message, where the client can populate [ClientLoginFinishParameters].
|
||||
//! 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], 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
|
||||
//! [custom identifiers](#custom-identifiers), with the ordering of the fields as
|
||||
//! `WithContextAndIdentifiers(context, Identifiers::ClientAndServerIdentifiers(username, server_name))`.
|
||||
//! For both of these messages, the `WithContextAndIdentifiers` variant can be
|
||||
//! used to specify these fields in addition to [custom
|
||||
//! identifiers](#custom-identifiers), with the ordering of the fields as
|
||||
//! `WithContextAndIdentifiers(context,
|
||||
//! Identifiers::ClientAndServerIdentifiers(username, server_name))`.
|
||||
//!
|
||||
//! ## Dummy Server Login
|
||||
//!
|
||||
//! For applications in which the server does not wish to reveal to the client whether an existing password file has been
|
||||
//! registered, the server can return a "dummy" credential response message to the client for an unregistered client,
|
||||
//! which is indistinguishable from the normal credential response message that the server would return for a registered client.
|
||||
//! The dummy message is created by passing a `None` to the password_file parameter for [ServerLogin::start].
|
||||
//! For applications in which the server does not wish to reveal to the client
|
||||
//! whether an existing password file has been registered, the server can return
|
||||
//! a "dummy" credential response message to the client for an unregistered
|
||||
//! client, which is indistinguishable from the normal credential response
|
||||
//! message that the server would return for a registered client. The dummy
|
||||
//! message is created by passing a `None` to the password_file parameter for
|
||||
//! [ServerLogin::start].
|
||||
//!
|
||||
//! ## Remote Private Keys
|
||||
//!
|
||||
//! Servers that want to store their private key in an external location (e.g. in an HSM or vault) can do so with the
|
||||
//! [`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.
|
||||
//! Servers that want to store their private key in an external location (e.g.
|
||||
//! in an HSM or vault) can do so with the [`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 generic_array::{GenericArray, typenum::U0};
|
||||
//! # use opaque_ke::{CipherSuite, errors::{InternalError}, key_exchange::group::KeGroup, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup};
|
||||
@@ -952,36 +1013,44 @@
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - The `slow-hash` feature, when enabled, introduces a dependency on `argon2` and implements the `SlowHash` trait for `Argon2`
|
||||
//! with a set of default parameters. In general, secure instantiations should choose to invoke a memory-hard password
|
||||
//! hashing function when the client's password is expected to have low entropy, instead of relying on [slow_hash::NoOpHash]
|
||||
//! as done in the above example. The more computationally intensive the `SlowHash` function is, the more resistant the server's
|
||||
//! 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 `slow-hash` feature, when enabled, introduces a dependency on `argon2`
|
||||
//! and implements the `SlowHash` trait for `Argon2` with a set of default parameters.
|
||||
//! In general, secure instantiations should choose to invoke a memory-hard password
|
||||
//! hashing function when the client's password is expected to have low entropy,
|
||||
//! instead of relying on [slow_hash::NoOpHash] as done in the above example. The
|
||||
//! more computationally intensive the `SlowHash` function is, the more resistant
|
||||
//! the server's 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 `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! [serde](https://serde.rs/).
|
||||
//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with [serde](https://serde.rs/).
|
||||
//!
|
||||
//! - 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
|
||||
//! - 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
|
||||
//! enables the use of `curve25519_dalek::ristretto::RistrettoPoint` as a
|
||||
//! `KeGroup` and `OprfGroup`.
|
||||
//!
|
||||
//! - 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 `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 `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.
|
||||
//! - 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.
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
#![no_std]
|
||||
@@ -1011,27 +1080,18 @@ mod tests;
|
||||
|
||||
// Exports
|
||||
|
||||
pub use ciphersuite::CipherSuite;
|
||||
pub use rand;
|
||||
|
||||
pub use ciphersuite::CipherSuite;
|
||||
|
||||
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,
|
||||
CredentialFinalization, CredentialFinalizationLen, CredentialRequest, CredentialRequestLen,
|
||||
CredentialResponse, CredentialResponseLen, RegistrationRequest, RegistrationRequestLen,
|
||||
RegistrationResponse, RegistrationResponseLen, RegistrationUpload, RegistrationUploadLen,
|
||||
};
|
||||
pub use crate::opaque::{
|
||||
ClientLoginFinishParameters, ClientRegistrationFinishParameters, ServerLoginStartParameters,
|
||||
};
|
||||
pub use crate::opaque::{
|
||||
ClientLoginFinishResult, ClientLoginStartResult, ClientRegistrationFinishResult,
|
||||
ClientRegistrationStartResult, Identifiers, ServerLoginFinishResult, ServerLoginStartResult,
|
||||
ServerRegistrationStartResult,
|
||||
ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
|
||||
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
|
||||
ClientRegistrationStartResult, Identifiers, ServerLogin, ServerLoginFinishResult,
|
||||
ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration, ServerRegistrationLen,
|
||||
ServerRegistrationStartResult, ServerSetup,
|
||||
};
|
||||
|
||||
+30
-34
@@ -7,34 +7,30 @@
|
||||
|
||||
//! Contains the messages used for OPAQUE
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::{Envelope, EnvelopeLen},
|
||||
errors::{
|
||||
utils::{check_slice_size, check_slice_size_atleast},
|
||||
ProtocolError,
|
||||
},
|
||||
hash::{OutputSize, ProxyHash},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes},
|
||||
tripledh::NonceLen,
|
||||
},
|
||||
keypair::{KeyPair, PublicKey, SecretKey},
|
||||
opaque::{MaskedResponse, MaskedResponseLen, ServerSetup},
|
||||
};
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use digest::Output;
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::{
|
||||
typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use voprf::Group;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::envelope::{Envelope, EnvelopeLen};
|
||||
use crate::errors::utils::{check_slice_size, check_slice_size_atleast};
|
||||
use crate::errors::ProtocolError;
|
||||
use crate::hash::{OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{
|
||||
FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes,
|
||||
};
|
||||
use crate::key_exchange::tripledh::NonceLen;
|
||||
use crate::keypair::{KeyPair, PublicKey, SecretKey};
|
||||
use crate::opaque::{MaskedResponse, MaskedResponseLen, ServerSetup};
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
@@ -91,8 +87,8 @@ where
|
||||
<<CS::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||
Le<<<CS::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||
{
|
||||
/// The "envelope" generated by the user, containing sealed
|
||||
/// cryptographic identifiers
|
||||
/// The "envelope" generated by the user, containing sealed cryptographic
|
||||
/// identifiers
|
||||
pub(crate) envelope: Envelope<CS>,
|
||||
/// The masking key used to mask the envelope
|
||||
pub(crate) masking_key: Output<CS::Hash>,
|
||||
@@ -139,8 +135,8 @@ impl_serialize_and_deserialize_for!(
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// login attempt
|
||||
/// The answer sent by the server to the user, upon reception of the login
|
||||
/// attempt
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(
|
||||
@@ -179,8 +175,8 @@ impl_serialize_and_deserialize_for!(
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
);
|
||||
|
||||
/// The answer sent by the client to the server, upon reception of the
|
||||
/// sealed envelope
|
||||
/// The answer sent by the client to the server, upon reception of the sealed
|
||||
/// envelope
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
#[derive_where(
|
||||
@@ -275,8 +271,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Only used for tests, where we can set the beta value to test for the reflection
|
||||
/// error case
|
||||
/// Only used for tests, where we can set the beta value to test for the
|
||||
/// reflection error case
|
||||
pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self {
|
||||
Self {
|
||||
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
||||
@@ -383,8 +379,8 @@ where
|
||||
|
||||
let checked_slice = check_slice_size_atleast(input, elem_len, "login_first_message_bytes")?;
|
||||
|
||||
// Check that the message is actually containing an element of the
|
||||
// correct subgroup
|
||||
// Check that the message is actually containing an element of the correct
|
||||
// subgroup
|
||||
let blinded_element = voprf::BlindedElement::<CS::OprfGroup, CS::Hash>::deserialize(
|
||||
&checked_slice[..elem_len],
|
||||
)?;
|
||||
@@ -476,8 +472,8 @@ where
|
||||
"credential_response_bytes",
|
||||
)?;
|
||||
|
||||
// Check that the message is actually containing an element of the
|
||||
// correct subgroup
|
||||
// Check that the message is actually containing an element of the correct
|
||||
// subgroup
|
||||
let beta_bytes = &checked_slice[..elem_len];
|
||||
let evaluation_element =
|
||||
voprf::EvaluationElement::<CS::OprfGroup, CS::Hash>::deserialize(beta_bytes)?;
|
||||
@@ -506,8 +502,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Only used for tests, where we can set the beta value to test for the reflection
|
||||
/// error case
|
||||
/// Only used for tests, where we can set the beta value to test for the
|
||||
/// reflection error case
|
||||
pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self {
|
||||
Self {
|
||||
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
||||
|
||||
+51
-43
@@ -7,38 +7,39 @@
|
||||
|
||||
//! Provides the main OPAQUE API
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::{Envelope, EnvelopeLen},
|
||||
errors::{utils::check_slice_size, InternalError, ProtocolError},
|
||||
hash::{Hash, OutputSize, ProxyHash},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{FromBytes, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, ToBytes},
|
||||
tripledh::NonceLen,
|
||||
},
|
||||
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
|
||||
messages::{CredentialRequestLen, RegistrationUploadLen},
|
||||
serialization::Serialize,
|
||||
slow_hash::SlowHash,
|
||||
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
||||
RegistrationResponse, RegistrationUpload,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::DeriveWhere;
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use digest::Output;
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::{
|
||||
typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use hkdf::{Hkdf, HkdfExtract};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
use voprf::Group;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::envelope::{Envelope, EnvelopeLen};
|
||||
use crate::errors::utils::check_slice_size;
|
||||
use crate::errors::{InternalError, ProtocolError};
|
||||
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{
|
||||
FromBytes, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, ToBytes,
|
||||
};
|
||||
use crate::key_exchange::tripledh::NonceLen;
|
||||
use crate::keypair::{KeyPair, PrivateKey, PublicKey, SecretKey};
|
||||
use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
|
||||
use crate::serialization::Serialize;
|
||||
use crate::slow_hash::SlowHash;
|
||||
use crate::{
|
||||
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
||||
RegistrationResponse, RegistrationUpload,
|
||||
};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
@@ -311,7 +312,8 @@ where
|
||||
.concat()
|
||||
}
|
||||
|
||||
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
|
||||
/// Returns an initial "blinded" request to send to the server, as well as a
|
||||
/// ClientRegistration
|
||||
pub fn start<R: RngCore + CryptoRng>(
|
||||
blinding_factor_rng: &mut R,
|
||||
password: &[u8],
|
||||
@@ -330,7 +332,8 @@ where
|
||||
}
|
||||
|
||||
/// "Unblinds" the server's answer and returns a final message containing
|
||||
/// cryptographic identifiers, to be sent to the server on setup finalization
|
||||
/// cryptographic identifiers, to be sent to the server on setup
|
||||
/// finalization
|
||||
pub fn finish<R: CryptoRng + RngCore>(
|
||||
self,
|
||||
rng: &mut R,
|
||||
@@ -416,8 +419,8 @@ where
|
||||
Ok(Self(RegistrationUpload::deserialize(input)?))
|
||||
}
|
||||
|
||||
/// From the client's "blinded" password, returns a response to be
|
||||
/// sent back to the client, as well as a ServerRegistration
|
||||
/// From the client's "blinded" password, returns a response to be sent back
|
||||
/// to the client, as well as a ServerRegistration
|
||||
pub fn start<S: SecretKey<CS::KeGroup>>(
|
||||
server_setup: &ServerSetup<CS, S>,
|
||||
message: RegistrationRequest<CS>,
|
||||
@@ -441,8 +444,8 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// From the client's cryptographic identifiers, fully populates and
|
||||
/// returns a ServerRegistration
|
||||
/// From the client's cryptographic identifiers, fully populates and returns
|
||||
/// a ServerRegistration
|
||||
pub fn finish(message: RegistrationUpload<CS>) -> Self {
|
||||
Self(message)
|
||||
}
|
||||
@@ -530,7 +533,8 @@ where
|
||||
<<CS::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||
Le<<<CS::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||
{
|
||||
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
||||
/// Returns an initial "blinded" password request to send to the server, as
|
||||
/// well as a ClientLogin
|
||||
pub fn start<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
password: &[u8],
|
||||
@@ -553,8 +557,8 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// "Unblinds" the server's answer and returns the opened assets from
|
||||
/// the server
|
||||
/// "Unblinds" the server's answer and returns the opened assets from the
|
||||
/// server
|
||||
pub fn finish(
|
||||
self,
|
||||
password: &[u8],
|
||||
@@ -676,8 +680,8 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// From the client's "blinded" password, returns a challenge to be
|
||||
/// sent back to the client, as well as a ServerLogin
|
||||
/// From the client's "blinded" password, returns a challenge to be sent
|
||||
/// back to the client, as well as a ServerLogin
|
||||
pub fn start<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
|
||||
rng: &mut R,
|
||||
server_setup: &ServerSetup<CS, S>,
|
||||
@@ -859,7 +863,8 @@ where
|
||||
{
|
||||
/// The registration request message to be sent to the server
|
||||
pub message: RegistrationRequest<CS>,
|
||||
/// The client state that must be persisted in order to complete registration
|
||||
/// The client state that must be persisted in order to complete
|
||||
/// registration
|
||||
pub state: ClientRegistration<CS>,
|
||||
}
|
||||
|
||||
@@ -878,7 +883,8 @@ where
|
||||
pub export_key: Output<CS::Hash>,
|
||||
/// The server's static public key
|
||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
/// Instance of the ClientRegistration, only used in tests for checking
|
||||
/// zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ClientRegistration<CS>,
|
||||
/// AuthKey, only used in tests
|
||||
@@ -889,8 +895,8 @@ where
|
||||
pub randomized_pwd: Output<CS::Hash>,
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server registration start.
|
||||
/// Note that there is no state output in this step
|
||||
/// Contains the fields that are returned by a server registration start. Note
|
||||
/// that there is no state output in this step
|
||||
#[derive(DeriveWhere)]
|
||||
#[derive_where(Clone)]
|
||||
pub struct ServerRegistrationStartResult<CS: CipherSuite>
|
||||
@@ -932,7 +938,8 @@ where
|
||||
{
|
||||
/// Specifying a context field that the server must agree on
|
||||
pub context: Option<&'c [u8]>,
|
||||
/// Specifying a user identifier and server identifier that will be matched against the server
|
||||
/// Specifying a user identifier and server identifier that will be matched
|
||||
/// against the server
|
||||
pub identifiers: Identifiers<'i>,
|
||||
/// Specifying a configuration for the slow hash
|
||||
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||
@@ -1000,7 +1007,8 @@ where
|
||||
/// The session key between client and server
|
||||
pub session_key: Output<CS::Hash>,
|
||||
_cs: PhantomData<CS>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
/// Instance of the ClientRegistration, only used in tests for checking
|
||||
/// zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ServerLogin<CS>,
|
||||
}
|
||||
@@ -1010,7 +1018,8 @@ where
|
||||
pub struct ServerLoginStartParameters<'c, 'i> {
|
||||
/// Specifying a context field that the client must agree on
|
||||
pub context: Option<&'c [u8]>,
|
||||
/// Specifying a user identifier and server identifier that will be matched against the client
|
||||
/// Specifying a user identifier and server identifier that will be matched
|
||||
/// against the client
|
||||
pub identifiers: Identifiers<'i>,
|
||||
}
|
||||
|
||||
@@ -1242,10 +1251,9 @@ pub(crate) fn bytestrings_from_identifiers<KG: KeGroup>(
|
||||
Ok((client_identity, server_identity))
|
||||
}
|
||||
|
||||
/// Internal function for computing the blind result by calling the
|
||||
/// voprf library. Note that for tests, we use the deterministic blinding
|
||||
/// in order to be able to set the blinding factor directly from the passed-in
|
||||
/// rng.
|
||||
/// Internal function for computing the blind result by calling the voprf
|
||||
/// library. Note that for tests, we use the deterministic blinding in order to
|
||||
/// be able to set the blinding factor directly from the passed-in rng.
|
||||
fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
password: &[u8],
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::errors::ProtocolError;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use digest::Update;
|
||||
use generic_array::{
|
||||
typenum::{U0, U2},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use generic_array::typenum::{U0, U2};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use hmac::Mac;
|
||||
|
||||
use crate::errors::ProtocolError;
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
pub(crate) fn i2osp<L: ArrayLength<u8>>(
|
||||
input: usize,
|
||||
@@ -43,7 +43,8 @@ pub(crate) fn os2ip(input: &[u8]) -> Result<usize, ProtocolError> {
|
||||
Ok(usize::from_be_bytes(output_array))
|
||||
}
|
||||
|
||||
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output without allocation.
|
||||
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output
|
||||
/// without allocation.
|
||||
pub(crate) struct Serialize<
|
||||
'a,
|
||||
L1: ArrayLength<u8>,
|
||||
@@ -161,9 +162,10 @@ mod tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
|
||||
use super::*;
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
|
||||
+27
-29
@@ -5,39 +5,35 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode},
|
||||
errors::*,
|
||||
hash::{OutputSize, ProxyHash},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen},
|
||||
},
|
||||
key_exchange::{
|
||||
traits::{FromBytes, KeyExchange, ToBytes},
|
||||
tripledh::{NonceLen, TripleDH},
|
||||
},
|
||||
keypair::KeyPair,
|
||||
messages::CredentialResponseWithoutKeLen,
|
||||
opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen},
|
||||
serialization::{i2osp, os2ip},
|
||||
*,
|
||||
};
|
||||
use core::ops::Add;
|
||||
use std::vec;
|
||||
use std::vec::Vec;
|
||||
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use digest::Output;
|
||||
use generic_array::{
|
||||
typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256},
|
||||
ArrayLength,
|
||||
};
|
||||
use proptest::{collection::vec, prelude::*};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256};
|
||||
use generic_array::ArrayLength;
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use voprf::Group;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode};
|
||||
use crate::errors::*;
|
||||
use crate::hash::{OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{
|
||||
FromBytes, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, ToBytes,
|
||||
};
|
||||
use crate::key_exchange::tripledh::{NonceLen, TripleDH};
|
||||
use crate::keypair::KeyPair;
|
||||
use crate::messages::CredentialResponseWithoutKeLen;
|
||||
use crate::opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen};
|
||||
use crate::serialization::{i2osp, os2ip};
|
||||
use crate::*;
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
struct Ristretto255;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
@@ -128,16 +124,18 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
||||
// 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 = Output::<CS::Hash>::default();
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
|
||||
// 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(&Output::<CS::Hash>::default()); // length-MAC_SIZE hmac
|
||||
// empty nonce
|
||||
mock_envelope_bytes.extend_from_slice(&[0; NonceLen::USIZE]);
|
||||
// ciphertext which is an encrypted private key
|
||||
//mock_envelope_bytes.extend_from_slice(&ciphertext);
|
||||
// length-MAC_SIZE hmac
|
||||
mock_envelope_bytes.extend_from_slice(&Output::<CS::Hash>::default());
|
||||
|
||||
let mock_client_kp = KeyPair::<CS::KeGroup>::generate_random(&mut rng);
|
||||
// serialization order: oprf_key, public key, envelope
|
||||
|
||||
+3
-4
@@ -7,14 +7,13 @@
|
||||
|
||||
//! Trait specifying a slow hashing function
|
||||
|
||||
use crate::{
|
||||
errors::InternalError,
|
||||
hash::{Hash, ProxyHash},
|
||||
};
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::Output;
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::hash::{Hash, ProxyHash};
|
||||
|
||||
/// Used for the slow hashing function in OPAQUE
|
||||
pub trait SlowHash<D: Hash>: Default
|
||||
where
|
||||
|
||||
+22
-23
@@ -7,39 +7,37 @@
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::EnvelopeLen,
|
||||
errors::*,
|
||||
hash::{OutputSize, ProxyHash},
|
||||
key_exchange::{
|
||||
group::KeGroup,
|
||||
traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen},
|
||||
tripledh::{NonceLen, TripleDH},
|
||||
},
|
||||
messages::{
|
||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||
RegistrationResponseLen, RegistrationUploadLen,
|
||||
},
|
||||
opaque::*,
|
||||
slow_hash::NoOpHash,
|
||||
tests::mock_rng::CycleRng,
|
||||
*,
|
||||
};
|
||||
use core::ops::Add;
|
||||
use std::string::{String, ToString};
|
||||
use std::vec::Vec;
|
||||
use std::{format, println, vec};
|
||||
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use digest::Output;
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256};
|
||||
use generic_array::ArrayLength;
|
||||
use rand::rngs::OsRng;
|
||||
use serde_json::Value;
|
||||
use std::string::{String, ToString};
|
||||
use std::vec::Vec;
|
||||
use std::{format, println, vec};
|
||||
use subtle::ConstantTimeEq;
|
||||
use voprf::Group;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::envelope::EnvelopeLen;
|
||||
use crate::errors::*;
|
||||
use crate::hash::{OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen};
|
||||
use crate::key_exchange::tripledh::{NonceLen, TripleDH};
|
||||
use crate::messages::{
|
||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||
RegistrationResponseLen, RegistrationUploadLen,
|
||||
};
|
||||
use crate::opaque::*;
|
||||
use crate::slow_hash::NoOpHash;
|
||||
use crate::tests::mock_rng::CycleRng;
|
||||
use crate::*;
|
||||
|
||||
// Tests
|
||||
// =====
|
||||
|
||||
@@ -491,9 +489,10 @@ where
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
use crate::keypair::KeyPair;
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::keypair::KeyPair;
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
// Inputs
|
||||
|
||||
@@ -6,22 +6,21 @@
|
||||
// of this source tree.
|
||||
|
||||
use core::cmp::min;
|
||||
use rand::{CryptoRng, Error, RngCore};
|
||||
use std::vec::Vec;
|
||||
|
||||
use rand::{CryptoRng, Error, RngCore};
|
||||
|
||||
/// A simple implementation of `RngCore` for testing purposes.
|
||||
///
|
||||
/// This generates a cyclic sequence (i.e. cycles over an initial buffer)
|
||||
///
|
||||
///
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CycleRng {
|
||||
v: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CycleRng {
|
||||
/// Create a `CycleRng`, yielding a sequence starting with
|
||||
/// `initial` and looping thereafter
|
||||
/// Create a `CycleRng`, yielding a sequence starting with `initial` and
|
||||
/// looping thereafter
|
||||
pub fn new(initial: Vec<u8>) -> Self {
|
||||
CycleRng { v: initial }
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! The OPAQUE test vectors taken from:
|
||||
//! https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md
|
||||
//! The OPAQUE test vectors taken from: https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md
|
||||
|
||||
pub(crate) static VECTORS: &str = r#"
|
||||
## Real Test Vectors {#real-vectors}
|
||||
|
||||
@@ -5,35 +5,33 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::EnvelopeLen,
|
||||
errors::*,
|
||||
hash::{OutputSize, ProxyHash},
|
||||
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 core::ops::Add;
|
||||
use std::string::ToString;
|
||||
use std::vec::Vec;
|
||||
use std::{println, vec};
|
||||
|
||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||
use generic_array::{
|
||||
typenum::{IsLess, Le, NonZero, Sum, U256},
|
||||
ArrayLength,
|
||||
};
|
||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
|
||||
use generic_array::ArrayLength;
|
||||
use json::JsonValue;
|
||||
use std::{println, string::ToString, vec, vec::Vec};
|
||||
use voprf::Group;
|
||||
|
||||
use crate::ciphersuite::CipherSuite;
|
||||
use crate::envelope::EnvelopeLen;
|
||||
use crate::errors::*;
|
||||
use crate::hash::{OutputSize, ProxyHash};
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::traits::{Ke1MessageLen, Ke2MessageLen};
|
||||
use crate::key_exchange::tripledh::{NonceLen, TripleDH};
|
||||
use crate::messages::{
|
||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||
RegistrationResponseLen, RegistrationUploadLen,
|
||||
};
|
||||
use crate::opaque::*;
|
||||
use crate::slow_hash::NoOpHash;
|
||||
use crate::tests::mock_rng::CycleRng;
|
||||
use crate::*;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug)]
|
||||
pub struct OpaqueTestVectorParameters {
|
||||
@@ -95,7 +93,8 @@ macro_rules! parse_default {
|
||||
macro_rules! parse_default_random {
|
||||
( $v:ident, $s:expr, $size:expr ) => {
|
||||
parse_default!($v, $s, {
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
let mut rng = OsRng;
|
||||
let mut v = vec![0u8; $size];
|
||||
rng.fill_bytes(&mut v);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[formatting]
|
||||
reorder_keys = true
|
||||
allowed_blank_lines = 1
|
||||
Reference in New Issue
Block a user