mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1901507 - Upgrade ohttp crate to latest version (0.5.1) r=necko-reviewers,glandium,supply-chain-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D213526
This commit is contained in:
parent
d4eaf72722
commit
10a45dcf15
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -435,7 +435,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.63.999"
|
||||
version = "0.64.999"
|
||||
dependencies = [
|
||||
"bindgen 0.69.4",
|
||||
]
|
||||
@ -4344,11 +4344,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ohttp"
|
||||
version = "0.3.1"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "850ce328ec7e4dc1a9446c56aef700d21d914268c8529b96017a2bf10f74b70f"
|
||||
checksum = "578cb11a3fb5c85697ed8bb850d5ad1cbf819d3eea05c2b253cf1d240fbb10c5"
|
||||
dependencies = [
|
||||
"bindgen 0.63.999",
|
||||
"bindgen 0.64.999",
|
||||
"byteorder",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bindgen"
|
||||
version = "0.63.999"
|
||||
version = "0.64.999"
|
||||
edition = "2018"
|
||||
license = "BSD-3-Clause"
|
||||
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
ohttp = { version = "0.3", default-features = false, features = ["gecko", "nss", "client", "server"] }
|
||||
ohttp = { version = "0.5", default-features = false, features = ["gecko", "nss", "client", "server"] }
|
||||
rand = "0.8"
|
||||
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
||||
|
@ -110,7 +110,7 @@ impl ObliviousHttpServer {
|
||||
&self,
|
||||
enc_request: &ThinVec<u8>,
|
||||
) -> Result<RefPtr<nsIObliviousHttpServerResponse>, nsresult> {
|
||||
let mut server = self.server.borrow_mut();
|
||||
let server = self.server.borrow_mut();
|
||||
let (request, server_response) = server
|
||||
.decapsulate(enc_request)
|
||||
.map_err(|_| NS_ERROR_FAILURE)?;
|
||||
@ -138,7 +138,7 @@ impl ObliviousHttp {
|
||||
) -> Result<RefPtr<nsIObliviousHttpClientRequest>, nsresult> {
|
||||
ohttp::init();
|
||||
|
||||
let client = ClientRequest::new(encoded_config).map_err(|_| NS_ERROR_FAILURE)?;
|
||||
let client = ClientRequest::from_encoded_config(encoded_config).map_err(|_| NS_ERROR_FAILURE)?;
|
||||
let (enc_request, response) = client.encapsulate(request).map_err(|_| NS_ERROR_FAILURE)?;
|
||||
let oblivious_http_client_response =
|
||||
ObliviousHttpClientResponse::allocate(InitObliviousHttpClientResponse {
|
||||
|
@ -373,8 +373,8 @@ user-login = "seanmonstar"
|
||||
user-name = "Sean McArthur"
|
||||
|
||||
[[publisher.ohttp]]
|
||||
version = "0.3.1"
|
||||
when = "2023-02-23"
|
||||
version = "0.5.1"
|
||||
when = "2024-01-10"
|
||||
user-id = 128763
|
||||
user-login = "martinthomson"
|
||||
user-name = "Martin Thomson"
|
||||
@ -559,6 +559,13 @@ user-id = 2017
|
||||
user-login = "mbrubeck"
|
||||
user-name = "Matt Brubeck"
|
||||
|
||||
[[publisher.syn]]
|
||||
version = "1.0.109"
|
||||
when = "2023-02-24"
|
||||
user-id = 3618
|
||||
user-login = "dtolnay"
|
||||
user-name = "David Tolnay"
|
||||
|
||||
[[publisher.syn]]
|
||||
version = "2.0.68"
|
||||
when = "2024-06-23"
|
||||
@ -1125,6 +1132,12 @@ criteria = "safe-to-deploy"
|
||||
delta = "0.31.1 -> 0.32.0"
|
||||
notes = "Various new features and refactorings as one would expect from an object parsing crate, all looks good."
|
||||
|
||||
[[audits.bytecode-alliance.audits.peeking_take_while]]
|
||||
who = "Nick Fitzgerald <fitzgen@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.0.0"
|
||||
notes = "I am the author of this crate."
|
||||
|
||||
[[audits.bytecode-alliance.audits.percent-encoding]]
|
||||
who = "Alex Crichton <alex@alexcrichton.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
2
third_party/rust/ohttp/.cargo-checksum.json
vendored
2
third_party/rust/ohttp/.cargo-checksum.json
vendored
@ -1 +1 @@
|
||||
{"files":{"Cargo.toml":"b92b4dceca6d654156f3be0c62061f4f869ed92d27fc2178be59fe8efa588db4","README.md":"a97309a7b0c65dbcbd43e6c64bf99e023cd07e9d3de5c4559f33db430edafebd","bindings/bindings.toml":"a016870127b63151e760c964d687934a4883ee165bdd9718341c8dd50be5a3f2","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nss_init.h":"cd4dffd0c629ece5786736dd6d26db8a96f56fd56ef95b150c623c41080c2f9e","bindings/nss_p11.h":"a16f60d0210d5823f2d92d0c04988a0bb1da85901388490cb3e755a62cc7d5dd","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","build.rs":"3a67dd5475792d9ce5d081f1f6d2abf3fc29b02d2fc2dcebf0c44a1fc2e6abcd","src/err.rs":"99127514f826c97072d33891e07277dd21108c877c1cd66b7be23131be183235","src/hpke.rs":"21182eed9bd71ea59fba1670491f845a7f773d1285b89d2772e052f8a37f475c","src/lib.rs":"d79c9edbeac382db424a1c0612cca353b275e16d7162b7b0877f8cb8874cfa80","src/nss/aead.rs":"780c47f57a6fa3e498d46f9935dc112c3b010658fa2d3874975b205f7b436ff3","src/nss/err.rs":"d367116bf9840ebe06d358066a1755f7d9936438dbc2e4e71a6b345d05e4bcd5","src/nss/hkdf.rs":"d70147f27be9defcb2cd67da9e49a4f09adecd0a2ebad646eae94d09512b1b0b","src/nss/hpke.rs":"4d216f653e1a588402646f0041f97fa1ef1907f4003e16dc5dfde73bfb568686","src/nss/mod.rs":"7dc88bef24a2d80a0c46a7e1cb24f2568bd75c1d082b3b39f005cc92873ab0cf","src/nss/p11.rs":"9bcf9ec65dc357f805b951fff875b5d7d91e0bf7170d96fe2044933e94950b8a","src/rand.rs":"be3a82fb6090b5cb833c2b8ba6e72690b9f44f7f91477e2c5b70b75623174b87","src/rh/aead.rs":"44321f6098e44b75ed586ebee843bed4edb88e9f8a384457a28ae8ab3d5245ea","src/rh/hkdf.rs":"4c6e59ca24498939e9b8da2da6336ae2f62396101f1b9b25f1ae587ab21c1a9d","src/rh/hpke.rs":"0744c71cfc33bdf22c4889e58bcc947a9debcb0a03890cf7e6d9353526bd4578","src/rh/mod.rs":"d6045628f9b95d75e8bfcc30d55b1fc8b5ea9e2a4c45e20102f4f33c0b711a0c"},"package":"850ce328ec7e4dc1a9446c56aef700d21d914268c8529b96017a2bf10f74b70f"}
|
||||
{"files":{"Cargo.toml":"f93723973762c48df66c9e2125da726559e9bc0bf5ed38fb4e776af3381502fc","README.md":"a97309a7b0c65dbcbd43e6c64bf99e023cd07e9d3de5c4559f33db430edafebd","bindings/bindings.toml":"a016870127b63151e760c964d687934a4883ee165bdd9718341c8dd50be5a3f2","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nss_init.h":"cd4dffd0c629ece5786736dd6d26db8a96f56fd56ef95b150c623c41080c2f9e","bindings/nss_p11.h":"a16f60d0210d5823f2d92d0c04988a0bb1da85901388490cb3e755a62cc7d5dd","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","build.rs":"75f7bc67d2757bf68f1ba64258a7a7db4b94de5403936b73d8140e9558c30698","src/config.rs":"d39a765f561423f320e0be8c6aaabf3c3fb85248468b345aa43f778974280945","src/err.rs":"533b382c7e74906521395fdff4b514981535fe1b45b307528c547bb545765adf","src/hpke.rs":"b98b3a1d2e87bd89d43d89e2513e2d3211832aef3fd2157337939186aa7c8531","src/lib.rs":"bfc972343aa3fa6707b0aaea04c25f1b6fa2ff3f9e9761ae6a09f9be52fce214","src/nss/aead.rs":"01b3b1654a310a276edb5750d2d0e3ee14943f8695f413d176b65623369d81be","src/nss/err.rs":"d367116bf9840ebe06d358066a1755f7d9936438dbc2e4e71a6b345d05e4bcd5","src/nss/hkdf.rs":"d70147f27be9defcb2cd67da9e49a4f09adecd0a2ebad646eae94d09512b1b0b","src/nss/hpke.rs":"cbd353d2f0c8db82fcf25dac2f2d2554e8a6258c4b21e0279e618fb2f992c49c","src/nss/mod.rs":"bca4b6fcc807b7b146989fe500c0e5b249ddfcaff1a795cc8b84a70631b50a60","src/nss/p11.rs":"1761896f0359c37cd186e34daaf1b582e907a123fe987b937c314f7b2cecc9de","src/rand.rs":"be3a82fb6090b5cb833c2b8ba6e72690b9f44f7f91477e2c5b70b75623174b87","src/rh/aead.rs":"88dd43ffec20a8870a424d27dbda0a9db74f4b3d1b03097220854a304ab091cd","src/rh/hkdf.rs":"4c6e59ca24498939e9b8da2da6336ae2f62396101f1b9b25f1ae587ab21c1a9d","src/rh/hpke.rs":"8d7de700e48bf4f6a711e6c5df1eb4a7ae7d33c65d75728e9d1c8c797f474e7a","src/rh/mod.rs":"d6045628f9b95d75e8bfcc30d55b1fc8b5ea9e2a4c45e20102f4f33c0b711a0c"},"package":"578cb11a3fb5c85697ed8bb850d5ad1cbf819d3eea05c2b253cf1d240fbb10c5"}
|
32
third_party/rust/ohttp/Cargo.toml
vendored
32
third_party/rust/ohttp/Cargo.toml
vendored
@ -12,7 +12,7 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "ohttp"
|
||||
version = "0.3.1"
|
||||
version = "0.5.1"
|
||||
authors = ["Martin Thomson <mt@lowentropy.net>"]
|
||||
build = "build.rs"
|
||||
description = "Oblivious HTTP"
|
||||
@ -44,11 +44,25 @@ version = "0.11"
|
||||
optional = true
|
||||
|
||||
[dependencies.hpke]
|
||||
version = "0.7"
|
||||
features = ["std"]
|
||||
version = "0.10.0"
|
||||
features = [
|
||||
"std",
|
||||
"x25519",
|
||||
]
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.hpke-pq]
|
||||
version = "0.10.1"
|
||||
features = [
|
||||
"std",
|
||||
"x25519",
|
||||
"xyber768d00",
|
||||
]
|
||||
optional = true
|
||||
default-features = false
|
||||
package = "hpke_pq"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.4"
|
||||
|
||||
@ -72,7 +86,7 @@ version = "0.9"
|
||||
default-features = false
|
||||
|
||||
[build-dependencies.bindgen]
|
||||
version = "0.63"
|
||||
version = "0.64"
|
||||
features = ["runtime"]
|
||||
optional = true
|
||||
default-features = false
|
||||
@ -91,21 +105,27 @@ version = "1.0"
|
||||
version = "0.5"
|
||||
|
||||
[features]
|
||||
app-svc = ["nss"]
|
||||
client = []
|
||||
default = [
|
||||
"client",
|
||||
"server",
|
||||
"rust-hpke",
|
||||
]
|
||||
gecko = ["mozbuild"]
|
||||
external-sqlite = []
|
||||
gecko = [
|
||||
"nss",
|
||||
"mozbuild",
|
||||
]
|
||||
nss = ["bindgen"]
|
||||
pq = ["hpke-pq"]
|
||||
rust-hpke = [
|
||||
"hpke/x25519",
|
||||
"rand",
|
||||
"aead",
|
||||
"aes-gcm",
|
||||
"chacha20poly1305",
|
||||
"hkdf",
|
||||
"sha2",
|
||||
"hpke",
|
||||
]
|
||||
server = []
|
||||
|
145
third_party/rust/ohttp/build.rs
vendored
145
third_party/rust/ohttp/build.rs
vendored
@ -133,59 +133,108 @@ mod nss {
|
||||
assert!(status.success(), "NSS build failed");
|
||||
}
|
||||
|
||||
fn dynamic_link() {
|
||||
let libs = if env::consts::OS == "windows" {
|
||||
&["nssutil3.dll", "nss3.dll"]
|
||||
fn nspr_libs() -> Vec<&'static str> {
|
||||
if env::consts::OS == "windows" {
|
||||
vec!["libplds4", "libplc4", "libnspr4"]
|
||||
} else {
|
||||
&["nssutil3", "nss3"]
|
||||
};
|
||||
dynamic_link_both(libs);
|
||||
vec!["plds4", "plc4", "nspr4"]
|
||||
}
|
||||
}
|
||||
|
||||
fn dynamic_link_both(extra_libs: &[&str]) {
|
||||
let nspr_libs = if env::consts::OS == "windows" {
|
||||
&["libplds4", "libplc4", "libnspr4"]
|
||||
fn dynamic_link() {
|
||||
let mut libs = if env::consts::OS == "windows" {
|
||||
vec!["nssutil3.dll", "nss3.dll"]
|
||||
} else {
|
||||
&["plds4", "plc4", "nspr4"]
|
||||
vec!["nssutil3", "nss3"]
|
||||
};
|
||||
for lib in nspr_libs.iter().chain(extra_libs) {
|
||||
|
||||
libs.append(&mut nspr_libs());
|
||||
|
||||
for lib in &libs {
|
||||
println!("cargo:rustc-link-lib=dylib={}", lib);
|
||||
}
|
||||
}
|
||||
|
||||
fn static_link() {
|
||||
fn static_softoken_libs(nsslibdir: &Path) -> Vec<&'static str> {
|
||||
let mut static_libs = vec!["pk11wrap_static", "softokn_static", "freebl_static"];
|
||||
|
||||
// NSS optionally builds platform-specific acceleration libraries as
|
||||
// separate static libraries.
|
||||
let accel_libs = &[
|
||||
"gcm-aes-x86_c_lib",
|
||||
"sha-x86_c_lib",
|
||||
"hw-acc-crypto-avx",
|
||||
"hw-acc-crypto-avx2",
|
||||
"armv8_c_lib",
|
||||
"gcm-aes-arm32-neon_c_lib",
|
||||
"gcm-aes-aarch64_c_lib",
|
||||
// NOTE: The intel-gcm-* libraries are already automatically
|
||||
// included in freebl_static as source files.
|
||||
];
|
||||
|
||||
// Build rules are complex, so simply check the lib directory to see if
|
||||
// any of the accelerator libraries were built to decide what to
|
||||
// include. Check different variations of the filename to handle
|
||||
// platform differences.
|
||||
for libname in accel_libs {
|
||||
let filename = if env::consts::OS == "windows" {
|
||||
format!("{libname}.lib")
|
||||
} else {
|
||||
format!("lib{libname}.a")
|
||||
};
|
||||
if nsslibdir.join(filename).is_file() {
|
||||
static_libs.push(libname);
|
||||
}
|
||||
}
|
||||
|
||||
static_libs
|
||||
}
|
||||
|
||||
fn static_link(nsslibdir: &Path, use_static_softoken: bool, use_static_nspr: bool) {
|
||||
let mut static_libs = vec![
|
||||
"certdb",
|
||||
"certhi",
|
||||
"cryptohi",
|
||||
"freebl",
|
||||
"nss_static",
|
||||
"nssb",
|
||||
"nssdev",
|
||||
"nsspki",
|
||||
"nssutil",
|
||||
"pk11wrap",
|
||||
"pkcs12",
|
||||
"pkcs7",
|
||||
"smime",
|
||||
"softokn_static",
|
||||
];
|
||||
if env::consts::OS != "macos" {
|
||||
static_libs.push("sqlite");
|
||||
let mut dynamic_libs = vec![];
|
||||
|
||||
if use_static_softoken {
|
||||
// Statically link pk11/softokn/freebl
|
||||
static_libs.append(&mut static_softoken_libs(nsslibdir));
|
||||
} else {
|
||||
// Use dlopen to get softokn3.so
|
||||
static_libs.push("pk11wrap");
|
||||
}
|
||||
for lib in static_libs {
|
||||
println!("cargo:rustc-link-lib=static={}", lib);
|
||||
|
||||
if use_static_nspr {
|
||||
static_libs.append(&mut nspr_libs());
|
||||
} else {
|
||||
dynamic_libs.append(&mut nspr_libs());
|
||||
}
|
||||
|
||||
if cfg!(not(feature = "external-sqlite")) && env::consts::OS != "macos" {
|
||||
static_libs.push("sqlite");
|
||||
}
|
||||
|
||||
// Dynamic libs that aren't transitively included by NSS libs.
|
||||
let mut other_libs = Vec::new();
|
||||
if env::consts::OS != "windows" {
|
||||
other_libs.extend_from_slice(&["pthread", "dl", "c", "z"]);
|
||||
dynamic_libs.extend_from_slice(&["pthread", "dl", "c", "z"]);
|
||||
}
|
||||
if env::consts::OS == "macos" {
|
||||
other_libs.push("sqlite3");
|
||||
if cfg!(not(feature = "external-sqlite")) && env::consts::OS == "macos" {
|
||||
dynamic_libs.push("sqlite3");
|
||||
}
|
||||
|
||||
for lib in &static_libs {
|
||||
println!("cargo:rustc-link-lib=static={}", lib);
|
||||
}
|
||||
for lib in &dynamic_libs {
|
||||
println!("cargo:rustc-link-lib=dylib={}", lib);
|
||||
}
|
||||
dynamic_link_both(&other_libs);
|
||||
}
|
||||
|
||||
fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> {
|
||||
@ -275,7 +324,9 @@ mod nss {
|
||||
nsslibdir.to_str().unwrap()
|
||||
);
|
||||
if is_debug() {
|
||||
static_link();
|
||||
let use_static_softoken = true;
|
||||
let use_static_nspr = true;
|
||||
static_link(&nsslibdir, use_static_softoken, use_static_nspr);
|
||||
} else {
|
||||
dynamic_link();
|
||||
}
|
||||
@ -411,10 +462,48 @@ mod nss {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "app-svc")]
|
||||
fn setup_for_app_svc() -> Vec<String> {
|
||||
// Locate the NSS libraries that application_services is using.
|
||||
// NOTE: This directory has a slightly different layout than then normal
|
||||
// 'dist' directory that NSS builds output.
|
||||
let nss_dir = nss_dir().expect("NSS_DIR env must be set for app_svc builds");
|
||||
if !nss_dir.exists() {
|
||||
eprintln!(
|
||||
"NSS_DIR path (obtained via `env`) does not exist: {}",
|
||||
nss_dir.display()
|
||||
);
|
||||
panic!("It looks like NSS is not built. Please run `libs/verify-[platform]-environment.sh` in application-services first!");
|
||||
}
|
||||
|
||||
let lib_dir = nss_dir.join("lib");
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
lib_dir.to_string_lossy()
|
||||
);
|
||||
|
||||
// For app_svc builds, we use static linking of NSS.
|
||||
let use_static_softoken = true;
|
||||
let use_static_nspr = true;
|
||||
static_link(&lib_dir, use_static_softoken, use_static_nspr);
|
||||
|
||||
let include_dir = nss_dir.join("include");
|
||||
println!("cargo:include={}", include_dir.to_string_lossy());
|
||||
|
||||
vec![String::from("-I") + &include_dir.join("nss").to_string_lossy()]
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "app-svc"))]
|
||||
fn setup_for_app_svc() -> Vec<String> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn build() {
|
||||
println!("cargo:rerun-if-env-changed=NSS_DIR");
|
||||
let flags = if cfg!(feature = "gecko") {
|
||||
setup_for_gecko()
|
||||
} else if cfg!(feature = "app-svc") {
|
||||
setup_for_app_svc()
|
||||
} else {
|
||||
nss_dir().map_or_else(pkg_config, |nss| build_nss(&nss))
|
||||
};
|
||||
|
376
third_party/rust/ohttp/src/config.rs
vendored
Normal file
376
third_party/rust/ohttp/src/config.rs
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
use crate::{
|
||||
err::{Error, Res},
|
||||
hpke::{Aead as AeadId, Kdf, Kem},
|
||||
KeyId,
|
||||
};
|
||||
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
io::{BufRead, BufReader, Cursor, Read},
|
||||
};
|
||||
|
||||
#[cfg(feature = "nss")]
|
||||
use crate::nss::{
|
||||
hpke::{generate_key_pair, Config as HpkeConfig, HpkeR},
|
||||
PrivateKey, PublicKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
use crate::rh::hpke::{
|
||||
derive_key_pair, generate_key_pair, Config as HpkeConfig, HpkeR, PrivateKey, PublicKey,
|
||||
};
|
||||
|
||||
/// A tuple of KDF and AEAD identifiers.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SymmetricSuite {
|
||||
kdf: Kdf,
|
||||
aead: AeadId,
|
||||
}
|
||||
|
||||
impl SymmetricSuite {
|
||||
#[must_use]
|
||||
pub const fn new(kdf: Kdf, aead: AeadId) -> Self {
|
||||
Self { kdf, aead }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn kdf(self) -> Kdf {
|
||||
self.kdf
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aead(self) -> AeadId {
|
||||
self.aead
|
||||
}
|
||||
}
|
||||
|
||||
/// The key configuration of a server. This can be used by both client and server.
|
||||
/// An important invariant of this structure is that it does not include
|
||||
/// any combination of KEM, KDF, and AEAD that is not supported.
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeyConfig {
|
||||
pub(crate) key_id: KeyId,
|
||||
pub(crate) kem: Kem,
|
||||
pub(crate) symmetric: Vec<SymmetricSuite>,
|
||||
pub(crate) sk: Option<PrivateKey>,
|
||||
pub(crate) pk: PublicKey,
|
||||
}
|
||||
|
||||
impl KeyConfig {
|
||||
fn strip_unsupported(symmetric: &mut Vec<SymmetricSuite>, kem: Kem) {
|
||||
symmetric.retain(|s| HpkeConfig::new(kem, s.kdf(), s.aead()).supported());
|
||||
}
|
||||
|
||||
/// Construct a configuration for the server side.
|
||||
/// # Panics
|
||||
/// If the configurations don't include a supported configuration.
|
||||
pub fn new(key_id: u8, kem: Kem, mut symmetric: Vec<SymmetricSuite>) -> Res<Self> {
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
assert!(!symmetric.is_empty());
|
||||
let (sk, pk) = generate_key_pair(kem)?;
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: Some(sk),
|
||||
pk,
|
||||
})
|
||||
}
|
||||
|
||||
/// Derive a configuration for the server side from input keying material,
|
||||
/// using the `DeriveKeyPair` functionality of the HPKE KEM defined here:
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-12.html#section-4>
|
||||
/// # Panics
|
||||
/// If the configurations don't include a supported configuration.
|
||||
#[allow(unused)]
|
||||
pub fn derive(
|
||||
key_id: u8,
|
||||
kem: Kem,
|
||||
mut symmetric: Vec<SymmetricSuite>,
|
||||
ikm: &[u8],
|
||||
) -> Res<Self> {
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
{
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
assert!(!symmetric.is_empty());
|
||||
let (sk, pk) = derive_key_pair(kem, ikm)?;
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: Some(sk),
|
||||
pk,
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "rust-hpke"))]
|
||||
{
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a list of key configurations.
|
||||
///
|
||||
/// This produces the key configuration format that is used for
|
||||
/// the "application/ohttp-keys" media type.
|
||||
/// Each item in the list is written as per [`encode()`].
|
||||
///
|
||||
/// # Panics
|
||||
/// Not as a result of this function.
|
||||
///
|
||||
/// [`encode()`]: Self::encode
|
||||
pub fn encode_list(list: &[impl AsRef<Self>]) -> Res<Vec<u8>> {
|
||||
let mut buf = Vec::new();
|
||||
for c in list {
|
||||
let offset = buf.len();
|
||||
buf.write_u16::<NetworkEndian>(0)?;
|
||||
c.as_ref().write(&mut buf)?;
|
||||
let len = buf.len() - offset - 2;
|
||||
buf[offset] = u8::try_from(len >> 8)?;
|
||||
buf[offset + 1] = u8::try_from(len & 0xff).unwrap();
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &mut Vec<u8>) -> Res<()> {
|
||||
buf.write_u8(self.key_id)?;
|
||||
buf.write_u16::<NetworkEndian>(u16::from(self.kem))?;
|
||||
let pk_buf = self.pk.key_data()?;
|
||||
buf.extend_from_slice(&pk_buf);
|
||||
buf.write_u16::<NetworkEndian>((self.symmetric.len() * 4).try_into()?)?;
|
||||
for s in &self.symmetric {
|
||||
buf.write_u16::<NetworkEndian>(u16::from(s.kdf()))?;
|
||||
buf.write_u16::<NetworkEndian>(u16::from(s.aead()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Encode into a wire format. This shares a format with the core of ECH:
|
||||
///
|
||||
/// ```tls-format
|
||||
/// opaque HpkePublicKey[Npk];
|
||||
/// uint16 HpkeKemId; // Defined in I-D.irtf-cfrg-hpke
|
||||
/// uint16 HpkeKdfId; // Defined in I-D.irtf-cfrg-hpke
|
||||
/// uint16 HpkeAeadId; // Defined in I-D.irtf-cfrg-hpke
|
||||
///
|
||||
/// struct {
|
||||
/// HpkeKdfId kdf_id;
|
||||
/// HpkeAeadId aead_id;
|
||||
/// } ECHCipherSuite;
|
||||
///
|
||||
/// struct {
|
||||
/// uint8 key_id;
|
||||
/// HpkeKemId kem_id;
|
||||
/// HpkePublicKey public_key;
|
||||
/// ECHCipherSuite cipher_suites<4..2^16-4>;
|
||||
/// } ECHKeyConfig;
|
||||
/// ```
|
||||
/// # Panics
|
||||
/// Not as a result of this function.
|
||||
pub fn encode(&self) -> Res<Vec<u8>> {
|
||||
let mut buf = Vec::new();
|
||||
self.write(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Construct a configuration from the encoded server configuration.
|
||||
/// The format of `encoded_config` is the output of `Self::encode`.
|
||||
pub fn decode(encoded_config: &[u8]) -> Res<Self> {
|
||||
let end_position = u64::try_from(encoded_config.len())?;
|
||||
let mut r = Cursor::new(encoded_config);
|
||||
let key_id = r.read_u8()?;
|
||||
let kem = Kem::try_from(r.read_u16::<NetworkEndian>()?)?;
|
||||
|
||||
// Note that the KDF and AEAD doesn't matter here.
|
||||
let kem_config = HpkeConfig::new(kem, Kdf::HkdfSha256, AeadId::Aes128Gcm);
|
||||
if !kem_config.supported() {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
let mut pk_buf = vec![0; kem_config.kem().n_pk()];
|
||||
r.read_exact(&mut pk_buf)?;
|
||||
|
||||
let sym_len = r.read_u16::<NetworkEndian>()?;
|
||||
let mut sym = vec![0; usize::from(sym_len)];
|
||||
r.read_exact(&mut sym)?;
|
||||
if sym.is_empty() || (sym.len() % 4 != 0) {
|
||||
return Err(Error::Format);
|
||||
}
|
||||
let sym_count = sym.len() / 4;
|
||||
let mut sym_r = BufReader::new(&sym[..]);
|
||||
let mut symmetric = Vec::with_capacity(sym_count);
|
||||
for _ in 0..sym_count {
|
||||
let kdf = Kdf::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
|
||||
let aead = AeadId::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
|
||||
symmetric.push(SymmetricSuite::new(kdf, aead));
|
||||
}
|
||||
|
||||
// Check that there was nothing extra and we are at the end of the buffer.
|
||||
if r.position() != end_position {
|
||||
return Err(Error::Format);
|
||||
}
|
||||
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
let pk = HpkeR::decode_public_key(kem_config.kem(), &pk_buf)?;
|
||||
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: None,
|
||||
pk,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decode a list of key configurations.
|
||||
/// This only returns the valid and supported key configurations;
|
||||
/// unsupported configurations are dropped silently.
|
||||
pub fn decode_list(encoded_list: &[u8]) -> Res<Vec<Self>> {
|
||||
let end_position = u64::try_from(encoded_list.len())?;
|
||||
let mut r = Cursor::new(encoded_list);
|
||||
let mut configs = Vec::new();
|
||||
loop {
|
||||
if r.position() == end_position {
|
||||
break;
|
||||
}
|
||||
let len = usize::from(r.read_u16::<NetworkEndian>()?);
|
||||
let buf = r.fill_buf()?;
|
||||
if len > buf.len() {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
let res = Self::decode(&buf[..len]);
|
||||
r.consume(len);
|
||||
match res {
|
||||
Ok(config) => configs.push(config),
|
||||
Err(Error::Unsupported) => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
/// Select creates a new configuration that contains the identified symmetric suite.
|
||||
///
|
||||
/// # Errors
|
||||
/// If the given suite is not supported by this configuration.
|
||||
pub fn select(&self, sym: SymmetricSuite) -> Res<HpkeConfig> {
|
||||
if self.symmetric.contains(&sym) {
|
||||
let config = HpkeConfig::new(self.kem, sym.kdf(), sym.aead());
|
||||
Ok(config)
|
||||
} else {
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Self> for KeyConfig {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
hpke::{Aead, Kdf, Kem},
|
||||
init, Error, KeyConfig, KeyId, SymmetricSuite,
|
||||
};
|
||||
use std::iter::zip;
|
||||
|
||||
const KEY_ID: KeyId = 1;
|
||||
const KEM: Kem = Kem::X25519Sha256;
|
||||
const SYMMETRIC: &[SymmetricSuite] = &[
|
||||
SymmetricSuite::new(Kdf::HkdfSha256, Aead::Aes128Gcm),
|
||||
SymmetricSuite::new(Kdf::HkdfSha256, Aead::ChaCha20Poly1305),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn encode_decode_config_list() {
|
||||
const COUNT: usize = 3;
|
||||
init();
|
||||
|
||||
let mut configs = Vec::with_capacity(COUNT);
|
||||
configs.resize_with(COUNT, || {
|
||||
KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap()
|
||||
});
|
||||
|
||||
let buf = KeyConfig::encode_list(&configs).unwrap();
|
||||
let decoded_list = KeyConfig::decode_list(&buf).unwrap();
|
||||
for (original, decoded) in zip(&configs, &decoded_list) {
|
||||
assert_eq!(decoded.key_id, original.key_id);
|
||||
assert_eq!(decoded.kem, original.kem);
|
||||
assert_eq!(
|
||||
decoded.pk.key_data().unwrap(),
|
||||
original.pk.key_data().unwrap()
|
||||
);
|
||||
assert!(decoded.sk.is_none());
|
||||
assert!(original.sk.is_some());
|
||||
}
|
||||
|
||||
// Check that truncation errors in `KeyConfig::decode` are caught.
|
||||
assert!(KeyConfig::decode_list(&buf[..buf.len() - 3]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_config_list() {
|
||||
let list = KeyConfig::decode_list(&[]).unwrap();
|
||||
assert!(list.is_empty());
|
||||
|
||||
// A reserved KEM ID is not bad. Note that we don't check that the data
|
||||
// following the KEM ID is even the minimum length, allowing this to be
|
||||
// zero bytes, where you need at least some bytes in a public key and some
|
||||
// bytes to identify at least one KDF and AEAD (i.e., more than 6 bytes).
|
||||
let list = KeyConfig::decode_list(&[0, 3, 0, 0, 0]).unwrap();
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_config_list_length() {
|
||||
init();
|
||||
|
||||
// A one byte length for a config.
|
||||
let res = KeyConfig::decode_list(&[0]);
|
||||
assert!(matches!(res, Err(Error::Io(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_bad_config() {
|
||||
init();
|
||||
|
||||
let mut x25519 = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC))
|
||||
.unwrap()
|
||||
.encode()
|
||||
.unwrap();
|
||||
{
|
||||
// Truncation tests.
|
||||
let trunc = |n: usize| KeyConfig::decode(&x25519[..n]);
|
||||
|
||||
// x25519, truncated inside the KEM ID.
|
||||
assert!(matches!(trunc(2), Err(Error::Io(_))));
|
||||
// ... inside the public key.
|
||||
assert!(matches!(trunc(4), Err(Error::Io(_))));
|
||||
// ... inside the length of the KDF+AEAD list.
|
||||
assert!(matches!(trunc(36), Err(Error::Io(_))));
|
||||
// ... inside the KDF+AEAD list.
|
||||
assert!(matches!(trunc(38), Err(Error::Io(_))));
|
||||
}
|
||||
|
||||
// And then with an extra byte at the end.
|
||||
x25519.push(0);
|
||||
assert!(matches!(KeyConfig::decode(&x25519), Err(Error::Format)));
|
||||
}
|
||||
|
||||
/// Truncate the KDF+AEAD list badly.
|
||||
#[test]
|
||||
fn truncate_kdf_aead_list() {
|
||||
init();
|
||||
|
||||
let mut x25519 = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC))
|
||||
.unwrap()
|
||||
.encode()
|
||||
.unwrap();
|
||||
x25519.truncate(38);
|
||||
assert_eq!(usize::from(x25519[36]), SYMMETRIC.len() * 4);
|
||||
x25519[36] = 1;
|
||||
assert!(matches!(KeyConfig::decode(&x25519), Err(Error::Format)));
|
||||
}
|
||||
}
|
5
third_party/rust/ohttp/src/err.rs
vendored
5
third_party/rust/ohttp/src/err.rs
vendored
@ -10,9 +10,12 @@ pub enum Error {
|
||||
Crypto(#[from] crate::nss::Error),
|
||||
#[error("an error was found in the format")]
|
||||
Format,
|
||||
#[cfg(all(feature = "rust-hpke", not(feature = "pq")))]
|
||||
#[error("a problem occurred with HPKE: {0}")]
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
Hpke(#[from] ::hpke::HpkeError),
|
||||
#[cfg(all(feature = "rust-hpke", feature = "pq"))]
|
||||
#[error("a problem occurred with HPKE: {0}")]
|
||||
Hpke(#[from] ::hpke_pq::HpkeError),
|
||||
#[error("an internal error occurred")]
|
||||
Internal,
|
||||
#[error("the wrong type of key was provided for the selected KEM")]
|
||||
|
13
third_party/rust/ohttp/src/hpke.rs
vendored
13
third_party/rust/ohttp/src/hpke.rs
vendored
@ -13,7 +13,9 @@ macro_rules! convert_enum {
|
||||
|
||||
fn try_from(v: u16) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
$(x if x == $name::$vname as u16 => Ok($name::$vname),)*
|
||||
$($(#[$vmeta])*
|
||||
x if x == $name::$vname as u16
|
||||
=> Ok($name::$vname),)*
|
||||
_ => Err(crate::Error::Unsupported),
|
||||
}
|
||||
}
|
||||
@ -30,6 +32,9 @@ macro_rules! convert_enum {
|
||||
convert_enum! {
|
||||
pub enum Kem {
|
||||
X25519Sha256 = 32,
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
X25519Kyber768Draft00 = 48,
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +43,9 @@ impl Kem {
|
||||
pub fn n_enc(self) -> usize {
|
||||
match self {
|
||||
Kem::X25519Sha256 => 32,
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Kem::X25519Kyber768Draft00 => 1120,
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +53,9 @@ impl Kem {
|
||||
pub fn n_pk(self) -> usize {
|
||||
match self {
|
||||
Kem::X25519Sha256 => 32,
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Kem::X25519Kyber768Draft00 => 1216,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
290
third_party/rust/ohttp/src/lib.rs
vendored
290
third_party/rust/ohttp/src/lib.rs
vendored
@ -5,6 +5,7 @@
|
||||
allow(dead_code, unused_imports)
|
||||
)]
|
||||
|
||||
mod config;
|
||||
mod err;
|
||||
pub mod hpke;
|
||||
#[cfg(feature = "nss")]
|
||||
@ -14,11 +15,16 @@ mod rand;
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
mod rh;
|
||||
|
||||
pub use err::Error;
|
||||
pub use crate::{
|
||||
config::{KeyConfig, SymmetricSuite},
|
||||
err::Error,
|
||||
};
|
||||
|
||||
use crate::hpke::{Aead as AeadId, Kdf, Kem};
|
||||
use crate::{
|
||||
err::Res,
|
||||
hpke::{Aead as AeadId, Kdf, Kem},
|
||||
};
|
||||
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
||||
use err::Res;
|
||||
use log::trace;
|
||||
use std::{
|
||||
cmp::max,
|
||||
@ -28,25 +34,21 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "nss")]
|
||||
use nss::random;
|
||||
use crate::nss::random;
|
||||
#[cfg(feature = "nss")]
|
||||
use nss::{
|
||||
use crate::nss::{
|
||||
aead::{Aead, Mode, NONCE_LEN},
|
||||
hkdf::{Hkdf, KeyMechanism},
|
||||
hpke::{generate_key_pair, Config as HpkeConfig, Exporter, HpkeR, HpkeS},
|
||||
PrivateKey, PublicKey,
|
||||
hpke::{Config as HpkeConfig, Exporter, HpkeR, HpkeS},
|
||||
};
|
||||
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
use crate::rand::random;
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
use rh::{
|
||||
use crate::rh::{
|
||||
aead::{Aead, Mode, NONCE_LEN},
|
||||
hkdf::{Hkdf, KeyMechanism},
|
||||
hpke::{
|
||||
derive_key_pair, generate_key_pair, Config as HpkeConfig, Exporter, HpkeR, HpkeS,
|
||||
PrivateKey, PublicKey,
|
||||
},
|
||||
hpke::{Config as HpkeConfig, Exporter, HpkeR, HpkeS},
|
||||
};
|
||||
|
||||
/// The request header is a `KeyId` and 2 each for KEM, KDF, and AEAD identifiers
|
||||
@ -66,187 +68,6 @@ pub fn init() {
|
||||
nss::init();
|
||||
}
|
||||
|
||||
/// A tuple of KDF and AEAD identifiers.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SymmetricSuite {
|
||||
kdf: Kdf,
|
||||
aead: AeadId,
|
||||
}
|
||||
|
||||
impl SymmetricSuite {
|
||||
#[must_use]
|
||||
pub const fn new(kdf: Kdf, aead: AeadId) -> Self {
|
||||
Self { kdf, aead }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn kdf(self) -> Kdf {
|
||||
self.kdf
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aead(self) -> AeadId {
|
||||
self.aead
|
||||
}
|
||||
}
|
||||
|
||||
/// The key configuration of a server. This can be used by both client and server.
|
||||
/// An important invariant of this structure is that it does not include
|
||||
/// any combination of KEM, KDF, and AEAD that is not supported.
|
||||
pub struct KeyConfig {
|
||||
key_id: KeyId,
|
||||
kem: Kem,
|
||||
symmetric: Vec<SymmetricSuite>,
|
||||
sk: Option<PrivateKey>,
|
||||
pk: PublicKey,
|
||||
}
|
||||
|
||||
impl KeyConfig {
|
||||
fn strip_unsupported(symmetric: &mut Vec<SymmetricSuite>, kem: Kem) {
|
||||
symmetric.retain(|s| HpkeConfig::new(kem, s.kdf(), s.aead()).supported());
|
||||
}
|
||||
|
||||
/// Construct a configuration for the server side.
|
||||
/// # Panics
|
||||
/// If the configurations don't include a supported configuration.
|
||||
pub fn new(key_id: u8, kem: Kem, mut symmetric: Vec<SymmetricSuite>) -> Res<Self> {
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
assert!(!symmetric.is_empty());
|
||||
let (sk, pk) = generate_key_pair(kem)?;
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: Some(sk),
|
||||
pk,
|
||||
})
|
||||
}
|
||||
|
||||
/// Derive a configuration for the server side from input keying material,
|
||||
/// using the `DeriveKeyPair` functionality of the HPKE KEM defined here:
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-12.html#section-4>
|
||||
/// # Panics
|
||||
/// If the configurations don't include a supported configuration.
|
||||
#[allow(unused)]
|
||||
pub fn derive(
|
||||
key_id: u8,
|
||||
kem: Kem,
|
||||
mut symmetric: Vec<SymmetricSuite>,
|
||||
ikm: &[u8],
|
||||
) -> Res<Self> {
|
||||
#[cfg(feature = "rust-hpke")]
|
||||
{
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
assert!(!symmetric.is_empty());
|
||||
let (sk, pk) = derive_key_pair(kem, ikm)?;
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: Some(sk),
|
||||
pk,
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "rust-hpke"))]
|
||||
{
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode into a wire format. This shares a format with the core of ECH:
|
||||
///
|
||||
/// ```tls-format
|
||||
/// opaque HpkePublicKey[Npk];
|
||||
/// uint16 HpkeKemId; // Defined in I-D.irtf-cfrg-hpke
|
||||
/// uint16 HpkeKdfId; // Defined in I-D.irtf-cfrg-hpke
|
||||
/// uint16 HpkeAeadId; // Defined in I-D.irtf-cfrg-hpke
|
||||
///
|
||||
/// struct {
|
||||
/// HpkeKdfId kdf_id;
|
||||
/// HpkeAeadId aead_id;
|
||||
/// } ECHCipherSuite;
|
||||
///
|
||||
/// struct {
|
||||
/// uint8 key_id;
|
||||
/// HpkeKemId kem_id;
|
||||
/// HpkePublicKey public_key;
|
||||
/// ECHCipherSuite cipher_suites<4..2^16-4>;
|
||||
/// } ECHKeyConfig;
|
||||
/// ```
|
||||
/// # Panics
|
||||
/// Not as a result of this function.
|
||||
pub fn encode(&self) -> Res<Vec<u8>> {
|
||||
let mut buf = Vec::new();
|
||||
buf.write_u8(self.key_id)?;
|
||||
buf.write_u16::<NetworkEndian>(u16::from(self.kem))?;
|
||||
let pk_buf = self.pk.key_data()?;
|
||||
buf.extend_from_slice(&pk_buf);
|
||||
buf.write_u16::<NetworkEndian>((self.symmetric.len() * 4).try_into()?)?;
|
||||
for s in &self.symmetric {
|
||||
buf.write_u16::<NetworkEndian>(u16::from(s.kdf()))?;
|
||||
buf.write_u16::<NetworkEndian>(u16::from(s.aead()))?;
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Construct a configuration from the encoded server configuration.
|
||||
/// The format of `encoded_config` is the output of `Self::encode`.
|
||||
fn parse(encoded_config: &[u8]) -> Res<Self> {
|
||||
let mut r = BufReader::new(encoded_config);
|
||||
let key_id = r.read_u8()?;
|
||||
let kem = Kem::try_from(r.read_u16::<NetworkEndian>()?)?;
|
||||
|
||||
// Note that the KDF and AEAD doesn't matter here.
|
||||
let kem_config = HpkeConfig::new(kem, Kdf::HkdfSha256, AeadId::Aes128Gcm);
|
||||
if !kem_config.supported() {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
let mut pk_buf = vec![0; kem_config.kem().n_pk()];
|
||||
r.read_exact(&mut pk_buf)?;
|
||||
|
||||
let sym_len = r.read_u16::<NetworkEndian>()?;
|
||||
let mut sym = vec![0; usize::from(sym_len)];
|
||||
r.read_exact(&mut sym)?;
|
||||
if sym.is_empty() || (sym.len() % 4 != 0) {
|
||||
return Err(Error::Format);
|
||||
}
|
||||
let sym_count = sym.len() / 4;
|
||||
let mut sym_r = BufReader::new(&sym[..]);
|
||||
let mut symmetric = Vec::with_capacity(sym_count);
|
||||
for _ in 0..sym_count {
|
||||
let kdf = Kdf::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
|
||||
let aead = AeadId::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
|
||||
symmetric.push(SymmetricSuite::new(kdf, aead));
|
||||
}
|
||||
|
||||
// Check that there was nothing extra.
|
||||
let mut tmp = [0; 1];
|
||||
if r.read(&mut tmp)? > 0 {
|
||||
return Err(Error::Format);
|
||||
}
|
||||
|
||||
Self::strip_unsupported(&mut symmetric, kem);
|
||||
let pk = HpkeR::decode_public_key(kem_config.kem(), &pk_buf)?;
|
||||
|
||||
Ok(Self {
|
||||
key_id,
|
||||
kem,
|
||||
symmetric,
|
||||
sk: None,
|
||||
pk,
|
||||
})
|
||||
}
|
||||
|
||||
fn select(&self, sym: SymmetricSuite) -> Res<HpkeConfig> {
|
||||
if self.symmetric.contains(&sym) {
|
||||
let config = HpkeConfig::new(self.kem, sym.kdf(), sym.aead());
|
||||
Ok(config)
|
||||
} else {
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the info parameter we use to initialize an `HpkeS` instance.
|
||||
fn build_info(key_id: KeyId, config: HpkeConfig) -> Res<Vec<u8>> {
|
||||
let mut info = Vec::with_capacity(INFO_LEN);
|
||||
@ -270,11 +91,8 @@ pub struct ClientRequest {
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl ClientRequest {
|
||||
/// Reads an encoded configuration and constructs a single use client sender.
|
||||
/// See `KeyConfig::encode` for the structure details.
|
||||
#[allow(clippy::similar_names)] // for `sk_s` and `pk_s`
|
||||
pub fn new(encoded_config: &[u8]) -> Res<Self> {
|
||||
let mut config = KeyConfig::parse(encoded_config)?;
|
||||
/// Construct a `ClientRequest` from a specific `KeyConfig` instance.
|
||||
pub fn from_config(config: &mut KeyConfig) -> Res<Self> {
|
||||
// TODO(mt) choose the best config, not just the first.
|
||||
let selected = config.select(config.symmetric[0])?;
|
||||
|
||||
@ -287,6 +105,25 @@ impl ClientRequest {
|
||||
Ok(Self { hpke, header })
|
||||
}
|
||||
|
||||
/// Reads an encoded configuration and constructs a single use client sender.
|
||||
/// See `KeyConfig::decode` for the structure details.
|
||||
pub fn from_encoded_config(encoded_config: &[u8]) -> Res<Self> {
|
||||
let mut config = KeyConfig::decode(encoded_config)?;
|
||||
Self::from_config(&mut config)
|
||||
}
|
||||
|
||||
/// Reads an encoded list of configurations and constructs a single use client sender
|
||||
/// from the first supported configuration.
|
||||
/// See `KeyConfig::decode_list` for the structure details.
|
||||
pub fn from_encoded_config_list(encoded_config_list: &[u8]) -> Res<Self> {
|
||||
let mut configs = KeyConfig::decode_list(encoded_config_list)?;
|
||||
if let Some(mut config) = configs.pop() {
|
||||
Self::from_config(&mut config)
|
||||
} else {
|
||||
Err(Error::Unsupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulate a request. This consumes this object.
|
||||
/// This produces a response handler and the bytes of an encapsulated request.
|
||||
pub fn encapsulate(mut self, request: &[u8]) -> Res<(Vec<u8>, ClientResponse)> {
|
||||
@ -312,6 +149,7 @@ impl ClientRequest {
|
||||
/// It holds a single key pair and can generate a configuration.
|
||||
/// (A more complex server would have multiple key pairs. This is simple.)
|
||||
#[cfg(feature = "server")]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Server {
|
||||
config: KeyConfig,
|
||||
}
|
||||
@ -336,7 +174,7 @@ impl Server {
|
||||
/// # Panics
|
||||
/// Not as a consequence of this code, but Rust won't know that for sure.
|
||||
#[allow(clippy::similar_names)] // for kem_id and key_id
|
||||
pub fn decapsulate(&mut self, enc_request: &[u8]) -> Res<(Vec<u8>, ServerResponse)> {
|
||||
pub fn decapsulate(&self, enc_request: &[u8]) -> Res<(Vec<u8>, ServerResponse)> {
|
||||
if enc_request.len() < REQUEST_HEADER_LEN {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
@ -364,7 +202,7 @@ impl Server {
|
||||
let mut hpke = HpkeR::new(
|
||||
cfg,
|
||||
&self.config.pk,
|
||||
self.config.sk.as_mut().unwrap(),
|
||||
self.config.sk.as_ref().unwrap(),
|
||||
&enc,
|
||||
&info,
|
||||
)?;
|
||||
@ -475,9 +313,10 @@ impl ClientResponse {
|
||||
#[cfg(all(test, feature = "client", feature = "server"))]
|
||||
mod test {
|
||||
use crate::{
|
||||
config::SymmetricSuite,
|
||||
err::Res,
|
||||
hpke::{Aead, Kdf, Kem},
|
||||
ClientRequest, Error, KeyConfig, KeyId, Server, SymmetricSuite,
|
||||
ClientRequest, Error, KeyConfig, KeyId, Server,
|
||||
};
|
||||
use log::trace;
|
||||
use std::{fmt::Debug, io::ErrorKind};
|
||||
@ -497,7 +336,7 @@ mod test {
|
||||
|
||||
fn init() {
|
||||
crate::init();
|
||||
let _ = env_logger::try_init();
|
||||
_ = env_logger::try_init(); // ignore errors here
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -505,11 +344,11 @@ mod test {
|
||||
init();
|
||||
|
||||
let server_config = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap();
|
||||
let mut server = Server::new(server_config).unwrap();
|
||||
let server = Server::new(server_config).unwrap();
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
trace!("Config: {}", hex::encode(&encoded_config));
|
||||
|
||||
let client = ClientRequest::new(&encoded_config).unwrap();
|
||||
let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
|
||||
let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
|
||||
trace!("Request: {}", hex::encode(REQUEST));
|
||||
trace!("Encapsulated Request: {}", hex::encode(&enc_request));
|
||||
@ -530,12 +369,12 @@ mod test {
|
||||
init();
|
||||
|
||||
let server_config = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap();
|
||||
let mut server = Server::new(server_config).unwrap();
|
||||
let server = Server::new(server_config).unwrap();
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
|
||||
let client1 = ClientRequest::new(&encoded_config).unwrap();
|
||||
let client1 = ClientRequest::from_encoded_config(&encoded_config).unwrap();
|
||||
let (enc_request1, client_response1) = client1.encapsulate(REQUEST).unwrap();
|
||||
let client2 = ClientRequest::new(&encoded_config).unwrap();
|
||||
let client2 = ClientRequest::from_encoded_config(&encoded_config).unwrap();
|
||||
let (enc_request2, client_response2) = client2.encapsulate(REQUEST).unwrap();
|
||||
assert_ne!(enc_request1, enc_request2);
|
||||
|
||||
@ -570,10 +409,10 @@ mod test {
|
||||
init();
|
||||
|
||||
let server_config = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap();
|
||||
let mut server = Server::new(server_config).unwrap();
|
||||
let server = Server::new(server_config).unwrap();
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
|
||||
let client = ClientRequest::new(&encoded_config).unwrap();
|
||||
let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
|
||||
let (enc_request, _) = client.encapsulate(REQUEST).unwrap();
|
||||
|
||||
let res = server.decapsulate(&enc_request[..cut]);
|
||||
@ -601,10 +440,10 @@ mod test {
|
||||
init();
|
||||
|
||||
let server_config = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap();
|
||||
let mut server = Server::new(server_config).unwrap();
|
||||
let server = Server::new(server_config).unwrap();
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
|
||||
let client = ClientRequest::new(&encoded_config).unwrap();
|
||||
let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
|
||||
let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
|
||||
|
||||
let (request, server_response) = server.decapsulate(&enc_request).unwrap();
|
||||
@ -643,7 +482,7 @@ mod test {
|
||||
|
||||
init();
|
||||
|
||||
let config = KeyConfig::parse(EXPECTED_CONFIG).unwrap();
|
||||
let config = KeyConfig::decode(EXPECTED_CONFIG).unwrap();
|
||||
|
||||
let new_config = KeyConfig::derive(KEY_ID, KEM, Vec::from(SYMMETRIC), IKM).unwrap();
|
||||
assert_eq!(config.key_id, new_config.key_id);
|
||||
@ -654,4 +493,31 @@ mod test {
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
assert_eq!(EXPECTED_CONFIG, encoded_config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_from_config_list() {
|
||||
init();
|
||||
|
||||
let server_config = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap();
|
||||
let server = Server::new(server_config).unwrap();
|
||||
let encoded_config = server.config().encode().unwrap();
|
||||
|
||||
let mut header: [u8; 2] = [0; 2];
|
||||
header[0] = u8::try_from((encoded_config.len() & 0xFF00) >> 8).unwrap();
|
||||
header[1] = u8::try_from(encoded_config.len() & 0xFF).unwrap();
|
||||
let mut encoded_config_list = Vec::new();
|
||||
encoded_config_list.extend(header.to_vec());
|
||||
encoded_config_list.extend(encoded_config);
|
||||
|
||||
let client = ClientRequest::from_encoded_config_list(&encoded_config_list).unwrap();
|
||||
let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
|
||||
|
||||
let (request, server_response) = server.decapsulate(&enc_request).unwrap();
|
||||
assert_eq!(&request[..], REQUEST);
|
||||
|
||||
let enc_response = server_response.encapsulate(RESPONSE).unwrap();
|
||||
|
||||
let response = client_response.decapsulate(&enc_response).unwrap();
|
||||
assert_eq!(&response[..], RESPONSE);
|
||||
}
|
||||
}
|
||||
|
2
third_party/rust/ohttp/src/nss/aead.rs
vendored
2
third_party/rust/ohttp/src/nss/aead.rs
vendored
@ -44,6 +44,8 @@ unsafe fn destroy_aead_context(ctx: *mut PK11Context) {
|
||||
}
|
||||
scoped_ptr!(Context, PK11Context, destroy_aead_context);
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Mode {
|
||||
Encrypt,
|
||||
|
29
third_party/rust/ohttp/src/nss/hpke.rs
vendored
29
third_party/rust/ohttp/src/nss/hpke.rs
vendored
@ -86,6 +86,8 @@ impl HpkeContext {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for HpkeContext {}
|
||||
|
||||
impl Exporter for HpkeContext {
|
||||
fn export(&self, info: &[u8], len: usize) -> Res<SymKey> {
|
||||
let mut out: *mut sys::PK11SymKey = null_mut();
|
||||
@ -168,7 +170,7 @@ impl HpkeR {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
pk_r: &PublicKey,
|
||||
sk_r: &mut PrivateKey,
|
||||
sk_r: &PrivateKey,
|
||||
enc: &[u8],
|
||||
info: &[u8],
|
||||
) -> Res<Self> {
|
||||
@ -290,7 +292,7 @@ pub fn generate_key_pair(kem: Kem) -> Res<(PrivateKey, PublicKey)> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{generate_key_pair, Config, HpkeR, HpkeS};
|
||||
use super::{generate_key_pair, Config, HpkeContext, HpkeR, HpkeS};
|
||||
use crate::{hpke::Aead, init};
|
||||
|
||||
const INFO: &[u8] = b"info";
|
||||
@ -302,9 +304,9 @@ mod test {
|
||||
fn make() {
|
||||
init();
|
||||
let cfg = Config::default();
|
||||
let (mut sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let (sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let hpke_s = HpkeS::new(cfg, &mut pk_r, INFO).unwrap();
|
||||
let _hpke_r = HpkeR::new(cfg, &pk_r, &mut sk_r, &hpke_s.enc().unwrap(), INFO).unwrap();
|
||||
let _hpke_r = HpkeR::new(cfg, &pk_r, &sk_r, &hpke_s.enc().unwrap(), INFO).unwrap();
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)] // for sk_x and pk_x
|
||||
@ -316,7 +318,7 @@ mod test {
|
||||
..Config::default()
|
||||
};
|
||||
assert!(cfg.supported());
|
||||
let (mut sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let (sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
|
||||
// Send
|
||||
let mut hpke_s = HpkeS::new(cfg, &mut pk_r, INFO).unwrap();
|
||||
@ -324,7 +326,7 @@ mod test {
|
||||
let ct = hpke_s.seal(AAD, PT).unwrap();
|
||||
|
||||
// Receive
|
||||
let mut hpke_r = HpkeR::new(cfg, &pk_r, &mut sk_r, &enc, INFO).unwrap();
|
||||
let mut hpke_r = HpkeR::new(cfg, &pk_r, &sk_r, &enc, INFO).unwrap();
|
||||
let pt = hpke_r.open(AAD, &ct).unwrap();
|
||||
assert_eq!(&pt[..], PT);
|
||||
}
|
||||
@ -338,4 +340,19 @@ mod test {
|
||||
fn seal_open_chacha() {
|
||||
seal_open(Aead::ChaCha20Poly1305);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_hpkecontext() {
|
||||
use std::{sync::mpsc, thread};
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
let context = HpkeContext::new(Config::default());
|
||||
tx.send(context).unwrap();
|
||||
});
|
||||
|
||||
let context = rx.recv().unwrap();
|
||||
drop(context);
|
||||
}
|
||||
}
|
||||
|
4
third_party/rust/ohttp/src/nss/mod.rs
vendored
4
third_party/rust/ohttp/src/nss/mod.rs
vendored
@ -11,7 +11,7 @@ pub mod aead;
|
||||
pub mod hkdf;
|
||||
pub mod hpke;
|
||||
|
||||
pub use self::p11::{random, PrivateKey, PublicKey, SymKey};
|
||||
pub use self::p11::{random, PrivateKey, PublicKey};
|
||||
use err::secstatus_to_res;
|
||||
pub use err::Error;
|
||||
use lazy_static::lazy_static;
|
||||
@ -37,7 +37,7 @@ enum NssLoaded {
|
||||
|
||||
impl Drop for NssLoaded {
|
||||
fn drop(&mut self) {
|
||||
if *self == Self::NoDb {
|
||||
if matches!(self, Self::NoDb) {
|
||||
unsafe {
|
||||
secstatus_to_res(nss_init::NSS_Shutdown()).expect("NSS Shutdown failed");
|
||||
}
|
||||
|
4
third_party/rust/ohttp/src/nss/p11.rs
vendored
4
third_party/rust/ohttp/src/nss/p11.rs
vendored
@ -67,7 +67,7 @@ macro_rules! scoped_ptr {
|
||||
|
||||
impl Drop for $scoped {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { $dtor(self.ptr) };
|
||||
unsafe { $dtor(self.ptr) };
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -240,7 +240,7 @@ impl<'a, T: Sized + 'a> ParamItem<'a, T> {
|
||||
};
|
||||
Self {
|
||||
item,
|
||||
marker: PhantomData::default(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
8
third_party/rust/ohttp/src/rh/aead.rs
vendored
8
third_party/rust/ohttp/src/rh/aead.rs
vendored
@ -54,7 +54,7 @@ impl AeadEngine {
|
||||
/// A switch-hitting AEAD that uses a selected primitive.
|
||||
pub struct Aead {
|
||||
mode: Mode,
|
||||
aead: AeadEngine,
|
||||
engine: AeadEngine,
|
||||
nonce_base: [u8; NONCE_LEN],
|
||||
seq: SequenceNumber,
|
||||
}
|
||||
@ -80,7 +80,7 @@ impl Aead {
|
||||
};
|
||||
Ok(Self {
|
||||
mode,
|
||||
aead,
|
||||
engine: aead,
|
||||
nonce_base,
|
||||
seq: 0,
|
||||
})
|
||||
@ -105,14 +105,14 @@ impl Aead {
|
||||
// A copy for the nonce generator to write into. But we don't use the value.
|
||||
let nonce = self.nonce(self.seq);
|
||||
self.seq += 1;
|
||||
let ct = self.aead.encrypt(&nonce, Payload { msg: pt, aad })?;
|
||||
let ct = self.engine.encrypt(&nonce, Payload { msg: pt, aad })?;
|
||||
Ok(ct)
|
||||
}
|
||||
|
||||
pub fn open(&mut self, aad: &[u8], seq: SequenceNumber, ct: &[u8]) -> Res<Vec<u8>> {
|
||||
assert_eq!(self.mode, Mode::Decrypt);
|
||||
let nonce = self.nonce(seq);
|
||||
let pt = self.aead.decrypt(&nonce, Payload { msg: ct, aad })?;
|
||||
let pt = self.engine.decrypt(&nonce, Payload { msg: ct, aad })?;
|
||||
Ok(pt)
|
||||
}
|
||||
}
|
||||
|
212
third_party/rust/ohttp/src/rh/hpke.rs
vendored
212
third_party/rust/ohttp/src/rh/hpke.rs
vendored
@ -3,15 +3,23 @@ use crate::{
|
||||
hpke::{Aead, Kdf, Kem},
|
||||
Error, Res,
|
||||
};
|
||||
use ::hpke::{
|
||||
aead::{AeadTag, AesGcm128, ChaCha20Poly1305},
|
||||
|
||||
#[cfg(not(feature = "pq"))]
|
||||
use ::hpke as rust_hpke;
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
use ::hpke_pq as rust_hpke;
|
||||
|
||||
use rust_hpke::{
|
||||
aead::{AeadCtxR, AeadCtxS, AeadTag, AesGcm128, ChaCha20Poly1305},
|
||||
kdf::HkdfSha256,
|
||||
kem::{Kem as HpkeKem, X25519HkdfSha256},
|
||||
kex::{KeyExchange, X25519},
|
||||
op_mode::{OpModeR, OpModeS},
|
||||
setup::{setup_receiver, setup_sender},
|
||||
AeadCtxR, AeadCtxS, Deserializable, EncappedKey, Serializable,
|
||||
kem::{Kem as KemTrait, X25519HkdfSha256},
|
||||
setup_receiver, setup_sender, Deserializable, OpModeR, OpModeS, Serializable,
|
||||
};
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
use rust_hpke::kem::X25519Kyber768Draft00;
|
||||
|
||||
use ::rand::thread_rng;
|
||||
use log::trace;
|
||||
use std::ops::Deref;
|
||||
@ -57,8 +65,13 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone)]
|
||||
pub enum PublicKey {
|
||||
X25519(<<X25519HkdfSha256 as HpkeKem>::Kex as KeyExchange>::PublicKey),
|
||||
X25519(<X25519HkdfSha256 as KemTrait>::PublicKey),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
X25519Kyber768Draft00(<X25519Kyber768Draft00 as KemTrait>::PublicKey),
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
@ -66,6 +79,9 @@ impl PublicKey {
|
||||
pub fn key_data(&self) -> Res<Vec<u8>> {
|
||||
Ok(match self {
|
||||
Self::X25519(k) => Vec::from(k.to_bytes().as_slice()),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(k) => Vec::from(k.to_bytes().as_slice()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -80,8 +96,13 @@ impl std::fmt::Debug for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone)]
|
||||
pub enum PrivateKey {
|
||||
X25519(<<X25519HkdfSha256 as HpkeKem>::Kex as KeyExchange>::PrivateKey),
|
||||
X25519(<X25519HkdfSha256 as KemTrait>::PrivateKey),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
X25519Kyber768Draft00(<X25519Kyber768Draft00 as KemTrait>::PrivateKey),
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
@ -89,6 +110,9 @@ impl PrivateKey {
|
||||
pub fn key_data(&self) -> Res<Vec<u8>> {
|
||||
Ok(match self {
|
||||
Self::X25519(k) => Vec::from(k.to_bytes().as_slice()),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(k) => Vec::from(k.to_bytes().as_slice()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -110,12 +134,25 @@ enum SenderContextX25519HkdfSha256HkdfSha256 {
|
||||
ChaCha20Poly1305(Box<AeadCtxS<ChaCha20Poly1305, HkdfSha256, X25519HkdfSha256>>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
enum SenderContextX25519Kyber768Draft00HkdfSha256 {
|
||||
AesGcm128(Box<AeadCtxS<AesGcm128, HkdfSha256, X25519Kyber768Draft00>>),
|
||||
}
|
||||
|
||||
enum SenderContextX25519HkdfSha256 {
|
||||
HkdfSha256(SenderContextX25519HkdfSha256HkdfSha256),
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
enum SenderContextX25519Kyber768Draft00 {
|
||||
HkdfSha256(SenderContextX25519Kyber768Draft00HkdfSha256),
|
||||
}
|
||||
|
||||
enum SenderContext {
|
||||
X25519HkdfSha256(SenderContextX25519HkdfSha256),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
X25519Kyber768Draft00(SenderContextX25519Kyber768Draft00),
|
||||
}
|
||||
|
||||
impl SenderContext {
|
||||
@ -124,13 +161,21 @@ impl SenderContext {
|
||||
Self::X25519HkdfSha256(SenderContextX25519HkdfSha256::HkdfSha256(
|
||||
SenderContextX25519HkdfSha256HkdfSha256::AesGcm128(context),
|
||||
)) => {
|
||||
let tag = context.seal(plaintext, aad)?;
|
||||
let tag = context.seal_in_place_detached(plaintext, aad)?;
|
||||
Vec::from(tag.to_bytes().as_slice())
|
||||
}
|
||||
Self::X25519HkdfSha256(SenderContextX25519HkdfSha256::HkdfSha256(
|
||||
SenderContextX25519HkdfSha256HkdfSha256::ChaCha20Poly1305(context),
|
||||
)) => {
|
||||
let tag = context.seal(plaintext, aad)?;
|
||||
let tag = context.seal_in_place_detached(plaintext, aad)?;
|
||||
Vec::from(tag.to_bytes().as_slice())
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(SenderContextX25519Kyber768Draft00::HkdfSha256(
|
||||
SenderContextX25519Kyber768Draft00HkdfSha256::AesGcm128(context),
|
||||
)) => {
|
||||
let tag = context.seal_in_place_detached(plaintext, aad)?;
|
||||
Vec::from(tag.to_bytes().as_slice())
|
||||
}
|
||||
})
|
||||
@ -148,6 +193,13 @@ impl SenderContext {
|
||||
)) => {
|
||||
context.export(info, out_buf)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(SenderContextX25519Kyber768Draft00::HkdfSha256(
|
||||
SenderContextX25519Kyber768Draft00HkdfSha256::AesGcm128(context),
|
||||
)) => {
|
||||
context.export(info, out_buf)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -171,7 +223,7 @@ impl HpkeS {
|
||||
|
||||
macro_rules! dispatch_hpkes_new {
|
||||
{
|
||||
($c:expr, $pk:expr, $csprng:expr): [$({
|
||||
($c:expr, $pk:expr, $csprng:expr): [$( $(#[$meta:meta])* {
|
||||
$kemid:path => $kem:path,
|
||||
$kdfid:path => $kdf:path,
|
||||
$aeadid:path => $aead:path,
|
||||
@ -180,6 +232,7 @@ impl HpkeS {
|
||||
} => {
|
||||
match ($c, $pk) {
|
||||
$(
|
||||
$(#[$meta])*
|
||||
(
|
||||
Config {
|
||||
kem: $kemid,
|
||||
@ -194,13 +247,17 @@ impl HpkeS {
|
||||
info,
|
||||
$csprng,
|
||||
)?;
|
||||
($ctxt1($ctxt2($ctxt3(Box::new(context)))), enc)
|
||||
(
|
||||
$ctxt1($ctxt2($ctxt3(Box::new(context)))),
|
||||
Vec::from(enc.to_bytes().as_slice()),
|
||||
)
|
||||
}
|
||||
)*
|
||||
_ => return Err(Error::InvalidKeyType),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let (context, enc) = dispatch_hpkes_new! { (config, pk_r, &mut csprng): [
|
||||
{
|
||||
Kem::X25519Sha256 => X25519HkdfSha256,
|
||||
@ -220,8 +277,19 @@ impl HpkeS {
|
||||
SenderContextX25519HkdfSha256::HkdfSha256,
|
||||
SenderContextX25519HkdfSha256HkdfSha256::ChaCha20Poly1305,
|
||||
},
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
{
|
||||
Kem::X25519Kyber768Draft00 => X25519Kyber768Draft00,
|
||||
Kdf::HkdfSha256 => HkdfSha256,
|
||||
Aead::Aes128Gcm => AesGcm128,
|
||||
PublicKey::X25519Kyber768Draft00,
|
||||
SenderContext::X25519Kyber768Draft00,
|
||||
SenderContextX25519Kyber768Draft00::HkdfSha256,
|
||||
SenderContextX25519Kyber768Draft00HkdfSha256::AesGcm128,
|
||||
},
|
||||
]};
|
||||
let enc = Vec::from(enc.to_bytes().as_slice());
|
||||
|
||||
Ok(Self {
|
||||
context,
|
||||
enc,
|
||||
@ -267,12 +335,25 @@ enum ReceiverContextX25519HkdfSha256HkdfSha256 {
|
||||
ChaCha20Poly1305(Box<AeadCtxR<ChaCha20Poly1305, HkdfSha256, X25519HkdfSha256>>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
enum ReceiverContextX25519Kyber768Draft00HkdfSha256 {
|
||||
AesGcm128(Box<AeadCtxR<AesGcm128, HkdfSha256, X25519Kyber768Draft00>>),
|
||||
}
|
||||
|
||||
enum ReceiverContextX25519HkdfSha256 {
|
||||
HkdfSha256(ReceiverContextX25519HkdfSha256HkdfSha256),
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
enum ReceiverContextX25519Kyber768Draft00 {
|
||||
HkdfSha256(ReceiverContextX25519Kyber768Draft00HkdfSha256),
|
||||
}
|
||||
|
||||
enum ReceiverContext {
|
||||
X25519HkdfSha256(ReceiverContextX25519HkdfSha256),
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
X25519Kyber768Draft00(ReceiverContextX25519Kyber768Draft00),
|
||||
}
|
||||
|
||||
impl ReceiverContext {
|
||||
@ -284,10 +365,10 @@ impl ReceiverContext {
|
||||
if ciphertext.len() < AeadTag::<AesGcm128>::size() {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
let (ct, tag) =
|
||||
let (ct, tag_slice) =
|
||||
ciphertext.split_at_mut(ciphertext.len() - AeadTag::<AesGcm128>::size());
|
||||
let tag = AeadTag::<AesGcm128>::from_bytes(tag)?;
|
||||
context.open(ct, aad, &tag)?;
|
||||
let tag = AeadTag::<AesGcm128>::from_bytes(tag_slice)?;
|
||||
context.open_in_place_detached(ct, aad, &tag)?;
|
||||
ct
|
||||
}
|
||||
Self::X25519HkdfSha256(ReceiverContextX25519HkdfSha256::HkdfSha256(
|
||||
@ -296,10 +377,24 @@ impl ReceiverContext {
|
||||
if ciphertext.len() < AeadTag::<ChaCha20Poly1305>::size() {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
let (ct, tag) =
|
||||
let (ct, tag_slice) =
|
||||
ciphertext.split_at_mut(ciphertext.len() - AeadTag::<ChaCha20Poly1305>::size());
|
||||
let tag = AeadTag::<ChaCha20Poly1305>::from_bytes(tag)?;
|
||||
context.open(ct, aad, &tag)?;
|
||||
let tag = AeadTag::<ChaCha20Poly1305>::from_bytes(tag_slice)?;
|
||||
context.open_in_place_detached(ct, aad, &tag)?;
|
||||
ct
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(ReceiverContextX25519Kyber768Draft00::HkdfSha256(
|
||||
ReceiverContextX25519Kyber768Draft00HkdfSha256::AesGcm128(context),
|
||||
)) => {
|
||||
if ciphertext.len() < AeadTag::<AesGcm128>::size() {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
let (ct, tag_slice) =
|
||||
ciphertext.split_at_mut(ciphertext.len() - AeadTag::<AesGcm128>::size());
|
||||
let tag = AeadTag::<AesGcm128>::from_bytes(tag_slice)?;
|
||||
context.open_in_place_detached(ct, aad, &tag)?;
|
||||
ct
|
||||
}
|
||||
})
|
||||
@ -317,6 +412,13 @@ impl ReceiverContext {
|
||||
)) => {
|
||||
context.export(info, out_buf)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Self::X25519Kyber768Draft00(ReceiverContextX25519Kyber768Draft00::HkdfSha256(
|
||||
ReceiverContextX25519Kyber768Draft00HkdfSha256::AesGcm128(context),
|
||||
)) => {
|
||||
context.export(info, out_buf)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -334,13 +436,13 @@ impl HpkeR {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
_pk_r: &PublicKey,
|
||||
sk_r: &mut PrivateKey,
|
||||
sk_r: &PrivateKey,
|
||||
enc: &[u8],
|
||||
info: &[u8],
|
||||
) -> Res<Self> {
|
||||
macro_rules! dispatch_hpker_new {
|
||||
{
|
||||
($c:ident, $sk:ident): [$({
|
||||
($c:ident, $sk:ident): [$( $(#[$meta:meta])* {
|
||||
$kemid:path => $kem:path,
|
||||
$kdfid:path => $kdf:path,
|
||||
$aeadid:path => $aead:path,
|
||||
@ -349,6 +451,7 @@ impl HpkeR {
|
||||
} => {
|
||||
match ($c, $sk) {
|
||||
$(
|
||||
$(#[$meta])*
|
||||
(
|
||||
Config {
|
||||
kem: $kemid,
|
||||
@ -357,7 +460,7 @@ impl HpkeR {
|
||||
},
|
||||
$ske(sk_r),
|
||||
) => {
|
||||
let enc = EncappedKey::from_bytes(enc)?;
|
||||
let enc = <$kem as KemTrait>::EncappedKey::from_bytes(enc)?;
|
||||
let context = setup_receiver::<$aead, $kdf, $kem>(
|
||||
&OpModeR::Base,
|
||||
sk_r,
|
||||
@ -390,7 +493,19 @@ impl HpkeR {
|
||||
ReceiverContextX25519HkdfSha256::HkdfSha256,
|
||||
ReceiverContextX25519HkdfSha256HkdfSha256::ChaCha20Poly1305,
|
||||
},
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
{
|
||||
Kem::X25519Kyber768Draft00 => X25519Kyber768Draft00,
|
||||
Kdf::HkdfSha256 => HkdfSha256,
|
||||
Aead::Aes128Gcm => AesGcm128,
|
||||
PrivateKey::X25519Kyber768Draft00,
|
||||
ReceiverContext::X25519Kyber768Draft00,
|
||||
ReceiverContextX25519Kyber768Draft00::HkdfSha256,
|
||||
ReceiverContextX25519Kyber768Draft00HkdfSha256::AesGcm128,
|
||||
},
|
||||
]};
|
||||
|
||||
Ok(Self { context, config })
|
||||
}
|
||||
|
||||
@ -401,8 +516,13 @@ impl HpkeR {
|
||||
pub fn decode_public_key(kem: Kem, k: &[u8]) -> Res<PublicKey> {
|
||||
Ok(match kem {
|
||||
Kem::X25519Sha256 => {
|
||||
PublicKey::X25519(<X25519 as KeyExchange>::PublicKey::from_bytes(k)?)
|
||||
PublicKey::X25519(<X25519HkdfSha256 as KemTrait>::PublicKey::from_bytes(k)?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Kem::X25519Kyber768Draft00 => PublicKey::X25519Kyber768Draft00(
|
||||
<X25519Kyber768Draft00 as KemTrait>::PublicKey::from_bytes(k)?,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@ -438,6 +558,15 @@ pub fn generate_key_pair(kem: Kem) -> Res<(PrivateKey, PublicKey)> {
|
||||
let (sk, pk) = X25519HkdfSha256::gen_keypair(&mut csprng);
|
||||
(PrivateKey::X25519(sk), PublicKey::X25519(pk))
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Kem::X25519Kyber768Draft00 => {
|
||||
let (sk, pk) = X25519Kyber768Draft00::gen_keypair(&mut csprng);
|
||||
(
|
||||
PrivateKey::X25519Kyber768Draft00(sk),
|
||||
PublicKey::X25519Kyber768Draft00(pk),
|
||||
)
|
||||
}
|
||||
};
|
||||
trace!("Generated key pair: sk={:?} pk={:?}", sk, pk);
|
||||
Ok((sk, pk))
|
||||
@ -450,6 +579,15 @@ pub fn derive_key_pair(kem: Kem, ikm: &[u8]) -> Res<(PrivateKey, PublicKey)> {
|
||||
let (sk, pk) = X25519HkdfSha256::derive_keypair(ikm);
|
||||
(PrivateKey::X25519(sk), PublicKey::X25519(pk))
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
Kem::X25519Kyber768Draft00 => {
|
||||
let (sk, pk) = X25519Kyber768Draft00::derive_keypair(ikm);
|
||||
(
|
||||
PrivateKey::X25519Kyber768Draft00(sk),
|
||||
PublicKey::X25519Kyber768Draft00(pk),
|
||||
)
|
||||
}
|
||||
};
|
||||
trace!("Derived key pair: sk={:?} pk={:?}", sk, pk);
|
||||
Ok((sk, pk))
|
||||
@ -458,7 +596,10 @@ pub fn derive_key_pair(kem: Kem, ikm: &[u8]) -> Res<(PrivateKey, PublicKey)> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{generate_key_pair, Config, HpkeR, HpkeS};
|
||||
use crate::{hpke::Aead, init};
|
||||
use crate::{
|
||||
hpke::{Aead, Kem},
|
||||
init,
|
||||
};
|
||||
|
||||
const INFO: &[u8] = b"info";
|
||||
const AAD: &[u8] = b"aad";
|
||||
@ -469,21 +610,22 @@ mod test {
|
||||
fn make() {
|
||||
init();
|
||||
let cfg = Config::default();
|
||||
let (mut sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let (sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let hpke_s = HpkeS::new(cfg, &mut pk_r, INFO).unwrap();
|
||||
let _hpke_r = HpkeR::new(cfg, &pk_r, &mut sk_r, &hpke_s.enc().unwrap(), INFO).unwrap();
|
||||
let _hpke_r = HpkeR::new(cfg, &pk_r, &sk_r, &hpke_s.enc().unwrap(), INFO).unwrap();
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)] // for sk_x and pk_x
|
||||
fn seal_open(aead: Aead) {
|
||||
fn seal_open(aead: Aead, kem: Kem) {
|
||||
// Setup
|
||||
init();
|
||||
let cfg = Config {
|
||||
kem,
|
||||
aead,
|
||||
..Config::default()
|
||||
};
|
||||
assert!(cfg.supported());
|
||||
let (mut sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
let (sk_r, mut pk_r) = generate_key_pair(cfg.kem()).unwrap();
|
||||
|
||||
// Send
|
||||
let mut hpke_s = HpkeS::new(cfg, &mut pk_r, INFO).unwrap();
|
||||
@ -491,18 +633,24 @@ mod test {
|
||||
let ct = hpke_s.seal(AAD, PT).unwrap();
|
||||
|
||||
// Receive
|
||||
let mut hpke_r = HpkeR::new(cfg, &pk_r, &mut sk_r, &enc, INFO).unwrap();
|
||||
let mut hpke_r = HpkeR::new(cfg, &pk_r, &sk_r, &enc, INFO).unwrap();
|
||||
let pt = hpke_r.open(AAD, &ct).unwrap();
|
||||
assert_eq!(&pt[..], PT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seal_open_gcm() {
|
||||
seal_open(Aead::Aes128Gcm);
|
||||
seal_open(Aead::Aes128Gcm, Kem::X25519Sha256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seal_open_chacha() {
|
||||
seal_open(Aead::ChaCha20Poly1305);
|
||||
seal_open(Aead::ChaCha20Poly1305, Kem::X25519Sha256);
|
||||
}
|
||||
|
||||
#[cfg(feature = "pq")]
|
||||
#[test]
|
||||
fn seal_open_xyber768d00() {
|
||||
seal_open(Aead::Aes128Gcm, Kem::X25519Kyber768Draft00);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ cstr = "0.2"
|
||||
viaduct = "0.1"
|
||||
url = "2.1"
|
||||
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||
ohttp = { version = "0.3", default-features = false, features = ["gecko", "nss", "client"] }
|
||||
ohttp = { version = "0.5", default-features = false, features = ["gecko", "nss", "client"] }
|
||||
bhttp = "0.3"
|
||||
thiserror = "1.0"
|
||||
mozbuild = "0.1"
|
||||
|
@ -106,7 +106,7 @@ fn ohttp_upload(upload_request: PingUploadRequest) -> Result<UploadResult, Viadu
|
||||
ohttp::init();
|
||||
});
|
||||
|
||||
let ohttp_request = ohttp::ClientRequest::new(config)?;
|
||||
let ohttp_request = ohttp::ClientRequest::from_encoded_config(config)?;
|
||||
let (capsule, ohttp_response) = ohttp_request.encapsulate(&binary_request)?;
|
||||
|
||||
const OHTTP_RELAY_URL: &str = "https://mozilla-ohttp.fastly-edge.com/";
|
||||
|
Loading…
Reference in New Issue
Block a user