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