1#![deny(warnings, clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)] #![cfg_attr(
4 not(all(feature = "client", feature = "server")),
5 allow(dead_code, unused_imports)
6)]
7#[cfg(all(feature = "nss", feature = "rust-hpke"))]
8compile_error!("features \"nss\" and \"rust-hpke\" are mutually incompatible");
9
10mod config;
11mod crypto;
12mod err;
13pub mod hpke;
14#[cfg(feature = "nss")]
15mod nss;
16#[cfg(feature = "rust-hpke")]
17mod rand;
18#[cfg(feature = "rust-hpke")]
19mod rh;
20#[cfg(feature = "stream")]
21mod stream;
22
23use std::{
24 cmp::max,
25 convert::TryFrom,
26 io::{Cursor, Read},
27 mem::size_of,
28};
29
30use byteorder::{NetworkEndian, WriteBytesExt};
31use crypto::{Decrypt, Encrypt};
32use log::trace;
33
34#[cfg(feature = "nss")]
35use crate::nss::{
36 aead::{Aead, Mode, NONCE_LEN},
37 hkdf::{Hkdf, KeyMechanism},
38 hpke::{Config as HpkeConfig, Exporter, HpkeR, HpkeS},
39 random, PublicKey, SymKey,
40};
41#[cfg(feature = "stream")]
42use crate::stream::{ClientRequest as StreamClient, ServerRequest as ServerRequestStream};
43pub use crate::{
44 config::{KeyConfig, SymmetricSuite},
45 err::Error,
46};
47use crate::{err::Res, hpke::Aead as AeadId};
48#[cfg(feature = "rust-hpke")]
49use crate::{
50 rand::random,
51 rh::{
52 aead::{Aead, Mode, NONCE_LEN},
53 hkdf::{Hkdf, KeyMechanism},
54 hpke::{Config as HpkeConfig, Exporter, HpkeR, HpkeS, PublicKey},
55 SymKey,
56 },
57};
58
59const REQUEST_HEADER_LEN: usize = size_of::<KeyId>() + 6;
61const INFO_REQUEST: &[u8] = b"message/bhttp request";
62const LABEL_RESPONSE: &[u8] = b"message/bhttp response";
63const INFO_KEY: &[u8] = b"key";
64const INFO_NONCE: &[u8] = b"nonce";
65
66pub type KeyId = u8;
68
69pub fn init() {
70 #[cfg(feature = "nss")]
71 nss::init();
72}
73
74fn build_info(label: &[u8], key_id: KeyId, config: HpkeConfig) -> Res<Vec<u8>> {
76 let mut info = Vec::with_capacity(label.len() + 1 + REQUEST_HEADER_LEN);
77 info.extend_from_slice(label);
78 info.push(0);
79 info.write_u8(key_id)?;
80 info.write_u16::<NetworkEndian>(u16::from(config.kem()))?;
81 info.write_u16::<NetworkEndian>(u16::from(config.kdf()))?;
82 info.write_u16::<NetworkEndian>(u16::from(config.aead()))?;
83 trace!("HPKE info: {}", hex::encode(&info));
84 Ok(info)
85}
86
87#[cfg(feature = "client")]
90pub struct ClientRequest {
91 key_id: KeyId,
92 config: HpkeConfig,
93 pk: PublicKey,
94}
95
96#[cfg(feature = "client")]
97impl ClientRequest {
98 pub fn from_config(config: &mut KeyConfig) -> Res<Self> {
100 let selected = config.select(config.symmetric[0])?;
102 Ok(Self {
103 key_id: config.key_id,
104 config: selected,
105 pk: config.pk.clone(),
106 })
107 }
108
109 pub fn from_encoded_config(encoded_config: &[u8]) -> Res<Self> {
112 let mut config = KeyConfig::decode(encoded_config)?;
113 Self::from_config(&mut config)
114 }
115
116 pub fn from_encoded_config_list(encoded_config_list: &[u8]) -> Res<Self> {
120 let mut configs = KeyConfig::decode_list(encoded_config_list)?;
121 if let Some(mut config) = configs.pop() {
122 Self::from_config(&mut config)
123 } else {
124 Err(Error::Unsupported)
125 }
126 }
127
128 pub fn encapsulate(self, request: &[u8]) -> Res<(Vec<u8>, ClientResponse)> {
131 let info = build_info(INFO_REQUEST, self.key_id, self.config)?;
133 let mut hpke = HpkeS::new(self.config, &self.pk, &info)?;
134
135 let header = Vec::from(&info[INFO_REQUEST.len() + 1..]);
136 debug_assert_eq!(header.len(), REQUEST_HEADER_LEN);
137
138 let extra = hpke.config().kem().n_enc() + hpke.config().aead().n_t() + request.len();
139 let expected_len = header.len() + extra;
140
141 let mut enc_request = header;
142 enc_request.reserve_exact(extra);
143
144 let enc = hpke.enc()?;
145 enc_request.extend_from_slice(&enc);
146
147 let mut ct = hpke.seal(&[], request)?;
148 enc_request.append(&mut ct);
149
150 debug_assert_eq!(expected_len, enc_request.len());
151 Ok((enc_request, ClientResponse::new(hpke, enc)))
152 }
153
154 #[cfg(feature = "stream")]
155 pub fn encapsulate_stream<S>(self, dst: S) -> Res<StreamClient<S>> {
156 StreamClient::start(dst, self.config, self.key_id, &self.pk)
157 }
158}
159
160#[cfg(feature = "server")]
164#[derive(Debug, Clone)]
165pub struct Server {
166 config: KeyConfig,
167}
168
169#[cfg(feature = "server")]
170impl Server {
171 pub fn new(config: KeyConfig) -> Res<Self> {
175 assert!(config.sk.is_some());
176 Ok(Self { config })
177 }
178
179 #[must_use]
181 pub fn config(&self) -> &KeyConfig {
182 &self.config
183 }
184
185 fn decode_request_header(&self, r: &mut Cursor<&[u8]>, label: &[u8]) -> Res<(HpkeR, Vec<u8>)> {
186 let hpke_config = self.config.decode_hpke_config(r)?;
187 let sym = SymmetricSuite::new(hpke_config.kdf(), hpke_config.aead());
188 let config = self.config.select(sym)?;
189 let info = build_info(label, self.config.key_id, hpke_config)?;
190
191 let mut enc = vec![0; config.kem().n_enc()];
192 r.read_exact(&mut enc)?;
193
194 Ok((
195 HpkeR::new(
196 config,
197 &self.config.pk,
198 self.config.sk.as_ref().unwrap(),
199 &enc,
200 &info,
201 )?,
202 enc,
203 ))
204 }
205
206 pub fn decapsulate(&self, enc_request: &[u8]) -> Res<(Vec<u8>, ServerResponse)> {
210 if enc_request.len() <= REQUEST_HEADER_LEN {
211 return Err(Error::Truncated);
212 }
213 let mut r = Cursor::new(enc_request);
214 let (mut hpke, enc) = self.decode_request_header(&mut r, INFO_REQUEST)?;
215
216 let request = hpke.open(&[], &enc_request[usize::try_from(r.position())?..])?;
217 Ok((request, ServerResponse::new(&hpke, &enc)?))
218 }
219
220 #[cfg(feature = "stream")]
222 pub fn decapsulate_stream<S>(self, src: S) -> ServerRequestStream<S> {
223 ServerRequestStream::new(self.config, src)
224 }
225}
226
227fn entropy(config: HpkeConfig) -> usize {
228 max(config.aead().n_n(), config.aead().n_k())
229}
230
231fn export_secret<E: Exporter>(exp: &E, label: &[u8], cfg: HpkeConfig) -> Res<SymKey> {
232 exp.export(label, entropy(cfg))
233}
234
235fn make_aead(mode: Mode, cfg: HpkeConfig, secret: &SymKey, enc: &[u8], nonce: &[u8]) -> Res<Aead> {
236 let mut salt = enc.to_vec();
237 salt.extend_from_slice(nonce);
238
239 let hkdf = Hkdf::new(cfg.kdf());
240 let prk = hkdf.extract(&salt, secret)?;
241
242 let key = hkdf.expand_key(&prk, INFO_KEY, KeyMechanism::Aead(cfg.aead()))?;
243 let iv = hkdf.expand_data(&prk, INFO_NONCE, cfg.aead().n_n())?;
244 let nonce_base = <[u8; NONCE_LEN]>::try_from(iv).unwrap();
245
246 Aead::new(mode, cfg.aead(), &key, nonce_base)
247}
248
249#[cfg(feature = "server")]
252pub struct ServerResponse {
253 response_nonce: Vec<u8>,
254 aead: Aead,
255}
256
257#[cfg(feature = "server")]
258impl ServerResponse {
259 fn new(hpke: &HpkeR, enc: &[u8]) -> Res<Self> {
260 let response_nonce = random(entropy(hpke.config()));
261 let aead = make_aead(
262 Mode::Encrypt,
263 hpke.config(),
264 &export_secret(hpke, LABEL_RESPONSE, hpke.config())?,
265 enc,
266 &response_nonce,
267 )?;
268 Ok(Self {
269 response_nonce,
270 aead,
271 })
272 }
273
274 pub fn encapsulate(mut self, response: &[u8]) -> Res<Vec<u8>> {
276 let mut enc_response = self.response_nonce;
277 let mut ct = self.aead.seal(&[], response)?;
278 enc_response.append(&mut ct);
279 Ok(enc_response)
280 }
281}
282
283#[cfg(feature = "server")]
284impl std::fmt::Debug for ServerResponse {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 f.write_str("ServerResponse")
287 }
288}
289
290#[cfg(feature = "client")]
293pub struct ClientResponse {
294 hpke: HpkeS,
295 enc: Vec<u8>,
296}
297
298#[cfg(feature = "client")]
299impl ClientResponse {
300 fn new(hpke: HpkeS, enc: Vec<u8>) -> Self {
304 Self { hpke, enc }
305 }
306
307 pub fn decapsulate(self, enc_response: &[u8]) -> Res<Vec<u8>> {
309 let mid = entropy(self.hpke.config());
310 if mid >= enc_response.len() {
311 return Err(Error::Truncated);
312 }
313 let (response_nonce, ct) = enc_response.split_at(mid);
314 let mut aead = make_aead(
315 Mode::Decrypt,
316 self.hpke.config(),
317 &export_secret(&self.hpke, LABEL_RESPONSE, self.hpke.config())?,
318 &self.enc,
319 response_nonce,
320 )?;
321 aead.open(&[], ct) }
323}
324
325#[cfg(all(test, feature = "client", feature = "server"))]
326mod test {
327 use std::{fmt::Debug, io::ErrorKind};
328
329 use log::trace;
330
331 use crate::{
332 config::SymmetricSuite,
333 err::Res,
334 hpke::{Aead, Kdf, Kem},
335 ClientRequest, Error, KeyConfig, KeyId, Server,
336 };
337
338 pub const KEY_ID: KeyId = 1;
339 pub const KEM: Kem = Kem::X25519Sha256;
340 pub const SYMMETRIC: &[SymmetricSuite] = &[
341 SymmetricSuite::new(Kdf::HkdfSha256, Aead::Aes128Gcm),
342 SymmetricSuite::new(Kdf::HkdfSha256, Aead::ChaCha20Poly1305),
343 ];
344
345 pub const REQUEST: &[u8] = &[
346 0x00, 0x03, 0x47, 0x45, 0x54, 0x05, 0x68, 0x74, 0x74, 0x70, 0x73, 0x0b, 0x65, 0x78, 0x61,
347 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x01, 0x2f,
348 ];
349 pub const RESPONSE: &[u8] = &[0x01, 0x40, 0xc8];
350
351 pub fn init() {
352 crate::init();
353 _ = env_logger::try_init(); }
355
356 pub fn make_config() -> KeyConfig {
357 KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap()
358 }
359
360 #[test]
361 fn request_response() {
362 init();
363
364 let server_config = make_config();
365 let server = Server::new(server_config).unwrap();
366 let encoded_config = server.config().encode().unwrap();
367 trace!("Config: {}", hex::encode(&encoded_config));
368
369 let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
370 let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
371 trace!("Request: {}", hex::encode(REQUEST));
372 trace!("Encapsulated Request: {}", hex::encode(&enc_request));
373
374 let (request, server_response) = server.decapsulate(&enc_request).unwrap();
375 assert_eq!(&request[..], REQUEST);
376
377 let enc_response = server_response.encapsulate(RESPONSE).unwrap();
378 trace!("Encapsulated Response: {}", hex::encode(&enc_response));
379
380 let response = client_response.decapsulate(&enc_response).unwrap();
381 assert_eq!(&response[..], RESPONSE);
382 trace!("Response: {}", hex::encode(RESPONSE));
383 }
384
385 #[test]
386 fn two_requests() {
387 init();
388
389 let server_config = make_config();
390 let server = Server::new(server_config).unwrap();
391 let encoded_config = server.config().encode().unwrap();
392
393 let client1 = ClientRequest::from_encoded_config(&encoded_config).unwrap();
394 let (enc_request1, client_response1) = client1.encapsulate(REQUEST).unwrap();
395 let client2 = ClientRequest::from_encoded_config(&encoded_config).unwrap();
396 let (enc_request2, client_response2) = client2.encapsulate(REQUEST).unwrap();
397 assert_ne!(enc_request1, enc_request2);
398
399 let (request1, server_response1) = server.decapsulate(&enc_request1).unwrap();
400 assert_eq!(&request1[..], REQUEST);
401 let (request2, server_response2) = server.decapsulate(&enc_request2).unwrap();
402 assert_eq!(&request2[..], REQUEST);
403
404 let enc_response1 = server_response1.encapsulate(RESPONSE).unwrap();
405 let enc_response2 = server_response2.encapsulate(RESPONSE).unwrap();
406 assert_ne!(enc_response1, enc_response2);
407
408 let response1 = client_response1.decapsulate(&enc_response1).unwrap();
409 assert_eq!(&response1[..], RESPONSE);
410 let response2 = client_response2.decapsulate(&enc_response2).unwrap();
411 assert_eq!(&response2[..], RESPONSE);
412 }
413
414 fn assert_truncated<T: Debug>(res: Res<T>) {
415 match res.unwrap_err() {
416 Error::Truncated => {}
417 #[cfg(feature = "rust-hpke")]
418 Error::Aead(_) => {}
419 #[cfg(feature = "nss")]
420 Error::Crypto(_) => {}
421 Error::Io(e) => assert_eq!(e.kind(), ErrorKind::UnexpectedEof),
422 e => panic!("unexpected error type: {e:?}"),
423 }
424 }
425
426 fn request_truncated(cut: usize) {
427 init();
428
429 let server_config = make_config();
430 let server = Server::new(server_config).unwrap();
431 let encoded_config = server.config().encode().unwrap();
432
433 let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
434 let (enc_request, _) = client.encapsulate(REQUEST).unwrap();
435
436 let res = server.decapsulate(&enc_request[..cut]);
437 assert_truncated(res);
438 }
439
440 #[test]
441 fn request_truncated_header() {
442 request_truncated(4);
443 }
444
445 #[test]
446 fn request_truncated_enc() {
447 request_truncated(24);
449 }
450
451 #[test]
452 fn request_truncated_ct() {
453 request_truncated(42);
455 }
456
457 fn response_truncated(cut: usize) {
458 init();
459
460 let server_config = make_config();
461 let server = Server::new(server_config).unwrap();
462 let encoded_config = server.config().encode().unwrap();
463
464 let client = ClientRequest::from_encoded_config(&encoded_config).unwrap();
465 let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
466
467 let (request, server_response) = server.decapsulate(&enc_request).unwrap();
468 assert_eq!(&request[..], REQUEST);
469
470 let enc_response = server_response.encapsulate(RESPONSE).unwrap();
471
472 let res = client_response.decapsulate(&enc_response[..cut]);
473 assert_truncated(res);
474 }
475
476 #[test]
477 fn response_truncated_ct() {
478 response_truncated(20);
480 }
481
482 #[test]
483 fn response_truncated_nonce() {
484 response_truncated(7);
485 }
486
487 #[cfg(feature = "rust-hpke")]
488 #[test]
489 fn derive_key_pair() {
490 const IKM: &[u8] = &[
491 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
492 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
493 ];
494 const EXPECTED_CONFIG: &[u8] = &[
495 0x01, 0x00, 0x20, 0xfc, 0x01, 0x38, 0x93, 0x64, 0x10, 0x31, 0x1a, 0x0c, 0x64, 0x1a,
496 0x5c, 0xa0, 0x86, 0x39, 0x1d, 0xe8, 0xe7, 0x03, 0x82, 0x33, 0x3f, 0x6d, 0x64, 0x49,
497 0x25, 0x21, 0xad, 0x7d, 0xc7, 0x8a, 0x5d, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00,
498 0x01, 0x00, 0x03,
499 ];
500
501 init();
502
503 let config = KeyConfig::decode(EXPECTED_CONFIG).unwrap();
504
505 let new_config = KeyConfig::derive(KEY_ID, KEM, Vec::from(SYMMETRIC), IKM).unwrap();
506 assert_eq!(config.key_id, new_config.key_id);
507 assert_eq!(config.kem, new_config.kem);
508 assert_eq!(config.symmetric, new_config.symmetric);
509
510 let server = Server::new(new_config).unwrap();
511 let encoded_config = server.config().encode().unwrap();
512 assert_eq!(EXPECTED_CONFIG, encoded_config);
513 }
514
515 #[test]
516 fn request_from_config_list() {
517 init();
518
519 let server_config = make_config();
520 let server = Server::new(server_config).unwrap();
521 let encoded_config = server.config().encode().unwrap();
522
523 let mut header: [u8; 2] = [0; 2];
524 header[0] = u8::try_from((encoded_config.len() & 0xFF00) >> 8).unwrap();
525 header[1] = u8::try_from(encoded_config.len() & 0xFF).unwrap();
526 let mut encoded_config_list = Vec::new();
527 encoded_config_list.extend(header.to_vec());
528 encoded_config_list.extend(encoded_config);
529
530 let client = ClientRequest::from_encoded_config_list(&encoded_config_list).unwrap();
531 let (enc_request, client_response) = client.encapsulate(REQUEST).unwrap();
532
533 let (request, server_response) = server.decapsulate(&enc_request).unwrap();
534 assert_eq!(&request[..], REQUEST);
535
536 let enc_response = server_response.encapsulate(RESPONSE).unwrap();
537
538 let response = client_response.decapsulate(&enc_response).unwrap();
539 assert_eq!(&response[..], RESPONSE);
540 }
541}