/// The request header is a `KeyId` and 2 each for KEM, KDF, and AEAD identifiers const REQUEST_HEADER_LEN: usize = size_of::<KeyId>() + 6; const INFO_REQUEST: &[u8] = b"message/bhttp request"; /// The info used for HPKE export is `INFO_REQUEST`, a zero byte, and the header. const INFO_LEN: usize = INFO_REQUEST.len() + 1 + REQUEST_HEADER_LEN; const LABEL_RESPONSE: &[u8] = b"message/bhttp response"; const INFO_KEY: &[u8] = b"key"; const INFO_NONCE: &[u8] = b"nonce";
/// The type of a key identifier. pubtype KeyId = u8;
/// Construct the info parameter we use to initialize an `HpkeS` instance. fn build_info(key_id: KeyId, config: HpkeConfig) -> Res<Vec<u8>> { letmut info = Vec::with_capacity(INFO_LEN);
info.extend_from_slice(INFO_REQUEST);
info.push(0);
info.write_u8(key_id)?;
info.write_u16::<NetworkEndian>(u16::from(config.kem()))?;
info.write_u16::<NetworkEndian>(u16::from(config.kdf()))?;
info.write_u16::<NetworkEndian>(u16::from(config.aead()))?;
trace!("HPKE info: {}", hex::encode(&info));
Ok(info)
}
/// This is the sort of information we expect to receive from the receiver. /// This might not be necessary if we agree on a format. #[cfg(feature = "client")] pubstruct ClientRequest {
hpke: HpkeS,
header: Vec<u8>,
}
#[cfg(feature = "client")] impl ClientRequest { /// Construct a `ClientRequest` from a specific `KeyConfig` instance. pubfn from_config(config: &mut KeyConfig) -> Res<Self> { // TODO(mt) choose the best config, not just the first. let selected = config.select(config.symmetric[0])?;
// Build the info, which contains the message header. let info = build_info(config.key_id, selected)?; let hpke = HpkeS::new(selected, &mut config.pk, &info)?;
/// Reads an encoded configuration and constructs a single use client sender. /// See `KeyConfig::decode` for the structure details. pubfn from_encoded_config(encoded_config: &[u8]) -> Res<Self> { letmut 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. pubfn from_encoded_config_list(encoded_config_list: &[u8]) -> Res<Self> { letmut configs = KeyConfig::decode_list(encoded_config_list)?; iflet 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. pubfn encapsulate(mutself, request: &[u8]) -> Res<(Vec<u8>, ClientResponse)> { let extra = self.hpke.config().kem().n_enc() + self.hpke.config().aead().n_t() + request.len(); let expected_len = self.header.len() + extra;
/// A server can handle multiple requests. /// 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)] pubstruct Server {
config: KeyConfig,
}
#[cfg(feature = "server")] impl Server { /// Create a new server configuration. /// # Panics /// If the configuration doesn't include a private key. pubfn new(config: KeyConfig) -> Res<Self> {
assert!(config.sk.is_some());
Ok(Self { config })
}
/// Get the configuration that this server uses. #[must_use] pubfn config(&self) -> &KeyConfig {
&self.config
}
/// Remove encapsulation on a message. /// # 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 pubfn decapsulate(&self, enc_request: &[u8]) -> Res<(Vec<u8>, ServerResponse)> { if enc_request.len() < REQUEST_HEADER_LEN { return Err(Error::Truncated);
} letmut r = BufReader::new(enc_request); let key_id = r.read_u8()?; if key_id != self.config.key_id { return Err(Error::KeyId);
} let kem_id = Kem::try_from(r.read_u16::<NetworkEndian>()?)?; if kem_id != self.config.kem { return Err(Error::InvalidKem);
} let kdf_id = Kdf::try_from(r.read_u16::<NetworkEndian>()?)?; let aead_id = AeadId::try_from(r.read_u16::<NetworkEndian>()?)?; let sym = SymmetricSuite::new(kdf_id, aead_id);
let info = build_info(
key_id,
HpkeConfig::new(self.config.kem, sym.kdf(), sym.aead()),
)?;
let hkdf = Hkdf::new(cfg.kdf()); let prk = hkdf.extract(&salt, &secret)?;
let key = hkdf.expand_key(&prk, INFO_KEY, KeyMechanism::Aead(cfg.aead()))?; let iv = hkdf.expand_data(&prk, INFO_NONCE, cfg.aead().n_n())?; let nonce_base = <[u8; NONCE_LEN]>::try_from(iv).unwrap();
Aead::new(mode, cfg.aead(), &key, nonce_base)
}
/// An object for encapsulating responses. /// The only way to obtain one of these is through `Server::decapsulate()`. #[cfg(feature = "server")] pubstruct ServerResponse {
response_nonce: Vec<u8>,
aead: Aead,
}
/// An object for decapsulating responses. /// The only way to obtain one of these is through `ClientRequest::encapsulate()`. #[cfg(feature = "client")] pubstruct ClientResponse {
hpke: HpkeS,
enc: Vec<u8>,
}
#[cfg(feature = "client")] impl ClientResponse { /// Private method for constructing one of these. /// Doesn't do anything because we don't have the nonce yet, so /// the work that can be done is limited. fn new(hpke: HpkeS, enc: Vec<u8>) -> Self { Self { hpke, enc }
}
/// Consume this object by decapsulating a response. pubfn decapsulate(self, enc_response: &[u8]) -> Res<Vec<u8>> { let mid = entropy(self.hpke.config()); if mid >= enc_response.len() { return Err(Error::Truncated);
} let (response_nonce, ct) = enc_response.split_at(mid); letmut aead = make_aead(
Mode::Decrypt, self.hpke.config(),
&self.hpke, self.enc,
response_nonce,
)?;
aead.open(&[], 0, ct) // 0 is the sequence number
}
}
#[cfg(all(test, feature = "client", feature = "server"))] mod test { usecrate::{
config::SymmetricSuite,
err::Res,
hpke::{Aead, Kdf, Kem},
ClientRequest, Error, KeyConfig, KeyId, Server,
}; use log::trace; use std::{fmt::Debug, io::ErrorKind};
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();
trace!("Config: {}", hex::encode(&encoded_config));
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));
let (request, server_response) = server.decapsulate(&enc_request).unwrap();
assert_eq!(&request[..], REQUEST);
let enc_response = server_response.encapsulate(RESPONSE).unwrap();
trace!("Encapsulated Response: {}", hex::encode(&enc_response));
let response = client_response.decapsulate(&enc_response).unwrap();
assert_eq!(&response[..], RESPONSE);
trace!("Response: {}", hex::encode(RESPONSE));
}
#[test] fn two_requests() {
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 client1 = ClientRequest::from_encoded_config(&encoded_config).unwrap(); let (enc_request1, client_response1) = client1.encapsulate(REQUEST).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);
let (request1, server_response1) = server.decapsulate(&enc_request1).unwrap();
assert_eq!(&request1[..], REQUEST); let (request2, server_response2) = server.decapsulate(&enc_request2).unwrap();
assert_eq!(&request2[..], REQUEST);
let enc_response1 = server_response1.encapsulate(RESPONSE).unwrap(); let enc_response2 = server_response2.encapsulate(RESPONSE).unwrap();
assert_ne!(enc_response1, enc_response2);
let response1 = client_response1.decapsulate(&enc_response1).unwrap();
assert_eq!(&response1[..], RESPONSE); let response2 = client_response2.decapsulate(&enc_response2).unwrap();
assert_eq!(&response2[..], RESPONSE);
}
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 client = ClientRequest::from_encoded_config(&encoded_config).unwrap(); let (enc_request, _) = client.encapsulate(REQUEST).unwrap();
let res = server.decapsulate(&enc_request[..cut]);
assert_truncated(res);
}
#[test] fn request_truncated_enc() { // header is 7, enc is 32
request_truncated(24);
}
#[test] fn request_truncated_ct() { // header and enc is 39, aead needs at least 16 more
request_truncated(42);
}
fn response_truncated(cut: usize) {
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 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();
assert_eq!(&request[..], REQUEST);
let enc_response = server_response.encapsulate(RESPONSE).unwrap();
let res = client_response.decapsulate(&enc_response[..cut]);
assert_truncated(res);
}
#[test] fn response_truncated_ct() { // nonce is 16, aead needs at least 16 more
response_truncated(20);
}
let server = Server::new(new_config).unwrap(); 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 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);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.