bitcoin_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
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
54/// The request header is a `KeyId` and 2 each for KEM, KDF, and AEAD identifiers
55const REQUEST_HEADER_LEN: usize = size_of::<KeyId>() + 6;
56const INFO_REQUEST: &[u8] = b"message/bhttp request";
57/// The info used for HPKE export is `INFO_REQUEST`, a zero byte, and the header.
58const 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
63/// The type of a key identifier.
64pub type KeyId = u8;
65
66pub fn init() {
67    #[cfg(feature = "nss")]
68    nss::init();
69}
70
71/// Construct the info parameter we use to initialize an `HpkeS` instance.
72fn 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/// This is the sort of information we expect to receive from the receiver.
85/// This might not be necessary if we agree on a format.
86#[cfg(feature = "client")]
87pub struct ClientRequest {
88    hpke: HpkeS,
89    header: Vec<u8>,
90}
91
92#[cfg(feature = "client")]
93impl ClientRequest {
94    /// Construct a `ClientRequest` from a specific `KeyConfig` instance.
95    pub fn from_config(config: &mut KeyConfig) -> Res<Self> {
96        // TODO(mt) choose the best config, not just the first.
97        let selected = config.select(config.symmetric[0])?;
98
99        // Build the info, which contains the message header.
100        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    /// Reads an encoded configuration and constructs a single use client sender.
109    /// See `KeyConfig::decode` for the structure details.
110    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    /// Reads an encoded list of configurations and constructs a single use client sender
116    /// from the first supported configuration.
117    /// See `KeyConfig::decode_list` for the structure details.
118    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    /// Encapsulate a request.  This consumes this object.
128    /// This produces a response handler and the bytes of an encapsulated request.
129    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/// A server can handle multiple requests.
149/// It holds a single key pair and can generate a configuration.
150/// (A more complex server would have multiple key pairs. This is simple.)
151#[cfg(feature = "server")]
152#[derive(Debug, Clone)]
153pub struct Server {
154    config: KeyConfig,
155}
156
157#[cfg(feature = "server")]
158impl Server {
159    /// Create a new server configuration.
160    /// # Panics
161    /// If the configuration doesn't include a private key.
162    pub fn new(config: KeyConfig) -> Res<Self> {
163        assert!(config.sk.is_some());
164        Ok(Self { config })
165    }
166
167    /// Get the configuration that this server uses.
168    #[must_use]
169    pub fn config(&self) -> &KeyConfig {
170        &self.config
171    }
172
173    /// Remove encapsulation on a message.
174    /// # Panics
175    /// Not as a consequence of this code, but Rust won't know that for sure.
176    #[allow(clippy::similar_names)] // for kem_id and key_id
177    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/// An object for encapsulating responses.
244/// The only way to obtain one of these is through `Server::decapsulate()`.
245#[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    /// Consume this object by encapsulating a response.
263    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/// An object for decapsulating responses.
279/// The only way to obtain one of these is through `ClientRequest::encapsulate()`.
280#[cfg(feature = "client")]
281pub struct ClientResponse {
282    hpke: HpkeS,
283    enc: Vec<u8>,
284}
285
286#[cfg(feature = "client")]
287impl ClientResponse {
288    /// Private method for constructing one of these.
289    /// Doesn't do anything because we don't have the nonce yet, so
290    /// the work that can be done is limited.
291    fn new(hpke: HpkeS, enc: Vec<u8>) -> Self {
292        Self { hpke, enc }
293    }
294
295    /// Consume this object by decapsulating a response.
296    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) // 0 is the sequence number
310    }
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(); // ignore errors here
340    }
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        // header is 7, enc is 32
430        request_truncated(24);
431    }
432
433    #[test]
434    fn request_truncated_ct() {
435        // header and enc is 39, aead needs at least 16 more
436        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        // nonce is 16, aead needs at least 16 more
461        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}