ohttp/
lib.rs

1#![deny(warnings, clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)] // I'm too lazy
3#![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
59/// The request header is a `KeyId` and 2 each for KEM, KDF, and AEAD identifiers
60const 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
66/// The type of a key identifier.
67pub type KeyId = u8;
68
69pub fn init() {
70    #[cfg(feature = "nss")]
71    nss::init();
72}
73
74/// Construct the info parameter we use to initialize an `HpkeS` instance.
75fn 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/// This is the sort of information we expect to receive from the receiver.
88/// This might not be necessary if we agree on a format.
89#[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    /// Construct a `ClientRequest` from a specific `KeyConfig` instance.
99    pub fn from_config(config: &mut KeyConfig) -> Res<Self> {
100        // TODO(mt) choose the best config, not just the first.
101        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    /// Reads an encoded configuration and constructs a single use client sender.
110    /// See `KeyConfig::decode` for the structure details.
111    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    /// Reads an encoded list of configurations and constructs a single use client sender
117    /// from the first supported configuration.
118    /// See `KeyConfig::decode_list` for the structure details.
119    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    /// Encapsulate a request.  This consumes this object.
129    /// This produces a response handler and the bytes of an encapsulated request.
130    pub fn encapsulate(self, request: &[u8]) -> Res<(Vec<u8>, ClientResponse)> {
131        // Build the info, which contains the message header.
132        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/// A server can handle multiple requests.
161/// It holds a single key pair and can generate a configuration.
162/// (A more complex server would have multiple key pairs. This is simple.)
163#[cfg(feature = "server")]
164#[derive(Debug, Clone)]
165pub struct Server {
166    config: KeyConfig,
167}
168
169#[cfg(feature = "server")]
170impl Server {
171    /// Create a new server configuration.
172    /// # Panics
173    /// If the configuration doesn't include a private key.
174    pub fn new(config: KeyConfig) -> Res<Self> {
175        assert!(config.sk.is_some());
176        Ok(Self { config })
177    }
178
179    /// Get the configuration that this server uses.
180    #[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    /// Remove encapsulation on a request.
207    /// # Panics
208    /// Not as a consequence of this code, but Rust won't know that for sure.
209    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    /// Remove encapsulation on a streamed request.
221    #[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/// An object for encapsulating responses.
250/// The only way to obtain one of these is through `Server::decapsulate()`.
251#[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    /// Consume this object by encapsulating a response.
275    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/// An object for decapsulating responses.
291/// The only way to obtain one of these is through `ClientRequest::encapsulate()`.
292#[cfg(feature = "client")]
293pub struct ClientResponse {
294    hpke: HpkeS,
295    enc: Vec<u8>,
296}
297
298#[cfg(feature = "client")]
299impl ClientResponse {
300    /// Private method for constructing one of these.
301    /// Doesn't do anything because we don't have the nonce yet, so
302    /// the work that can be done is limited.
303    fn new(hpke: HpkeS, enc: Vec<u8>) -> Self {
304        Self { hpke, enc }
305    }
306
307    /// Consume this object by decapsulating a response.
308    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) // 0 is the sequence number
322    }
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(); // ignore errors here
354    }
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        // header is 7, enc is 32
448        request_truncated(24);
449    }
450
451    #[test]
452    fn request_truncated_ct() {
453        // header and enc is 39, aead needs at least 16 more
454        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        // nonce is 16, aead needs at least 16 more
479        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}