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