├── .gitignore ├── README.md ├── LICENSE ├── .github └── workflows │ └── semgrep.yml ├── Cargo.toml └── src ├── bhttp.rs ├── crypt.rs ├── bhttp ├── builder.rs └── parser.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aloha: Alternative Library for Oblivious HTTP Applications 2 | 3 | Aloha is a low-level Oblivious HTTP parsing/building library that 4 | focus on performance. The crypto functionality is built on top of 5 | [hpke] crate, while the bHTTP implementation leverages a chained 6 | operation to avoid heap allocations. 7 | 8 | Please see the crate documentation for details and examples. 9 | 10 | [hpke]: https://github.com/rozbb/rust-hpke 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Cloudflare, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: {} 3 | workflow_dispatch: {} 4 | push: 5 | branches: 6 | - main 7 | - master 8 | schedule: 9 | - cron: '0 0 * * *' 10 | name: Semgrep config 11 | jobs: 12 | semgrep: 13 | name: semgrep/ci 14 | runs-on: ubuntu-24.04 15 | env: 16 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 17 | SEMGREP_URL: https://cloudflare.semgrep.dev 18 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev 19 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version 20 | container: 21 | image: semgrep/semgrep 22 | steps: 23 | - uses: actions/checkout@v4 24 | - run: semgrep ci 25 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aloha" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | authors = ["Anbang Wen "] 7 | description = "Low-level Rust implementation of Oblivious HTTP" 8 | repository = "https://github.com/cloudflare/aloha-rs/" 9 | keywords = ["ohttp", "bhttp", "privacy", "oblivious"] 10 | categories = ["network-programming", "cryptography"] 11 | include = ["/src", "README.md", "LICENSE"] 12 | 13 | [dependencies] 14 | aead = "0.5" 15 | bytes = "1.0" 16 | generic-array = "0.14" 17 | hkdf = "0.12" 18 | hpke = { version = "0.10.0", features = [ "std" ] } 19 | rand = { version = "0.8", features = [ "std_rng" ], default-features = false } 20 | thiserror = "1.0" 21 | 22 | [dev-dependencies] 23 | hex-literal = "0.3" 24 | rand = { version = "0.8", features = [ "std_rng" ] } 25 | rstest = "0.16" 26 | x25519-dalek = "2.0.0-pre.1" 27 | -------------------------------------------------------------------------------- /src/bhttp.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. 2 | // Licensed under the Apache-2.0 license found in the LICENSE file or 3 | // at http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | //! A RFC 9292 implementation that has chained parser and builder to 6 | //! avoid heap allocation. 7 | //! 8 | //! To build a bHTTP message, start with a [`Builder`] and a choice of 9 | //! [`Framing`], advance the builder by calling vairous `push_` 10 | //! functions with necessary data, the builder will transit into 11 | //! another. 12 | //! 13 | //! Similar, to parse a bHTTP message, start with a [`Parser`], then 14 | //! move to the next parser in the chain by calling the `next` 15 | //! function. 16 | //! 17 | //! # Examples 18 | //! Build a bHTTP request: 19 | //! ``` 20 | //! use aloha::bhttp::{Builder, Error, Framing}; 21 | //! 22 | //! # fn main() -> Result<(), Error> { 23 | //! let mut buf = Vec::new(); 24 | //! Builder::new(&mut buf, Framing::KnownLenReq) 25 | //! .push_ctrl(b"GET", b"https", b"www.example.com", b"/hello.txt")? 26 | //! .push_headers(&[ 27 | //! ( 28 | //! &b"user-agent"[..], 29 | //! &b"curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"[..], 30 | //! ), 31 | //! (&b"host"[..], &b"www.example.com"[..]), 32 | //! (&b"accept-language"[..], &b"en, mi"[..]), 33 | //! ])?; 34 | //! # Ok(()) 35 | //! # } 36 | //! ``` 37 | //! 38 | //! Parse a bHTTP response: 39 | //! ```no_run 40 | //! use aloha::bhttp::{Error, Framing, Parser}; 41 | //! 42 | //! # fn main() -> Result<(), Error> { 43 | //! # let some_buf = &[]; 44 | //! let parser = Parser::new(some_buf); 45 | //! let req_ctrl = parser.next_req()?; 46 | //! let ctrl = req_ctrl.get()?; 47 | //! assert_eq!(b"GET", ctrl.method); 48 | //! assert_eq!(b"https", ctrl.scheme); 49 | //! assert_eq!(b"example.com", ctrl.authority); 50 | //! assert_eq!(b"/", ctrl.path); 51 | //! let headers = req_ctrl.next()?; 52 | //! // ... 53 | //! # Ok(()) 54 | //! # } 55 | //! ``` 56 | 57 | use bytes::{Buf, BufMut}; 58 | use thiserror::Error as ThisError; 59 | 60 | mod builder; 61 | mod parser; 62 | 63 | pub use builder::*; 64 | pub use parser::*; 65 | 66 | const CONTENT_TERMINATOR: u8 = 0x00; 67 | 68 | /// Errors used in bHTTP library. 69 | #[derive(ThisError, Debug, PartialEq, Eq, Clone, Copy)] 70 | pub enum Error { 71 | /// Provided buffer is too short. 72 | #[error("Provided buffer is too short")] 73 | ShortBuf, 74 | /// Input data is invalid. 75 | #[error("Input data is invalid")] 76 | InvalidInput, 77 | /// Unexpected state in message builder. 78 | #[error("Unexpected state in message builder")] 79 | UnexpectedBuildState, 80 | /// Unexpected framing. 81 | #[error("Unexpected framing")] 82 | UnexpectedFraming, 83 | } 84 | 85 | type Result = std::result::Result; 86 | 87 | /// Represent the framing byte in the bHTTP message. 88 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 89 | #[repr(u8)] 90 | pub enum Framing { 91 | /// Known Length Request 92 | KnownLenReq = 0, 93 | /// Known Length Response 94 | KnownLenRes = 1, 95 | /// Indeterminate Length Request 96 | IndLenReq = 2, 97 | /// Indeterminate Length Reponse 98 | IndLenRes = 3, 99 | } 100 | 101 | impl Framing { 102 | /// Whether the framing stands for a known-lengthed message. 103 | pub fn known_len(&self) -> bool { 104 | *self == Self::KnownLenReq || *self == Self::KnownLenRes 105 | } 106 | 107 | /// Whether the framing stands for request message. 108 | pub fn is_request(&self) -> bool { 109 | *self == Self::KnownLenReq || *self == Self::IndLenReq 110 | } 111 | } 112 | 113 | impl TryFrom for Framing { 114 | type Error = Error; 115 | fn try_from(n: u8) -> Result { 116 | match n { 117 | 0 => Ok(Self::KnownLenReq), 118 | 1 => Ok(Self::KnownLenRes), 119 | 2 => Ok(Self::IndLenReq), 120 | 3 => Ok(Self::IndLenRes), 121 | _ => Err(Error::InvalidInput), 122 | } 123 | } 124 | } 125 | 126 | fn is_final_ctrl(status: usize) -> bool { 127 | status >= 200 128 | } 129 | 130 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 131 | struct VarInt(u64); 132 | 133 | impl VarInt { 134 | const MAX: u64 = (1 << 62) - 1; 135 | 136 | fn as_usize(&self) -> usize { 137 | self.0 as usize 138 | } 139 | 140 | fn size(&self) -> usize { 141 | match self.0 { 142 | 0..=0x3f => 1, 143 | 0x40..=0x3fff => 2, 144 | 0x4000..=0x3fff_ffff => 3, 145 | 0x4000_0000.. => 4, 146 | } 147 | } 148 | } 149 | 150 | impl From for u64 { 151 | fn from(v: VarInt) -> Self { 152 | v.0 153 | } 154 | } 155 | 156 | impl TryFrom for VarInt { 157 | type Error = Error; 158 | fn try_from(n: u64) -> Result { 159 | if n > Self::MAX { 160 | Err(Error::InvalidInput) 161 | } else { 162 | Ok(Self(n)) 163 | } 164 | } 165 | } 166 | 167 | impl TryFrom for VarInt { 168 | type Error = Error; 169 | fn try_from(n: usize) -> Result { 170 | let n = u64::try_from(n).map_err(|_| Error::InvalidInput)?; 171 | Self::try_from(n) 172 | } 173 | } 174 | 175 | impl VarInt { 176 | /// Parse VarInt from a byte slice. 177 | fn parse(buf: &mut B) -> Result { 178 | if !buf.has_remaining() { 179 | return Err(Error::InvalidInput); 180 | } 181 | let b = buf.chunk()[0]; 182 | let n = 1 << (b >> 6); 183 | if buf.remaining() < n { 184 | return Err(Error::InvalidInput); 185 | } 186 | Ok(Self(match n { 187 | 1 => (buf.get_u8() & ((1 << 6) - 1)).into(), 188 | 2 => (buf.get_u16() & ((1 << 14) - 1)).into(), 189 | 4 => (buf.get_u32() & ((1 << 30) - 1)).into(), 190 | 8 => buf.get_u64() & ((1 << 62) - 1), 191 | _ => unreachable!(), 192 | })) 193 | } 194 | 195 | /// Compose into bytes. 196 | fn compose(&self, buf: &mut B) -> Result<()> { 197 | let len = buf.remaining_mut(); 198 | match self.0 { 199 | 0..=0x3f if len > 0 => buf.put_u8(self.0 as u8), 200 | 0x40..=0x3fff if len > 1 => buf.put_u16((self.0 | (0b01 << 14)) as u16), 201 | 0x4000..=0x3fff_ffff if len > 3 => buf.put_u32((self.0 | (0b10 << 30)) as u32), 202 | 0x4000_0000..=0x3fff_ffff_ffff_ffff if len >= 8 => buf.put_u64(self.0 | (0b11 << 62)), 203 | Self::MAX.. => return Err(Error::InvalidInput), 204 | _ => return Err(Error::ShortBuf), 205 | } 206 | 207 | Ok(()) 208 | } 209 | } 210 | 211 | #[cfg(test)] 212 | mod tests { 213 | use super::*; 214 | use hex_literal::hex; 215 | use rstest::*; 216 | use std::str::from_utf8; 217 | 218 | pub(crate) fn unwrap_fieldline<'a>( 219 | f: Option>, 220 | ) -> (&'a str, &'a str) { 221 | let (k, v) = f.unwrap().unwrap(); 222 | (from_utf8(k).unwrap(), from_utf8(v).unwrap()) 223 | } 224 | 225 | pub(crate) const EXAMPLE_KNOWN_LEN_REQ1: &[u8] = hex!( 226 | " 227 | 00034745 54056874 7470730b 6578616d 228 | 706c652e 636f6d01 2f 229 | " 230 | ) 231 | .as_slice(); 232 | 233 | // https://www.rfc-editor.org/rfc/rfc9292#name-request-example 234 | pub(crate) const EXAMPLE_KNOWN_LEN_REQ2: &[u8] = hex!( 235 | " 236 | 00034745 54056874 74707300 0a2f6865 237 | 6c6c6f2e 74787440 6c0a7573 65722d61 238 | 67656e74 34637572 6c2f372e 31362e33 239 | 206c6962 6375726c 2f372e31 362e3320 240 | 4f70656e 53534c2f 302e392e 376c207a 241 | 6c69622f 312e322e 3304686f 73740f77 242 | 77772e65 78616d70 6c652e63 6f6d0f61 243 | 63636570 742d6c61 6e677561 67650665 244 | 6e2c206d 690000 245 | " 246 | ) 247 | .as_slice(); 248 | 249 | // https://www.rfc-editor.org/rfc/rfc9292#name-request-example 250 | pub(crate) const EXAMPLE_IND_LEN_REQ1: &[u8] = hex!( 251 | " 252 | 02034745 54056874 74707300 0a2f6865 253 | 6c6c6f2e 7478740a 75736572 2d616765 254 | 6e743463 75726c2f 372e3136 2e33206c 255 | 69626375 726c2f37 2e31362e 33204f70 256 | 656e5353 4c2f302e 392e376c 207a6c69 257 | 622f312e 322e3304 686f7374 0f777777 258 | 2e657861 6d706c65 2e636f6d 0f616363 259 | 6570742d 6c616e67 75616765 06656e2c 260 | 206d6900 00000000 00000000 00000000 261 | " 262 | ) 263 | .as_slice(); 264 | 265 | pub(crate) const EXAMPLE_IND_LEN_REQ2: &[u8] = hex!( 266 | " 267 | 02034745 54056874 74707300 0a2f6865 268 | 6c6c6f2e 7478740a 75736572 2d616765 269 | 6e743463 75726c2f 372e3136 2e33206c 270 | 69626375 726c2f37 2e31362e 33204f70 271 | 656e5353 4c2f302e 392e376c 207a6c69 272 | 622f312e 322e3304 686f7374 0f777777 273 | 2e657861 6d706c65 2e636f6d 0f616363 274 | 6570742d 6c616e67 75616765 06656e2c 275 | 206d6900 276 | " 277 | ) 278 | .as_slice(); 279 | 280 | // https://www.rfc-editor.org/rfc/rfc9292#name-response-example 281 | pub(crate) const EXAMPLE_KNOWN_LEN_RES1: &[u8] = hex!( 282 | " 283 | 0140c800 1d546869 7320636f 6e74656e 284 | 7420636f 6e746169 6e732043 524c462e 285 | 0d0a0d07 74726169 6c657204 74657874 286 | " 287 | ) 288 | .as_slice(); 289 | 290 | // https://www.rfc-editor.org/rfc/rfc9292#name-response-example 291 | pub(crate) const EXAMPLE_IND_LEN_RES1: &[u8] = hex!( 292 | " 293 | 03406607 72756e6e 696e670a 22736c65 294 | 65702031 35220040 67046c69 6e6b233c 295 | 2f737479 6c652e63 73733e3b 2072656c 296 | 3d707265 6c6f6164 3b206173 3d737479 297 | 6c65046c 696e6b24 3c2f7363 72697074 298 | 2e6a733e 3b207265 6c3d7072 656c6f61 299 | 643b2061 733d7363 72697074 0040c804 300 | 64617465 1d4d6f6e 2c203237 204a756c 301 | 20323030 39203132 3a32383a 35332047 302 | 4d540673 65727665 72064170 61636865 303 | 0d6c6173 742d6d6f 64696669 65641d57 304 | 65642c20 3232204a 756c2032 30303920 305 | 31393a31 353a3536 20474d54 04657461 306 | 67142233 34616133 38372d64 2d313536 307 | 38656230 30220d61 63636570 742d7261 308 | 6e676573 05627974 65730e63 6f6e7465 309 | 6e742d6c 656e6774 68023531 04766172 310 | 790f4163 63657074 2d456e63 6f64696e 311 | 670c636f 6e74656e 742d7479 70650a74 312 | 6578742f 706c6169 6e003348 656c6c6f 313 | 20576f72 6c642120 4d792063 6f6e7465 314 | 6e742069 6e636c75 64657320 61207472 315 | 61696c69 6e672043 524c462e 0d0a0000 316 | " 317 | ) 318 | .as_slice(); 319 | 320 | #[rstest] 321 | #[case(&[0x00], Ok(0x00))] 322 | #[case(&[0x01, 0x02, 0x03], Ok(0x01))] 323 | #[case(&[0x40, 0xff], Ok(0xff))] 324 | #[case(&[0x80, 0xad, 0xbe, 0xef], Ok(0x00adbeef))] 325 | #[case(&[0xc0], Err(Error::InvalidInput))] 326 | fn varint_parse(#[case] slice: &[u8], #[case] exp: Result) { 327 | let mut buf = slice; 328 | assert_eq!(exp, VarInt::parse(&mut buf).map(|v| v.into())) 329 | } 330 | 331 | #[rstest] 332 | #[case(0x00, 1, Ok(&hex!("00")[..]))] 333 | #[case(0x3f, 1, Ok(&hex!("3f")[..]))] 334 | #[case(1 << 8, 2, Ok(&hex!("4100")[..]))] 335 | #[case(1 << 16, 4, Ok(&hex!("80010000")[..]))] 336 | #[case(1 << 32, 8, Ok(&hex!("c000000100000000")[..]))] 337 | #[case(1 << 16, 1, Err(Error::ShortBuf))] 338 | fn varint_compose(#[case] num: u64, #[case] buf_len: usize, #[case] exp_hex: Result<&[u8]>) { 339 | let mut buf = vec![0xff; buf_len]; 340 | let mut slice_mut = buf.as_mut_slice(); 341 | let len = slice_mut.len(); 342 | let r = VarInt::try_from(num).unwrap().compose(&mut slice_mut); 343 | let new_len = slice_mut.len(); 344 | 345 | assert_eq!(exp_hex, r.map(|_| &buf[..len - new_len])); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/crypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. 2 | // Licensed under the Apache-2.0 license found in the LICENSE file or 3 | // at http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | use aead::{AeadCore, AeadInPlace, KeyInit, KeySizeUser}; 6 | use bytes::{BufMut, Bytes, BytesMut}; 7 | use generic_array::GenericArray; 8 | use hkdf::hmac::{digest::OutputSizeUser, Hmac}; 9 | use hkdf::{Hkdf, HmacImpl}; 10 | use hpke::aead::{Aead, AeadTag, AesGcm128, AesGcm256, ChaCha20Poly1305}; 11 | use hpke::kdf::{HkdfSha256, HkdfSha384, HkdfSha512, Kdf}; 12 | use hpke::kem::Kem; 13 | use hpke::{Deserializable, OpModeR, OpModeS, Serializable}; 14 | use rand::Rng; 15 | use rand::{CryptoRng, RngCore}; 16 | 17 | use super::*; 18 | 19 | /// Macro to dispatch the generic functions based on the algorithm id 20 | /// in the header. 21 | macro_rules! dispatch { 22 | ($(<$pre:ident>)? $f:ident <$($kem:ident)? $(,$g:ident)* >($hdr:ident $(,$arg:ident)* $(,)?)) => { 23 | match $hdr.kdf_id { 24 | HkdfSha256::KDF_ID => dispatch!(inner, $(<$pre>)?$f::<$(|$kem,)? HkdfSha256 $(,$g)*>($hdr $(,$arg)*)), 25 | HkdfSha384::KDF_ID => dispatch!(inner, $(<$pre>)?$f::<$(|$kem,)? HkdfSha384 $(,$g)*>($hdr $(,$arg)*)), 26 | HkdfSha512::KDF_ID => dispatch!(inner, $(<$pre>)?$f::<$(|$kem,)? HkdfSha512 $(,$g)*>($hdr $(,$arg)*)), 27 | _ => Err(Error::UnsupportedKdf), 28 | } 29 | }; 30 | 31 | (inner, $(<$pre:ident>)? $f:ident::<$(|$kem:ident,)? $kdf:ty $(,$g:ident)* >($hdr:ident $(,$arg:ident)* $(,)?)) => { 32 | match $hdr.aead_id { 33 | AesGcm128::AEAD_ID => $($pre::)?$f::<$($kem,)? $kdf, AesGcm128 $(,$g)*>($hdr $(,$arg)*), 34 | AesGcm256::AEAD_ID => $($pre::)?$f::<$($kem,)? $kdf, AesGcm256 $(,$g)*>($hdr $(,$arg)*), 35 | ChaCha20Poly1305::AEAD_ID => $($pre::)?$f::<$($kem,)? $kdf, ChaCha20Poly1305 $(,$g)*>($hdr $(,$arg)*), 36 | _ => Err(Error::UnsupportedAead), 37 | } 38 | }; 39 | } 40 | 41 | pub(crate) fn encrypt_req( 42 | hdr: Header, 43 | pubkey: &::PublicKey, 44 | req: &[u8], 45 | rng: &mut R, 46 | ) -> Result<(BytesMut, Ctx)> { 47 | dispatch!(encrypt_req_with < KEM, R > (hdr, pubkey, req, rng)) 48 | } 49 | 50 | fn encrypt_req_with( 51 | hdr: Header, 52 | pubkey: &::PublicKey, 53 | req: &[u8], 54 | rng: &mut R, 55 | ) -> Result<(BytesMut, Ctx)> { 56 | // create info 57 | let mut info = [0u8; LABEL_REQ.len() + 1 + Header::SIZE]; 58 | compose_info::(hdr.cid, LABEL_REQ.as_bytes(), &mut &mut info[..])?; 59 | 60 | let (enc_key, mut ctx) = 61 | hpke::setup_sender::(&OpModeS::Base, pubkey, &info, rng)?; 62 | let enc_key_bytes = enc_key.to_bytes(); 63 | 64 | let mut buf = BytesMut::with_capacity( 65 | Header::SIZE + enc_key_bytes.len() + req.len() + AeadTag::::size(), 66 | ); 67 | 68 | compose_header::(hdr.cid, &mut buf)?; 69 | buf.put(enc_key_bytes.as_ref()); 70 | 71 | // push req into buffer for inplace seal 72 | let start = buf.len(); 73 | buf.put(req); 74 | let end = buf.len(); 75 | 76 | let tag = ctx.seal_in_place_detached(&mut buf.as_mut()[start..end], &[])?; 77 | 78 | buf.put(tag.to_bytes().as_ref()); 79 | 80 | // let secret: GenericArray::AeadImpl as KeySizeUser>::KeySize> = 81 | // Default::default(); 82 | let mut secret = vec![0; aead_key_size::()]; 83 | ctx.export(LABEL_RES.as_bytes(), &mut secret)?; 84 | 85 | let out_ctx = Ctx { 86 | hdr, 87 | secret: secret.into(), 88 | encapped_key: Bytes::copy_from_slice(&enc_key_bytes), 89 | }; 90 | Ok((buf, out_ctx)) 91 | } 92 | 93 | pub(crate) fn decrypt_req_in_place( 94 | hdr: Header, 95 | enc_req: B, 96 | priv_key: &::PrivateKey, 97 | ) -> Result<(B, Ctx)> { 98 | dispatch!( decrypt_req_in_place (hdr, enc_req, priv_key)) 99 | } 100 | 101 | /// Trait to support in place operations over a mutable buffer. The 102 | /// method is an associated function because it can benefit from the 103 | /// internal macros, same reason for the unused header parameter in 104 | /// response decryption. 105 | pub trait InPlaceMut: Sized { 106 | /// In place decrypt a request. 107 | fn decrypt_req_in_place( 108 | hdr: Header, 109 | buf: Self, 110 | priv_key: &::PrivateKey, 111 | ) -> Result<(Self, Ctx)> 112 | where 113 | KEM: Kem, 114 | KDF: Kdf, 115 | AEAD: Aead; 116 | 117 | /// In place decrypt a response. 118 | fn decrypt_res_in_place( 119 | _hdr: Header, 120 | buf: Self, 121 | enc_key: &[u8], 122 | secret: &[u8], 123 | ) -> Result 124 | where 125 | KDF: Kdf, 126 | AEAD: Aead; 127 | } 128 | 129 | impl InPlaceMut for BytesMut { 130 | fn decrypt_req_in_place( 131 | hdr: Header, 132 | mut buf: BytesMut, 133 | priv_key: &::PrivateKey, 134 | ) -> Result<(BytesMut, Ctx)> 135 | where 136 | KEM: Kem, 137 | KDF: Kdf, 138 | AEAD: Aead, 139 | { 140 | // buf: [hdr encapped_key encrypted_req tag] 141 | 142 | let enc_key_len = ::EncappedKey::size(); 143 | let tag_len = AeadTag::::size(); 144 | if buf.len() < Header::SIZE + enc_key_len + tag_len { 145 | return Err(Error::InvalidInput); 146 | } 147 | 148 | let _ = buf.split_to(Header::SIZE); 149 | let enc_key_bytes = buf.split_to(enc_key_len); 150 | let tag_bytes = buf.split_off(buf.len() - tag_len); 151 | 152 | let secret = 153 | kem_decap::(priv_key, &hdr, &enc_key_bytes, &tag_bytes, &mut buf)?; 154 | 155 | let out_ctx = Ctx { 156 | hdr, 157 | encapped_key: enc_key_bytes.freeze(), 158 | secret: secret.into(), 159 | }; 160 | 161 | Ok((buf, out_ctx)) 162 | } 163 | 164 | fn decrypt_res_in_place( 165 | _hdr: Header, 166 | mut buf: BytesMut, 167 | enc_key: &[u8], 168 | secret: &[u8], 169 | ) -> Result 170 | where 171 | KDF: Kdf, 172 | AEAD: Aead, 173 | { 174 | // buf contains [res_nonce res tag] 175 | let res_nonce_size = res_nonce_size::(); 176 | let tag_size = AeadTag::::size(); 177 | if buf.len() < res_nonce_size + tag_size { 178 | return Err(Error::InvalidInput); 179 | } 180 | 181 | let res_nonce = buf.split_to(res_nonce_size); 182 | let tag = buf.split_off(buf.len() - tag_size); 183 | 184 | aead_open::(secret, enc_key, &res_nonce, &mut buf, &tag)?; 185 | Ok(buf) 186 | } 187 | } 188 | 189 | impl<'a> InPlaceMut for &'a mut [u8] { 190 | fn decrypt_req_in_place( 191 | hdr: Header, 192 | buf: &'a mut [u8], 193 | priv_key: &::PrivateKey, 194 | ) -> Result<(&'a mut [u8], Ctx)> 195 | where 196 | KEM: Kem, 197 | KDF: Kdf, 198 | AEAD: Aead, 199 | { 200 | // buf: [hdr encapped_key encrypted_req tag] 201 | 202 | let enc_key_len = ::EncappedKey::size(); 203 | let tag_len = AeadTag::::size(); 204 | if buf.len() < Header::SIZE + enc_key_len + tag_len { 205 | return Err(Error::InvalidInput); 206 | } 207 | 208 | let (_, buf) = buf.split_at_mut(Header::SIZE); 209 | let (enc_key_bytes, buf) = buf.split_at_mut(enc_key_len); 210 | let (buf, tag_bytes) = buf.split_at_mut(buf.len() - tag_len); 211 | 212 | let secret = kem_decap::(priv_key, &hdr, enc_key_bytes, tag_bytes, buf)?; 213 | 214 | let out_ctx = Ctx { 215 | hdr, 216 | encapped_key: Bytes::copy_from_slice(enc_key_bytes), 217 | secret: secret.into(), 218 | }; 219 | 220 | Ok((buf, out_ctx)) 221 | } 222 | 223 | fn decrypt_res_in_place( 224 | _hdr: Header, 225 | buf: &'a mut [u8], 226 | enc_key: &[u8], 227 | secret: &[u8], 228 | ) -> Result<&'a mut [u8]> 229 | where 230 | KDF: Kdf, 231 | AEAD: Aead, 232 | { 233 | // buf contains [res_nonce res tag] 234 | let res_nonce_size = res_nonce_size::(); 235 | let tag_size = AeadTag::::size(); 236 | if buf.len() < res_nonce_size + tag_size { 237 | return Err(Error::InvalidInput); 238 | } 239 | 240 | let (res_nonce, buf) = buf.split_at_mut(res_nonce_size); 241 | let (buf, tag) = buf.split_at_mut(buf.len() - tag_size); 242 | 243 | aead_open::(secret, enc_key, res_nonce, buf, tag)?; 244 | Ok(buf) 245 | } 246 | } 247 | 248 | fn kem_decap( 249 | priv_key: &::PrivateKey, 250 | hdr: &Header, 251 | enc_key_bytes: &[u8], 252 | tag_bytes: &[u8], 253 | buf: &mut [u8], 254 | ) -> Result> 255 | where 256 | KEM: Kem, 257 | KDF: Kdf, 258 | AEAD: Aead, 259 | { 260 | let enc_key = ::EncappedKey::from_bytes(enc_key_bytes)?; 261 | let tag = AeadTag::from_bytes(tag_bytes)?; 262 | 263 | let mut info = [0u8; LABEL_REQ.len() + 1 + Header::SIZE]; 264 | compose_info::(hdr.cid, LABEL_REQ.as_bytes(), &mut &mut info[..])?; 265 | 266 | let mut recv_ctx = 267 | hpke::setup_receiver::(&OpModeR::Base, priv_key, &enc_key, &info)?; 268 | 269 | recv_ctx.open_in_place_detached(buf, &[], &tag)?; 270 | 271 | // maybe this allocation could be avoided 272 | let mut secret = vec![0; aead_key_size::()]; 273 | recv_ctx.export(LABEL_RES.as_bytes(), &mut secret)?; 274 | Ok(secret) 275 | } 276 | 277 | fn aead_open( 278 | secret: &[u8], 279 | key: &[u8], 280 | nonce: &[u8], 281 | buf: &mut [u8], 282 | tag: &[u8], 283 | ) -> Result<()> 284 | where 285 | KDF: Kdf, 286 | AEAD: Aead, 287 | { 288 | let mut salt = key.to_vec(); 289 | salt.extend_from_slice(nonce); 290 | let (key, nonce) = match ::KDF_ID { 291 | HkdfSha256::KDF_ID => { 292 | extract_and_expand::::HashImpl, Hmac<_>>(&salt, secret) 293 | .map(|(_prk, key, nonce)| (key, nonce)) 294 | } 295 | HkdfSha384::KDF_ID => { 296 | extract_and_expand::::HashImpl, Hmac<_>>(&salt, secret) 297 | .map(|(_prk, key, nonce)| (key, nonce)) 298 | } 299 | HkdfSha512::KDF_ID => { 300 | extract_and_expand::::HashImpl, Hmac<_>>(&salt, secret) 301 | .map(|(_prk, key, nonce)| (key, nonce)) 302 | } 303 | _ => return Err(Error::UnsupportedKdf), 304 | }?; 305 | 306 | let cipher = ::AeadImpl::new(&key); 307 | 308 | cipher 309 | .decrypt_in_place_detached(&nonce, &[], buf, GenericArray::from_slice(tag)) 310 | .map_err(|_| Error::AeadError) 311 | } 312 | 313 | pub(crate) fn encrypt_res( 314 | hdr: Header, 315 | res: &[u8], 316 | enc_key: &[u8], 317 | secret: &[u8], 318 | rng: &mut R, 319 | ) -> Result { 320 | dispatch!(encrypt_res_with_header <, R> (hdr, res, enc_key, secret, rng)) 321 | } 322 | 323 | fn encrypt_res_with_header( 324 | _hdr: Header, 325 | res: &[u8], 326 | enc_key: &[u8], 327 | secret: &[u8], 328 | rng: &mut R, 329 | ) -> Result { 330 | encrypt_res_with::(res, enc_key, secret, rng) 331 | } 332 | 333 | fn encrypt_res_with( 334 | res: &[u8], 335 | enc_key: &[u8], 336 | secret: &[u8], 337 | rng: &mut R, 338 | ) -> Result { 339 | let res_nonce_len = res_nonce_size::(); 340 | 341 | // buf will contain [enc_key res_nonce res tag] 342 | let mut buf = BytesMut::with_capacity( 343 | enc_key.len() + res_nonce_len + res.len() + AeadTag::::size(), 344 | ); 345 | buf.put(enc_key); 346 | // reserv for nonce, and fill with random data 347 | buf.put_bytes(0, res_nonce_len); 348 | rng.fill(&mut buf[enc_key.len()..]); 349 | 350 | // buf contains salt(enc_key + res_nonce) now 351 | 352 | let (key, nonce) = match ::KDF_ID { 353 | HkdfSha256::KDF_ID => { 354 | extract_and_expand::::HashImpl, Hmac<_>>(&buf, secret) 355 | .map(|(_prk, key, nonce)| (key, nonce)) 356 | } 357 | HkdfSha384::KDF_ID => { 358 | extract_and_expand::::HashImpl, Hmac<_>>(&buf, secret) 359 | .map(|(_prk, key, nonce)| (key, nonce)) 360 | } 361 | HkdfSha512::KDF_ID => { 362 | extract_and_expand::::HashImpl, Hmac<_>>(&buf, secret) 363 | .map(|(_prk, key, nonce)| (key, nonce)) 364 | } 365 | _ => return Err(Error::UnsupportedKdf), 366 | }?; 367 | 368 | let cipher = ::AeadImpl::new(&key); 369 | buf.put(res); 370 | 371 | let tag = cipher 372 | .encrypt_in_place_detached(&nonce, &[], &mut buf[enc_key.len() + res_nonce_len..]) 373 | .map_err(|_| Error::AeadError)?; 374 | 375 | buf.put(tag.as_slice()); 376 | let _enc_key = buf.split_to(enc_key.len()); 377 | 378 | Ok(buf) 379 | } 380 | 381 | pub(crate) fn decrypt_res_in_place( 382 | hdr: Header, 383 | enc_res: B, 384 | enc_key: &[u8], 385 | secret: &[u8], 386 | ) -> Result { 387 | dispatch!( decrypt_res_in_place <> (hdr, enc_res, enc_key, secret)) 388 | } 389 | 390 | type EEOut = ( 391 | GenericArray::OutputSize>, 392 | GenericArray::AeadImpl as KeySizeUser>::KeySize>, 393 | GenericArray::AeadImpl as AeadCore>::NonceSize>, 394 | ); 395 | 396 | fn extract_and_expand(salt: &[u8], secret: &[u8]) -> Result> 397 | where 398 | A: Aead, 399 | H: OutputSizeUser, 400 | I: HmacImpl, 401 | { 402 | let (prk, hk) = Hkdf::::extract(Some(salt), secret); 403 | let mut key: GenericArray::AeadImpl as KeySizeUser>::KeySize> = 404 | Default::default(); 405 | hk.expand(LABEL_AEAD_KEY.as_bytes(), &mut key) 406 | .map_err(|_| Error::InvalidInput)?; 407 | let mut nonce: GenericArray::AeadImpl as AeadCore>::NonceSize> = 408 | Default::default(); 409 | hk.expand(LABEL_AEAD_NONCE.as_bytes(), &mut nonce) 410 | .map_err(|_| Error::InvalidInput)?; 411 | 412 | Ok((prk, key, nonce)) 413 | } 414 | -------------------------------------------------------------------------------- /src/bhttp/builder.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. 2 | // Licensed under the Apache-2.0 license found in the LICENSE file or 3 | // at http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | use bytes::BufMut; 6 | 7 | use super::*; 8 | 9 | /// Entrypoint to build a bHTTP message. 10 | pub struct Builder { 11 | buf: B, 12 | framing: Framing, 13 | } 14 | 15 | impl Builder { 16 | /// Create a new builder. 17 | pub fn new(buf: B, framing: Framing) -> Self { 18 | Self { buf, framing } 19 | } 20 | 21 | /// Push request control data. 22 | pub fn push_ctrl( 23 | mut self, 24 | mut method: &[u8], 25 | mut scheme: &[u8], 26 | mut authority: &[u8], 27 | mut path: &[u8], 28 | ) -> Result> { 29 | if !self.framing.is_request() { 30 | return Err(Error::UnexpectedFraming); 31 | } 32 | 33 | if !self.buf.has_remaining_mut() { 34 | return Err(Error::ShortBuf); 35 | } 36 | self.buf.put_u8(self.framing as u8); 37 | 38 | compose_len_bytes(&mut self.buf, &mut method)?; 39 | compose_len_bytes(&mut self.buf, &mut scheme)?; 40 | compose_len_bytes(&mut self.buf, &mut authority)?; 41 | compose_len_bytes(&mut self.buf, &mut path)?; 42 | 43 | Ok(HeaderBuilder { 44 | buf: self.buf, 45 | framing: self.framing, 46 | appending: false, 47 | }) 48 | } 49 | 50 | /// Push informational/final response contral data. 51 | pub fn push_status(mut self, status: usize) -> Result> { 52 | if self.framing.is_request() { 53 | return Err(Error::UnexpectedFraming); 54 | } 55 | 56 | if !self.buf.has_remaining_mut() { 57 | return Err(Error::ShortBuf); 58 | } 59 | self.buf.put_u8(self.framing as u8); 60 | 61 | VarInt::try_from(status)?.compose(&mut self.buf)?; 62 | 63 | Ok(InfoBuilder { 64 | buf: self.buf, 65 | framing: self.framing, 66 | is_final: is_final_ctrl(status), 67 | appending: false, 68 | }) 69 | } 70 | } 71 | 72 | /// Build response informational/final control data. 73 | pub struct RCtrlBuilder { 74 | buf: B, 75 | framing: Framing, 76 | } 77 | 78 | impl RCtrlBuilder { 79 | /// Push informational/final response contral data. 80 | pub fn push_status(mut self, status: usize) -> Result> { 81 | VarInt::try_from(status)?.compose(&mut self.buf)?; 82 | Ok(InfoBuilder { 83 | buf: self.buf, 84 | framing: self.framing, 85 | is_final: is_final_ctrl(status), 86 | appending: false, 87 | }) 88 | } 89 | } 90 | 91 | /// Build informational response fields. 92 | pub struct InfoBuilder { 93 | buf: B, 94 | framing: Framing, 95 | is_final: bool, 96 | appending: bool, 97 | } 98 | 99 | impl InfoBuilder { 100 | /// Push all the informational fields. 101 | pub fn push_fields(mut self, fields: &[(&[u8], &[u8])]) -> Result> { 102 | if self.is_final { 103 | return Err(Error::UnexpectedBuildState); 104 | } 105 | 106 | push_fields(&mut self.buf, self.framing, fields)?; 107 | Ok(RCtrlBuilder { 108 | buf: self.buf, 109 | framing: self.framing, 110 | }) 111 | } 112 | 113 | /// Append a single field line in indeterminate length mode. 114 | pub fn append_field(mut self, field: (&[u8], &[u8])) -> Result { 115 | if self.framing.known_len() { 116 | return Err(Error::UnexpectedFraming); 117 | } 118 | 119 | if self.is_final { 120 | return Err(Error::UnexpectedBuildState); 121 | } 122 | 123 | let (mut name, mut value) = field; 124 | if name.is_empty() { 125 | return Err(Error::InvalidInput); 126 | } 127 | compose_len_bytes(&mut self.buf, &mut name)?; 128 | compose_len_bytes(&mut self.buf, &mut value)?; 129 | self.appending = true; 130 | Ok(self) 131 | } 132 | 133 | /// Finish appending field line. 134 | pub fn done(mut self) -> Result> { 135 | if self.framing.known_len() { 136 | return Err(Error::UnexpectedFraming); 137 | } 138 | 139 | if self.is_final || !self.appending { 140 | return Err(Error::UnexpectedBuildState); 141 | } 142 | 143 | if !self.buf.has_remaining_mut() { 144 | return Err(Error::ShortBuf); 145 | } 146 | self.buf.put_u8(CONTENT_TERMINATOR); 147 | 148 | Ok(RCtrlBuilder { 149 | buf: self.buf, 150 | framing: self.framing, 151 | }) 152 | } 153 | 154 | /// Move to next builder in chain. 155 | pub fn next(self) -> Result> { 156 | if !self.is_final { 157 | return Err(Error::UnexpectedBuildState); 158 | } 159 | 160 | Ok(HeaderBuilder { 161 | buf: self.buf, 162 | framing: self.framing, 163 | appending: false, 164 | }) 165 | } 166 | } 167 | 168 | /// Build headers. 169 | pub struct HeaderBuilder { 170 | buf: B, 171 | framing: Framing, 172 | appending: bool, 173 | } 174 | 175 | impl HeaderBuilder { 176 | /// Push all the headers. 177 | pub fn push_headers(mut self, fields: &[(&[u8], &[u8])]) -> Result> { 178 | push_fields(&mut self.buf, self.framing, fields)?; 179 | Ok(ContentBuilder { 180 | buf: self.buf, 181 | framing: self.framing, 182 | appending: false, 183 | }) 184 | } 185 | 186 | /// Append a single header in indeterminate length mode. 187 | pub fn append_header(mut self, field: (&[u8], &[u8])) -> Result { 188 | if self.framing.known_len() { 189 | return Err(Error::UnexpectedFraming); 190 | } 191 | 192 | let (mut name, mut value) = field; 193 | if name.is_empty() { 194 | return Err(Error::InvalidInput); 195 | } 196 | compose_len_bytes(&mut self.buf, &mut name)?; 197 | compose_len_bytes(&mut self.buf, &mut value)?; 198 | self.appending = true; 199 | Ok(self) 200 | } 201 | 202 | /// Move to next builder in chain. 203 | pub fn next(mut self) -> Result> { 204 | if !self.appending { 205 | return Err(Error::UnexpectedBuildState); 206 | } 207 | 208 | if !self.buf.has_remaining_mut() { 209 | return Err(Error::ShortBuf); 210 | } 211 | self.buf.put_u8(CONTENT_TERMINATOR); 212 | Ok(ContentBuilder { 213 | buf: self.buf, 214 | framing: self.framing, 215 | appending: false, 216 | }) 217 | } 218 | } 219 | 220 | /// Build content. 221 | pub struct ContentBuilder { 222 | buf: B, 223 | framing: Framing, 224 | appending: bool, 225 | } 226 | 227 | impl ContentBuilder { 228 | /// Push content at once. 229 | pub fn push_content(mut self, mut content: &[u8]) -> Result> { 230 | let empty = content.is_empty(); 231 | compose_len_bytes(&mut self.buf, &mut content)?; 232 | 233 | // Content has already been terminated if empty. 234 | if !self.framing.known_len() && !empty { 235 | if !self.buf.has_remaining_mut() { 236 | return Err(Error::ShortBuf); 237 | } 238 | self.buf.put_u8(CONTENT_TERMINATOR); 239 | } 240 | 241 | Ok(TailerBuilder { 242 | buf: self.buf, 243 | framing: self.framing, 244 | appending: false, 245 | }) 246 | } 247 | 248 | /// Append a content chunk in indeterminate length mode. 249 | pub fn append_chunk(mut self, mut chunk: &[u8]) -> Result { 250 | if chunk.is_empty() { 251 | return Err(Error::InvalidInput); 252 | } 253 | 254 | compose_len_bytes(&mut self.buf, &mut chunk)?; 255 | self.appending = true; 256 | Ok(self) 257 | } 258 | 259 | /// Move to next builder in chain. 260 | pub fn next(mut self) -> Result> { 261 | if !self.buf.has_remaining_mut() { 262 | return Err(Error::ShortBuf); 263 | } 264 | self.buf.put_u8(CONTENT_TERMINATOR); 265 | 266 | Ok(TailerBuilder { 267 | buf: self.buf, 268 | framing: self.framing, 269 | appending: false, 270 | }) 271 | } 272 | } 273 | 274 | /// Build tailers. 275 | pub struct TailerBuilder { 276 | buf: B, 277 | framing: Framing, 278 | appending: bool, 279 | } 280 | 281 | impl TailerBuilder { 282 | /// Push all tailers at once. 283 | pub fn push_tailers(mut self, fields: &[(&[u8], &[u8])]) -> Result> { 284 | push_fields(&mut self.buf, self.framing, fields)?; 285 | Ok(PaddingBuilder { buf: self.buf }) 286 | } 287 | 288 | /// Append a single tailer in indeterminate length mode. 289 | pub fn append_tailer(mut self, field: (&[u8], &[u8])) -> Result { 290 | if self.framing.known_len() { 291 | return Err(Error::UnexpectedFraming); 292 | } 293 | 294 | let (mut name, mut value) = field; 295 | if name.is_empty() { 296 | return Err(Error::InvalidInput); 297 | } 298 | compose_len_bytes(&mut self.buf, &mut name)?; 299 | compose_len_bytes(&mut self.buf, &mut value)?; 300 | self.appending = true; 301 | Ok(self) 302 | } 303 | 304 | /// Move to next builder in chain. 305 | pub fn next(mut self) -> Result> { 306 | if !self.appending { 307 | return Err(Error::UnexpectedBuildState); 308 | } 309 | 310 | if !self.buf.has_remaining_mut() { 311 | return Err(Error::ShortBuf); 312 | } 313 | self.buf.put_u8(CONTENT_TERMINATOR); 314 | Ok(PaddingBuilder { buf: self.buf }) 315 | } 316 | } 317 | 318 | /// Build padding. 319 | pub struct PaddingBuilder { 320 | buf: B, 321 | } 322 | 323 | impl PaddingBuilder { 324 | /// Push n bytes of padding. 325 | pub fn push_padding(mut self, n: usize) -> Result<()> { 326 | if self.buf.remaining_mut() < n { 327 | return Err(Error::ShortBuf); 328 | } 329 | self.buf.put_bytes(CONTENT_TERMINATOR, n); 330 | Ok(()) 331 | } 332 | } 333 | 334 | fn push_fields(buf: &mut B, framing: Framing, fields: &[(&[u8], &[u8])]) -> Result<()> { 335 | if framing.known_len() { 336 | push_fields_with_len(buf, fields) 337 | } else { 338 | push_fields_no_len(buf, fields) 339 | } 340 | } 341 | 342 | fn push_fields_with_len(buf: &mut B, fields: &[(&[u8], &[u8])]) -> Result<()> { 343 | let mut len = 0; 344 | for (name, value) in fields.iter() { 345 | len += VarInt::try_from(name.len())?.size(); 346 | len += name.len(); 347 | len += VarInt::try_from(value.len())?.size(); 348 | len += value.len(); 349 | } 350 | 351 | let n = VarInt::try_from(len)?; 352 | if buf.remaining_mut() < n.size() + len { 353 | return Err(Error::ShortBuf); 354 | } 355 | 356 | n.compose(buf)?; 357 | 358 | for (mut name, mut value) in fields.iter() { 359 | compose_len_bytes(buf, &mut name)?; 360 | compose_len_bytes(buf, &mut value)?; 361 | } 362 | 363 | Ok(()) 364 | } 365 | 366 | fn push_fields_no_len(buf: &mut B, fields: &[(&[u8], &[u8])]) -> Result<()> { 367 | for (mut name, mut value) in fields.iter() { 368 | if name.is_empty() { 369 | return Err(Error::InvalidInput); 370 | } 371 | compose_len_bytes(buf, &mut name)?; 372 | compose_len_bytes(buf, &mut value)?; 373 | } 374 | 375 | if !buf.has_remaining_mut() { 376 | return Err(Error::ShortBuf); 377 | } 378 | buf.put_u8(CONTENT_TERMINATOR); 379 | 380 | Ok(()) 381 | } 382 | 383 | // If data is empty, 1 byte of 0 will be pushed. 384 | fn compose_len_bytes(buf: &mut B, data: &mut T) -> Result<()> { 385 | let len = data.remaining(); 386 | let n = VarInt::try_from(len)?; 387 | 388 | if buf.remaining_mut() < n.size() + len { 389 | return Err(Error::ShortBuf); 390 | } 391 | 392 | n.compose(buf)?; 393 | buf.put(data); 394 | Ok(()) 395 | } 396 | 397 | #[cfg(test)] 398 | mod tests { 399 | use super::super::tests::*; 400 | use super::*; 401 | 402 | #[test] 403 | fn build_known_len_req() { 404 | let mut buf = Vec::new(); 405 | Builder::new(&mut buf, Framing::KnownLenReq) 406 | .push_ctrl(b"GET", b"https", b"", b"/hello.txt") 407 | .unwrap() 408 | .push_headers(&[ 409 | ( 410 | &b"user-agent"[..], 411 | &b"curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"[..], 412 | ), 413 | (&b"host"[..], &b"www.example.com"[..]), 414 | (&b"accept-language"[..], &b"en, mi"[..]), 415 | ]) 416 | .unwrap() 417 | .push_content(&[]) 418 | .unwrap() 419 | .push_tailers(&[]) 420 | .unwrap(); 421 | assert_eq!(EXAMPLE_KNOWN_LEN_REQ2, buf); 422 | } 423 | 424 | #[test] 425 | fn build_ind_len_req() { 426 | let mut buf = Vec::new(); 427 | Builder::new(&mut buf, Framing::IndLenReq) 428 | .push_ctrl(b"GET", b"https", b"", b"/hello.txt") 429 | .unwrap() 430 | .push_headers(&[ 431 | ( 432 | &b"user-agent"[..], 433 | &b"curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"[..], 434 | ), 435 | (&b"host"[..], &b"www.example.com"[..]), 436 | (&b"accept-language"[..], &b"en, mi"[..]), 437 | ]) 438 | .unwrap() 439 | .push_content(&[]) 440 | .unwrap() 441 | .push_tailers(&[]) 442 | .unwrap() 443 | .push_padding(10) 444 | .unwrap(); 445 | assert_eq!(EXAMPLE_IND_LEN_REQ1, buf); 446 | } 447 | 448 | #[test] 449 | fn build_known_len_res() { 450 | let mut buf = Vec::new(); 451 | Builder::new(&mut buf, Framing::KnownLenRes) 452 | .push_status(200) 453 | .unwrap() 454 | .next() 455 | .unwrap() 456 | .push_headers(&[]) 457 | .unwrap() 458 | .push_content("This content contains CRLF.\r\n".as_bytes()) 459 | .unwrap() 460 | .push_tailers(&[("trailer".as_bytes(), "text".as_bytes())]) 461 | .unwrap(); 462 | assert_eq!(EXAMPLE_KNOWN_LEN_RES1, buf); 463 | } 464 | 465 | #[test] 466 | fn build_ind_len_res() { 467 | let mut buf = Vec::new(); 468 | Builder::new(&mut buf, Framing::IndLenRes) 469 | .push_status(102) 470 | .unwrap() 471 | .push_fields(&[("running".as_bytes(), r#""sleep 15""#.as_bytes())]) 472 | .unwrap() 473 | .push_status(103) 474 | .unwrap() 475 | .push_fields(&[ 476 | ( 477 | "link".as_bytes(), 478 | r#"; rel=preload; as=style"#.as_bytes(), 479 | ), 480 | ( 481 | "link".as_bytes(), 482 | r#"; rel=preload; as=script"#.as_bytes(), 483 | ), 484 | ]) 485 | .unwrap() 486 | .push_status(200) 487 | .unwrap() 488 | .next() 489 | .unwrap() 490 | .push_headers(&[ 491 | ( 492 | "date".as_bytes(), 493 | r#"Mon, 27 Jul 2009 12:28:53 GMT"#.as_bytes(), 494 | ), 495 | ("server".as_bytes(), "Apache".as_bytes()), 496 | ( 497 | "last-modified".as_bytes(), 498 | "Wed, 22 Jul 2009 19:15:56 GMT".as_bytes(), 499 | ), 500 | ("etag".as_bytes(), r#""34aa387-d-1568eb00""#.as_bytes()), 501 | ("accept-ranges".as_bytes(), "bytes".as_bytes()), 502 | ("content-length".as_bytes(), "51".as_bytes()), 503 | ("vary".as_bytes(), "Accept-Encoding".as_bytes()), 504 | ("content-type".as_bytes(), "text/plain".as_bytes()), 505 | ]) 506 | .unwrap() 507 | .push_content("Hello World! My content includes a trailing CRLF.\r\n".as_bytes()) 508 | .unwrap() 509 | .push_tailers(&[]) 510 | .unwrap(); 511 | 512 | assert_eq!(EXAMPLE_IND_LEN_RES1, &buf); 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /src/bhttp/parser.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. 2 | // Licensed under the Apache-2.0 license found in the LICENSE file or 3 | // at http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | // All the iterators are fused, so the consumed len can be accquired 6 | // later. 7 | use super::*; 8 | use std::fmt; 9 | 10 | macro_rules! consumed { 11 | ($iter:expr) => {{ 12 | let n = $iter.slice.len(); 13 | let mut err = None; 14 | while let Some(v) = $iter.next() { 15 | match v { 16 | Ok(_) => {} 17 | Err(e) => { 18 | err = Some(e); 19 | break; 20 | } 21 | } 22 | } 23 | match err { 24 | Some(e) => Err(e), 25 | None => Ok(n - $iter.slice.len()), 26 | } 27 | }}; 28 | } 29 | 30 | macro_rules! iter_bail { 31 | ($self:expr, $e:expr) => { 32 | match $e { 33 | Ok(v) => v, 34 | Err(e) => { 35 | $self.done = true; 36 | return Some(Err(e)); 37 | } 38 | } 39 | }; 40 | } 41 | 42 | /// Entrypoint to parse a bHTTP message. 43 | #[derive(Clone, Copy)] 44 | pub struct Parser<'a> { 45 | slice: &'a [u8], 46 | } 47 | 48 | impl<'a> Parser<'a> { 49 | /// Create a new parser. 50 | pub fn new(slice: &'a [u8]) -> Self { 51 | Self { slice } 52 | } 53 | 54 | /// Parse the framing from the buffer. 55 | pub fn framing(&self) -> Result { 56 | if self.slice.is_empty() { 57 | return Err(Error::ShortBuf); 58 | } 59 | Framing::try_from(self.slice[0]) 60 | } 61 | 62 | /// Consume the parser, and convert it into a request control data 63 | /// parser. 64 | pub fn next_req(mut self) -> Result> { 65 | let framing = self.framing()?; 66 | if !framing.is_request() { 67 | return Err(Error::UnexpectedFraming); 68 | } 69 | self.slice.advance(1); 70 | Ok(ReqCtrlParser { 71 | slice: self.slice, 72 | framing, 73 | }) 74 | } 75 | 76 | /// Consume the parser, and convert it into a response control data 77 | /// parser. 78 | pub fn next_res(mut self) -> Result> { 79 | let framing = self.framing()?; 80 | if framing.is_request() { 81 | return Err(Error::UnexpectedFraming); 82 | } 83 | self.slice.advance(1); 84 | Ok(ResCtrlParser { 85 | slice: self.slice, 86 | framing, 87 | }) 88 | } 89 | } 90 | 91 | /// Request control data parser. 92 | #[derive(Clone, Copy)] 93 | pub struct ReqCtrlParser<'a> { 94 | slice: &'a [u8], 95 | framing: Framing, 96 | } 97 | 98 | impl<'a> ReqCtrlParser<'a> { 99 | /// Parse and return the request control data. 100 | pub fn get(&self) -> Result { 101 | let mut slice = self.slice; 102 | let method = get_sized(&mut slice)?; 103 | let scheme = get_sized(&mut slice)?; 104 | let authority = get_sized(&mut slice)?; 105 | let path = get_sized(&mut slice)?; 106 | Ok(ReqCtrl { 107 | method, 108 | scheme, 109 | authority, 110 | path, 111 | }) 112 | } 113 | 114 | /// Consume the parser, and create a parser for headers. 115 | pub fn next(mut self) -> Result> { 116 | get_sized(&mut self.slice)?; 117 | get_sized(&mut self.slice)?; 118 | get_sized(&mut self.slice)?; 119 | get_sized(&mut self.slice)?; 120 | Ok(HeaderParser { 121 | slice: self.slice, 122 | framing: self.framing, 123 | }) 124 | } 125 | } 126 | 127 | /// Request control data. 128 | #[derive(Clone, Copy)] 129 | pub struct ReqCtrl<'a> { 130 | /// method 131 | pub method: &'a [u8], 132 | /// scheme 133 | pub scheme: &'a [u8], 134 | /// authority 135 | pub authority: &'a [u8], 136 | /// path 137 | pub path: &'a [u8], 138 | } 139 | 140 | /// Response control data parser. 141 | #[derive(Clone, Copy)] 142 | pub struct ResCtrlParser<'a> { 143 | slice: &'a [u8], 144 | framing: Framing, 145 | } 146 | 147 | impl<'a> ResCtrlParser<'a> { 148 | /// Iterator over the informational and final control data. 149 | pub fn iter(&self) -> ResCtrlIter { 150 | ResCtrlIter { 151 | slice: self.slice, 152 | framing: self.framing, 153 | done: false, 154 | } 155 | } 156 | 157 | /// Consume the parser, and create a parser for headers. 158 | pub fn next(self) -> Result> { 159 | let mut iter = self.iter(); 160 | let n = consumed!(iter)?; 161 | Ok(HeaderParser { 162 | slice: &self.slice[n..], 163 | framing: self.framing, 164 | }) 165 | } 166 | } 167 | 168 | /// Iterator over items in response control data. 169 | pub struct ResCtrlIter<'a> { 170 | slice: &'a [u8], 171 | framing: Framing, 172 | done: bool, 173 | } 174 | 175 | impl<'a> Iterator for ResCtrlIter<'a> { 176 | type Item = Result<(usize, Option>)>; 177 | fn next(&mut self) -> Option { 178 | if self.done { 179 | return None; 180 | } 181 | 182 | let status = iter_bail!(self, VarInt::parse(&mut self.slice)).as_usize(); 183 | 184 | if is_final_ctrl(status) { 185 | self.done = true; 186 | return Some(Ok((status, None))); 187 | } 188 | 189 | // calculate the offset 190 | let mut iter = FieldIter { 191 | slice: self.slice, 192 | framing: self.framing, 193 | done: false, 194 | len: None, 195 | }; 196 | let n = iter_bail!(self, consumed!(iter)); 197 | 198 | let iter = FieldIter { 199 | slice: self.slice, 200 | framing: self.framing, 201 | done: false, 202 | len: None, 203 | }; 204 | 205 | self.slice.advance(n); 206 | Some(Ok((status, Some(iter)))) 207 | } 208 | } 209 | 210 | /// Parser for header section. 211 | #[derive(Clone, Copy)] 212 | pub struct HeaderParser<'a> { 213 | slice: &'a [u8], 214 | framing: Framing, 215 | } 216 | 217 | impl<'a> HeaderParser<'a> { 218 | /// Return an iterator over each header. 219 | pub fn iter(&self) -> FieldIter { 220 | let truncated = self.slice.is_empty(); 221 | FieldIter { 222 | slice: self.slice, 223 | framing: self.framing, 224 | done: truncated, 225 | len: None, 226 | } 227 | } 228 | 229 | /// Consume current parser, and return a new one for content. 230 | pub fn next(self) -> Result> { 231 | let mut iter = self.iter(); 232 | let n = consumed!(iter)?; 233 | Ok(ContentParser { 234 | slice: &self.slice[n..], 235 | framing: self.framing, 236 | }) 237 | } 238 | } 239 | 240 | /// Parser for content. 241 | #[derive(Clone, Copy)] 242 | pub struct ContentParser<'a> { 243 | slice: &'a [u8], 244 | framing: Framing, 245 | } 246 | 247 | impl<'a> ContentParser<'a> { 248 | /// Return an iterator over each content chunk. 249 | pub fn iter(&self) -> ContentIter { 250 | let truncated = self.slice.is_empty(); 251 | ContentIter { 252 | slice: self.slice, 253 | framing: self.framing, 254 | done: truncated, 255 | } 256 | } 257 | 258 | /// Consume current parser, and return a new one for tailers. 259 | pub fn next(self) -> Result> { 260 | let mut iter = self.iter(); 261 | let n = consumed!(iter)?; 262 | Ok(TailerParser { 263 | slice: &self.slice[n..], 264 | framing: self.framing, 265 | }) 266 | } 267 | } 268 | 269 | /// Iterator for content chunks. 270 | pub struct ContentIter<'a> { 271 | slice: &'a [u8], 272 | framing: Framing, 273 | done: bool, 274 | } 275 | 276 | impl<'a> Iterator for ContentIter<'a> { 277 | type Item = Result<&'a [u8]>; 278 | fn next(&mut self) -> Option { 279 | if self.done { 280 | return None; 281 | } 282 | 283 | if self.framing.known_len() { 284 | self.done = true; 285 | Some(get_sized(&mut self.slice)) 286 | } else { 287 | let r = match is_terminator(&mut self.slice) { 288 | Err(e) => Some(Err(e)), 289 | Ok(true) => { 290 | self.done = true; 291 | None 292 | } 293 | Ok(false) => Some(get_sized(&mut self.slice)), 294 | }; 295 | 296 | // Fuse parsing on error. 297 | if let Some(Err(_)) = &r { 298 | self.done = true; 299 | } 300 | r 301 | } 302 | } 303 | } 304 | 305 | /// Parser for tailer section. 306 | #[derive(Clone, Copy)] 307 | pub struct TailerParser<'a> { 308 | slice: &'a [u8], 309 | framing: Framing, 310 | } 311 | 312 | impl<'a> TailerParser<'a> { 313 | /// Return an iterator over each field line in tailer section. 314 | pub fn iter(&self) -> FieldIter { 315 | let truncated = self.slice.is_empty(); 316 | FieldIter { 317 | slice: self.slice, 318 | framing: self.framing, 319 | done: truncated, 320 | len: None, 321 | } 322 | } 323 | 324 | /// Consume current parser, and return one for parse padding. 325 | pub fn next(self) -> Result> { 326 | let mut iter = self.iter(); 327 | let n = consumed!(iter)?; 328 | Ok(PaddingParser { 329 | slice: &self.slice[n..], 330 | }) 331 | } 332 | } 333 | 334 | /// Parser for padding section. 335 | #[derive(Clone, Copy)] 336 | pub struct PaddingParser<'a> { 337 | slice: &'a [u8], 338 | } 339 | 340 | impl<'a> PaddingParser<'a> { 341 | /// Return the length of the padding. 342 | pub fn len(&self) -> usize { 343 | self.slice.len() 344 | } 345 | } 346 | 347 | /// Iterator over the fields, used in header, tailer, and 348 | /// informational response control data. 349 | pub struct FieldIter<'a> { 350 | slice: &'a [u8], 351 | framing: Framing, 352 | done: bool, 353 | len: Option, 354 | } 355 | 356 | impl<'a> Iterator for FieldIter<'a> { 357 | type Item = Result<(&'a [u8], &'a [u8])>; 358 | fn next(&mut self) -> Option { 359 | if self.done { 360 | return None; 361 | } 362 | 363 | if self.framing.known_len() { 364 | let mut len = match self.len { 365 | None => { 366 | match VarInt::parse(&mut self.slice).and_then(|n| { 367 | let n = n.as_usize(); 368 | if self.slice.len() < n { 369 | Err(Error::ShortBuf) 370 | } else { 371 | Ok(n) 372 | } 373 | }) { 374 | Ok(n) => n, 375 | Err(e) => { 376 | self.done = true; 377 | return Some(Err(e)); 378 | } 379 | } 380 | } 381 | Some(v) => v, 382 | }; 383 | 384 | if len == 0 { 385 | self.done = true; 386 | return None; 387 | } 388 | 389 | let n = self.slice.len(); 390 | let r = parse_field(&mut self.slice); 391 | if r.is_err() { 392 | self.done = true; 393 | } else { 394 | len -= n - self.slice.len(); 395 | self.len = Some(len); 396 | } 397 | Some(r) 398 | } else { 399 | let r = match is_terminator(&mut self.slice) { 400 | Err(e) => Some(Err(e)), 401 | Ok(true) => { 402 | self.done = true; 403 | None 404 | } 405 | Ok(false) => Some(parse_field(&mut self.slice)), 406 | }; 407 | // Fuse parsing on error. 408 | if let Some(Err(_)) = &r { 409 | self.done = true; 410 | } 411 | r 412 | } 413 | } 414 | } 415 | 416 | fn get_sized<'a>(slice: &mut &'a [u8]) -> Result<&'a [u8]> { 417 | let len = VarInt::parse(slice)?.as_usize(); 418 | if slice.remaining() < len { 419 | return Err(Error::ShortBuf); 420 | } 421 | let seg = &slice[..len]; 422 | slice.advance(len); 423 | Ok(seg) 424 | } 425 | 426 | fn is_terminator(slice: &mut &[u8]) -> Result { 427 | // missing terminator 428 | if slice.is_empty() { 429 | return Err(Error::InvalidInput); 430 | } 431 | 432 | if slice[0] == CONTENT_TERMINATOR { 433 | slice.advance(1); 434 | Ok(true) 435 | } else { 436 | Ok(false) 437 | } 438 | } 439 | 440 | fn parse_field<'a>(slice: &mut &'a [u8]) -> Result<(&'a [u8], &'a [u8])> { 441 | Ok((get_sized(slice)?, get_sized(slice)?)) 442 | } 443 | 444 | impl<'a> fmt::Display for Parser<'a> { 445 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 446 | let str_or_bytes = |f: &mut fmt::Formatter, prefix: &str, b: &[u8], suffix: &str| { 447 | if b.is_empty() { 448 | return Ok(()); 449 | } 450 | 451 | match std::str::from_utf8(b) { 452 | Ok(s) => write!(f, "{}{}{}", prefix, s, suffix), 453 | _ => write!(f, "{}{:?}{}", prefix, b, suffix), 454 | } 455 | }; 456 | 457 | let framing = self.framing().map_err(|_| fmt::Error)?; 458 | writeln!(f, "F {:?}", framing)?; 459 | 460 | let headers = if framing.is_request() { 461 | let req_ctrl = self.next_req().map_err(|_| fmt::Error)?; 462 | let ctrl = req_ctrl.get().map_err(|_| fmt::Error)?; 463 | str_or_bytes(f, "| method: ", ctrl.method, "\n")?; 464 | str_or_bytes(f, "| scheme: ", ctrl.scheme, "\n")?; 465 | str_or_bytes(f, "| authority: ", ctrl.authority, "\n")?; 466 | str_or_bytes(f, "| path: ", ctrl.path, "\n")?; 467 | req_ctrl.next().map_err(|_| fmt::Error)? 468 | } else { 469 | let res_ctrl = self.next_res().map_err(|_| fmt::Error)?; 470 | for item in res_ctrl.iter() { 471 | let (status, info) = item.map_err(|_| fmt::Error)?; 472 | writeln!(f, "| status: {}", status)?; 473 | if let Some(iter) = info { 474 | for item in iter { 475 | let (name, value) = item.map_err(|_| fmt::Error)?; 476 | str_or_bytes(f, "| ", name, ": ")?; 477 | str_or_bytes(f, "", value, "\n")?; 478 | } 479 | } 480 | } 481 | 482 | res_ctrl.next().map_err(|_| fmt::Error)? 483 | }; 484 | 485 | for item in headers.iter() { 486 | let (name, value) = item.map_err(|_| fmt::Error)?; 487 | str_or_bytes(f, "H ", name, ": ")?; 488 | str_or_bytes(f, "", value, "\n")?; 489 | } 490 | 491 | let content = headers.next().map_err(|_| fmt::Error)?; 492 | for item in content.iter() { 493 | let chunk = item.map_err(|_| fmt::Error)?; 494 | str_or_bytes(f, "C ", chunk, "\n")?; 495 | } 496 | 497 | let tailers = content.next().map_err(|_| fmt::Error)?; 498 | for item in tailers.iter() { 499 | let (name, value) = item.map_err(|_| fmt::Error)?; 500 | str_or_bytes(f, "T ", name, ": ")?; 501 | str_or_bytes(f, "", value, "\n")?; 502 | } 503 | 504 | let padding = tailers.next().map_err(|_| fmt::Error)?; 505 | write!(f, "P: {}", padding.len()) 506 | } 507 | } 508 | 509 | #[cfg(test)] 510 | mod tests { 511 | use super::super::tests::*; 512 | use super::*; 513 | use hex_literal::hex; 514 | 515 | #[test] 516 | fn test_sized_segment() { 517 | let buf = hex!("06010203040506"); 518 | let mut slice = &buf[..]; 519 | let seg = get_sized(&mut slice).unwrap(); 520 | assert_eq!(0, slice.len()); 521 | assert_eq!(&buf[1..], seg); 522 | } 523 | 524 | #[test] 525 | fn parse_known_len_req() { 526 | let parser = Parser::new(EXAMPLE_KNOWN_LEN_REQ1); 527 | println!("{}", parser); 528 | assert_eq!(Ok(Framing::KnownLenReq), parser.framing()); 529 | assert!(parser.next_res().is_err()); 530 | let req_ctrl = parser.next_req().unwrap(); 531 | let ctrl = req_ctrl.get().unwrap(); 532 | assert_eq!(b"GET", ctrl.method); 533 | assert_eq!(b"https", ctrl.scheme); 534 | assert_eq!(b"example.com", ctrl.authority); 535 | assert_eq!(b"/", ctrl.path); 536 | let headers = req_ctrl.next().unwrap(); 537 | // all the rest are truncated 538 | assert_eq!(None, headers.iter().next()); 539 | let content = headers.next().unwrap(); 540 | assert_eq!(None, content.iter().next()); 541 | let tailers = content.next().unwrap(); 542 | assert_eq!(None, tailers.iter().next()); 543 | let padding = tailers.next().unwrap(); 544 | assert_eq!(0, padding.len()); 545 | 546 | let parser = Parser::new(EXAMPLE_KNOWN_LEN_REQ2); 547 | println!("{}", parser); 548 | assert_eq!(Ok(Framing::KnownLenReq), parser.framing()); 549 | let req_ctrl = parser.next_req().unwrap(); 550 | let ctrl = req_ctrl.get().unwrap(); 551 | assert_eq!(b"GET", ctrl.method); 552 | assert_eq!(b"https", ctrl.scheme); 553 | assert_eq!(b"", ctrl.authority); 554 | assert_eq!(b"/hello.txt", ctrl.path); 555 | 556 | let headers = req_ctrl.next().unwrap(); 557 | let mut header_iter = headers.iter(); 558 | assert_eq!( 559 | ( 560 | "user-agent", 561 | "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3" 562 | ), 563 | unwrap_fieldline(header_iter.next()) 564 | ); 565 | assert_eq!( 566 | ("host", "www.example.com"), 567 | unwrap_fieldline(header_iter.next()) 568 | ); 569 | assert_eq!( 570 | ("accept-language", "en, mi"), 571 | unwrap_fieldline(header_iter.next()) 572 | ); 573 | assert_eq!(None, header_iter.next()); 574 | 575 | let content = headers.next().unwrap(); 576 | assert_eq!(Some(Ok(&[][..])), content.iter().next()); 577 | let tailers = content.next().unwrap(); 578 | assert_eq!(None, tailers.iter().next()); 579 | let padding = tailers.next().unwrap(); 580 | assert_eq!(0, padding.len()); 581 | } 582 | 583 | #[test] 584 | fn parse_ind_len_req() { 585 | let parser = Parser::new(EXAMPLE_IND_LEN_REQ1); 586 | println!("{}", parser); 587 | assert_eq!(Ok(Framing::IndLenReq), parser.framing()); 588 | let req_ctrl = parser.next_req().unwrap(); 589 | let ctrl = req_ctrl.get().unwrap(); 590 | assert_eq!(b"GET", ctrl.method); 591 | assert_eq!(b"https", ctrl.scheme); 592 | assert_eq!(b"", ctrl.authority); 593 | assert_eq!(b"/hello.txt", ctrl.path); 594 | let headers = req_ctrl.next().unwrap(); 595 | let mut header_iter = headers.iter(); 596 | assert_eq!( 597 | ( 598 | "user-agent", 599 | "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3" 600 | ), 601 | unwrap_fieldline(header_iter.next()) 602 | ); 603 | assert_eq!( 604 | ("host", "www.example.com"), 605 | unwrap_fieldline(header_iter.next()) 606 | ); 607 | assert_eq!( 608 | ("accept-language", "en, mi"), 609 | unwrap_fieldline(header_iter.next()) 610 | ); 611 | assert_eq!(None, header_iter.next()); 612 | 613 | let content = headers.next().unwrap(); 614 | assert_eq!(None, content.iter().next()); 615 | let tailers = content.next().unwrap(); 616 | assert_eq!(None, tailers.iter().next()); 617 | let padding = tailers.next().unwrap(); 618 | assert_eq!(10, padding.len()); 619 | 620 | // above request with truncation 621 | let parser = Parser::new(EXAMPLE_IND_LEN_REQ2); 622 | let req_ctrl = parser.next_req().unwrap(); 623 | let headers = req_ctrl.next().unwrap(); 624 | let content = headers.next().unwrap(); 625 | assert_eq!(None, content.iter().next()); 626 | let tailers = content.next().unwrap(); 627 | assert_eq!(None, tailers.iter().next()); 628 | let padding = tailers.next().unwrap(); 629 | assert_eq!(0, padding.len()); 630 | } 631 | 632 | #[test] 633 | fn parse_known_len_res() { 634 | let parser = Parser::new(EXAMPLE_KNOWN_LEN_RES1); 635 | println!("{}", parser); 636 | assert_eq!(Ok(Framing::KnownLenRes), parser.framing()); 637 | let res_ctrl = parser.next_res().unwrap(); 638 | 639 | let mut iter = res_ctrl.iter(); 640 | let (status, info) = iter.next().unwrap().unwrap(); 641 | assert_eq!(200, status); 642 | assert!(info.is_none()); 643 | 644 | let headers = res_ctrl.next().unwrap(); 645 | let mut iter = headers.iter(); 646 | assert_eq!(None, iter.next()); 647 | 648 | let content = headers.next().unwrap(); 649 | let mut iter = content.iter(); 650 | assert_eq!( 651 | &b"This content contains CRLF.\r\n"[..], 652 | iter.next().unwrap().unwrap() 653 | ); 654 | 655 | let tailers = content.next().unwrap(); 656 | let mut iter = tailers.iter(); 657 | assert_eq!(("trailer", "text"), unwrap_fieldline(iter.next())); 658 | let padding = tailers.next().unwrap(); 659 | assert_eq!(0, padding.len()); 660 | } 661 | 662 | #[test] 663 | fn parse_ind_len_res() { 664 | let parser = Parser::new(EXAMPLE_IND_LEN_RES1); 665 | println!("{}", parser); 666 | assert_eq!(Ok(Framing::IndLenRes), parser.framing()); 667 | 668 | let res_ctrl = parser.next_res().unwrap(); 669 | let mut iter = res_ctrl.iter(); 670 | let (status, info_iter) = iter.next().unwrap().unwrap(); 671 | assert_eq!(102, status); 672 | let mut info_iter = info_iter.unwrap(); 673 | assert_eq!( 674 | ("running", r#""sleep 15""#), 675 | unwrap_fieldline(info_iter.next()) 676 | ); 677 | assert_eq!(None, info_iter.next()); 678 | 679 | let (status, info_iter) = iter.next().unwrap().unwrap(); 680 | assert_eq!(103, status); 681 | let mut info_iter = info_iter.unwrap(); 682 | assert_eq!( 683 | ("link", r#"; rel=preload; as=style"#), 684 | unwrap_fieldline(info_iter.next()) 685 | ); 686 | assert_eq!( 687 | ("link", r#"; rel=preload; as=script"#), 688 | unwrap_fieldline(info_iter.next()) 689 | ); 690 | assert_eq!(None, info_iter.next()); 691 | let (status, info_iter) = iter.next().unwrap().unwrap(); 692 | assert_eq!(200, status); 693 | assert!(info_iter.is_none()); 694 | assert!(iter.next().is_none()); 695 | 696 | let headers = res_ctrl.next().unwrap(); 697 | let mut iter = headers.iter(); 698 | assert_eq!( 699 | ("date", "Mon, 27 Jul 2009 12:28:53 GMT"), 700 | unwrap_fieldline(iter.next()) 701 | ); 702 | assert_eq!(("server", "Apache"), unwrap_fieldline(iter.next())); 703 | assert_eq!( 704 | ("last-modified", "Wed, 22 Jul 2009 19:15:56 GMT"), 705 | unwrap_fieldline(iter.next()) 706 | ); 707 | assert_eq!( 708 | ("etag", r#""34aa387-d-1568eb00""#), 709 | unwrap_fieldline(iter.next()) 710 | ); 711 | assert_eq!(("accept-ranges", "bytes"), unwrap_fieldline(iter.next())); 712 | assert_eq!(("content-length", "51"), unwrap_fieldline(iter.next())); 713 | assert_eq!(("vary", "Accept-Encoding"), unwrap_fieldline(iter.next())); 714 | assert_eq!( 715 | ("content-type", "text/plain"), 716 | unwrap_fieldline(iter.next()) 717 | ); 718 | assert!(iter.next().is_none()); 719 | 720 | let content = headers.next().unwrap(); 721 | let mut iter = content.iter(); 722 | assert_eq!( 723 | &b"Hello World! My content includes a trailing CRLF.\r\n"[..], 724 | iter.next().unwrap().unwrap() 725 | ); 726 | assert!(iter.next().is_none()); 727 | 728 | let tailers = content.next().unwrap(); 729 | let mut iter = tailers.iter(); 730 | assert!(iter.next().is_none()); 731 | 732 | let padding = tailers.next().unwrap(); 733 | assert_eq!(0, padding.len()); 734 | } 735 | } 736 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cloudflare, Inc. 2 | // Licensed under the Apache-2.0 license found in the LICENSE file or 3 | // at http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | #![deny(missing_docs)] 6 | //! This library implements [draft-ietf-ohai-ohttp-06][draft]. 7 | //! 8 | //! [draft]: https://datatracker.ietf.org/doc/draft-ietf-ohai-ohttp/06/ 9 | //! 10 | //! # Quick start 11 | //! ``` 12 | //! use aloha::{bhttp, id, Config, Error}; 13 | //! use rand::thread_rng; 14 | //! 15 | //! # fn main() -> Result<(), Error> { 16 | //! // Some of the crypto functions require a RNG. 17 | //! let mut rng = thread_rng(); 18 | //! 19 | //! // [server] Generates a server side config with selected algorithms. 20 | //! let srv_conf = Config::builder() 21 | //! .with_id(1) 22 | //! .gen_keypair(id::KemId::X25519HKDFSHA256, &mut rng) 23 | //! .push_alg(id::KdfId::HKDFSHA256, id::AeadId::AESGCM128) 24 | //! .build()?; 25 | //! 26 | //! // [server] From the server side config, get a client side one and 27 | //! // deliver in to the client side after serializaion. 28 | //! let mut cli_conf_bytes = Vec::new(); 29 | //! srv_conf.get_client().compose(&mut cli_conf_bytes)?; 30 | //! 31 | //! // ... distribute the cli_conf_bytes to the client 32 | //! 33 | //! // [client] Parse the client config from raw bytes. 34 | //! let cli_conf = Config::parse(&mut cli_conf_bytes.as_slice())?; 35 | //! 36 | //! // [client] Build a bhttp request 37 | //! let mut req = Vec::new(); 38 | //! bhttp::Builder::new(&mut req, bhttp::Framing::KnownLenReq) 39 | //! .push_ctrl(b"GET", b"https", b"example.com", b"/ping")? 40 | //! .push_headers(&[("host".as_bytes(), "example.com".as_bytes())])?; 41 | //! 42 | //! // [client] Encrypt the request data and send it to the server. 43 | //! let (enc_req, cli_ctx) = cli_conf.encrypt_req(0, &req, &mut rng)?; 44 | //! 45 | //! // [server] Use the server side config to decrypt the request. 46 | //! let (dec_req, srv_ctx) = srv_conf.decrypt_req(&enc_req)?; 47 | //! assert_eq!(req, dec_req.as_ref()); 48 | //! 49 | //! // [server] Parse the bhttp msg. 50 | //! let parser = bhttp::Parser::new(&dec_req); 51 | //! let req_ctrl = parser.next_req()?; 52 | //! let ctrl = req_ctrl.get()?; 53 | //! assert_eq!(b"GET", ctrl.method); 54 | //! assert_eq!(b"https", ctrl.scheme); 55 | //! assert_eq!(b"example.com", ctrl.authority); 56 | //! assert_eq!(b"/ping", ctrl.path); 57 | //! let _headers = req_ctrl.next()?; 58 | //! 59 | //! // [server] Use the context to encrypt a (bhttp) response. 60 | //! let res = b"pong"; 61 | //! let enc_res = srv_ctx.encrypt_res(&res[..], &mut rng)?; 62 | //! // [client] Use the context to decrypt the response. 63 | //! let dec_res = cli_ctx.decrypt_res(&enc_res)?; 64 | //! assert_eq!(&res[..], &dec_res); 65 | //! # Ok(()) 66 | //! # } 67 | //! ``` 68 | 69 | use aead::{AeadCore, KeySizeUser}; 70 | use bytes::{Buf, BufMut, Bytes, BytesMut}; 71 | use core::ops::Deref; 72 | use generic_array::typenum::Unsigned; 73 | use hpke::aead::Aead; 74 | use hpke::kdf::Kdf; 75 | use hpke::kem::{DhP256HkdfSha256, Kem, X25519HkdfSha256}; 76 | use hpke::{Deserializable, HpkeError, Serializable}; 77 | use rand::Rng; 78 | use rand::{CryptoRng, RngCore}; 79 | use thiserror::Error as ThisError; 80 | 81 | pub mod bhttp; 82 | mod crypt; 83 | 84 | pub use crypt::InPlaceMut; 85 | 86 | /// HTTP media type for key config. 87 | pub const MT_KEY_CONFIG: &str = "application/ohttp-keys"; 88 | /// HTTP media type for oHTTP request. 89 | pub const MT_OHTTP_REQ: &str = "message/ohttp-req"; 90 | /// HTTP media type for oHTTP response. 91 | pub const MT_OHTTP_RES: &str = "message/ohttp-res"; 92 | 93 | /// Reexport of several HPKE algorithm IDs that supported by this 94 | /// library. 95 | pub mod id { 96 | use hpke::aead::{Aead, AesGcm128, AesGcm256, ChaCha20Poly1305}; 97 | use hpke::kdf::{self, Kdf}; 98 | use hpke::kem::{self, Kem}; 99 | 100 | /// Supported KEM IDs 101 | #[repr(u16)] 102 | pub enum KemId { 103 | /// DhP256HkdfSha256 104 | DHP256HKDFSHA256 = kem::DhP256HkdfSha256::KEM_ID, 105 | /// X25519HkdfSha256 106 | X25519HKDFSHA256 = kem::X25519HkdfSha256::KEM_ID, 107 | } 108 | 109 | /// Supported KDF IDs 110 | #[repr(u16)] 111 | pub enum KdfId { 112 | /// HkdfSha256 113 | HKDFSHA256 = kdf::HkdfSha256::KDF_ID, 114 | /// HkdfSha384 115 | HKDFSHA384 = kdf::HkdfSha384::KDF_ID, 116 | /// HkdfSha512 117 | HKDFSHA512 = kdf::HkdfSha512::KDF_ID, 118 | } 119 | 120 | /// Supported AEAD IDs 121 | #[repr(u16)] 122 | pub enum AeadId { 123 | /// AesGcm128 124 | AESGCM128 = AesGcm128::AEAD_ID, 125 | /// AesGcm256 126 | AESGCM256 = AesGcm256::AEAD_ID, 127 | /// ChaCha20Poly1305 128 | CHACHA20POLY1305 = ChaCha20Poly1305::AEAD_ID, 129 | } 130 | } 131 | 132 | const LABEL_REQ: &str = "message/bhttp request"; 133 | const LABEL_RES: &str = "message/bhttp response"; 134 | 135 | const LABEL_AEAD_KEY: &str = "key"; 136 | const LABEL_AEAD_NONCE: &str = "nonce"; 137 | 138 | /// Size of the ikm used to generate the key pair. 139 | const IKM_SIZE: usize = 32; 140 | 141 | const fn aead_key_size() -> usize { 142 | <::AeadImpl as KeySizeUser>::KeySize::USIZE 143 | } 144 | 145 | const fn aead_nonce_size() -> usize { 146 | <::AeadImpl as AeadCore>::NonceSize::USIZE 147 | } 148 | 149 | const fn res_nonce_size() -> usize { 150 | let a = aead_key_size::(); 151 | let b = aead_nonce_size::(); 152 | 153 | // get the max from the two in compile time 154 | [a, b][(a < b) as usize] 155 | } 156 | 157 | /// Errors used in this library. 158 | #[derive(ThisError, Debug, Clone)] 159 | pub enum Error { 160 | /// Provided buffer is too short 161 | #[error("Provided buffer is too short")] 162 | ShortBuf, 163 | /// Input data is invalid. 164 | #[error("Input data is invalid")] 165 | InvalidInput, 166 | /// Kem is not supported. 167 | #[error("Kem is not supported")] 168 | UnsupportedKem, 169 | /// Kdf is not supported. 170 | #[error("kdf is not supported")] 171 | UnsupportedKdf, 172 | /// Aead is not supported. 173 | #[error("Aead is not supported")] 174 | UnsupportedAead, 175 | 176 | /// Config ID in message doesn't match current config. 177 | #[error("config id mismatch")] 178 | ConfigIdMismatch, 179 | 180 | /// No private key in config. Happens when calling server side 181 | /// functions on client config. 182 | #[error("No private key in config")] 183 | NoPrivateKey, 184 | 185 | /// No ID provided in config. 186 | #[error("No ID provided in config")] 187 | MissingId, 188 | 189 | /// No public key provided in config. 190 | #[error("No public key provided in config")] 191 | MissingPublicKey, 192 | 193 | /// No symmetric algorithm set provided in config. 194 | #[error("No symmetric algorithm set provided in config")] 195 | MissingSymAlg, 196 | 197 | /// Opaque error from AEAD operations. 198 | #[error("Aead error")] 199 | AeadError, 200 | 201 | /// Errors from hpke crate. 202 | #[error(transparent)] 203 | Hpke(#[from] HpkeError), 204 | 205 | /// Errors from bhttp module. 206 | #[error(transparent)] 207 | Bhttp(#[from] bhttp::Error), 208 | } 209 | 210 | type Result = std::result::Result; 211 | 212 | #[derive(Clone)] 213 | enum PubKey { 214 | X25519HkdfSha256(::PublicKey), 215 | DhP256HkdfSha256(::PublicKey), 216 | } 217 | 218 | #[derive(Clone)] 219 | enum PrivKey { 220 | X25519HkdfSha256(::PrivateKey), 221 | DhP256HkdfSha256(::PrivateKey), 222 | } 223 | 224 | fn compose_to(buf: &mut BM, data: B) -> Result<()> { 225 | if buf.remaining_mut() < data.remaining() { 226 | return Err(Error::ShortBuf); 227 | } 228 | 229 | buf.put(data); 230 | Ok(()) 231 | } 232 | 233 | /// Config contains necessary parameters to establish a conversation 234 | /// between a client and a server. 235 | /// 236 | /// It can encrypt a request with [`Self::encrypt_req`] on the client 237 | /// side, or decrypt one via [`Self::decrypt_req`] on the server side. 238 | /// The internal difference between a client and a server config is 239 | /// that the latter one has the private key. Use [`Self::get_client`] 240 | /// to drop the private key and get a config for the client. 241 | #[derive(Clone)] 242 | pub struct Config { 243 | id: u8, 244 | pub_key: PubKey, 245 | priv_key: Option, 246 | algs: SymAlgs, 247 | } 248 | 249 | impl Config { 250 | /// Create a builder for building a server side config. 251 | pub fn builder() -> ConfigBuilder { 252 | Default::default() 253 | } 254 | 255 | /// Return the KEM ID supported by the config. 256 | pub fn kem_id(&self) -> u16 { 257 | match &self.pub_key { 258 | PubKey::X25519HkdfSha256(_) => ::KEM_ID, 259 | PubKey::DhP256HkdfSha256(_) => ::KEM_ID, 260 | } 261 | } 262 | 263 | fn try_as_header(&self, i: usize) -> Result
{ 264 | let alg = self.algs.try_get(i)?; 265 | 266 | Ok(Header { 267 | cid: self.id, 268 | kem_id: self.kem_id(), 269 | kdf_id: alg.kdf_id, 270 | aead_id: alg.aead_id, 271 | }) 272 | } 273 | 274 | /// Encrypt a request, and return the encrypted data as well as a 275 | /// [context](Ctx), which can be used to decrypt the response 276 | /// later. 277 | pub fn encrypt_req( 278 | &self, 279 | alg_idx: usize, 280 | req: &[u8], 281 | rng: &mut R, 282 | ) -> Result<(BytesMut, Ctx)> { 283 | let hdr = self.try_as_header(alg_idx)?; 284 | match &self.pub_key { 285 | PubKey::X25519HkdfSha256(k) => { 286 | crypt::encrypt_req::(hdr, k, req, rng) 287 | } 288 | PubKey::DhP256HkdfSha256(k) => { 289 | crypt::encrypt_req::(hdr, k, req, rng) 290 | } 291 | } 292 | } 293 | 294 | /// Parse a client side config from a given buffer of bytes. 295 | pub fn parse(buf: &mut B) -> Result { 296 | if buf.remaining() < 1 + 2 { 297 | return Err(Error::InvalidInput); 298 | } 299 | 300 | let id = buf.get_u8(); 301 | let kem_id = buf.get_u16(); 302 | let public_key = match kem_id { 303 | X25519HkdfSha256::KEM_ID => { 304 | let key_len = ::PublicKey::size(); 305 | if buf.remaining() < key_len { 306 | return Err(Error::InvalidInput); 307 | } 308 | PubKey::X25519HkdfSha256(::PublicKey::from_bytes( 309 | &buf.copy_to_bytes(key_len), 310 | )?) 311 | } 312 | DhP256HkdfSha256::KEM_ID => { 313 | let key_len = ::PublicKey::size(); 314 | if buf.remaining() < key_len { 315 | return Err(Error::InvalidInput); 316 | } 317 | PubKey::DhP256HkdfSha256(::PublicKey::from_bytes( 318 | &buf.copy_to_bytes(key_len), 319 | )?) 320 | } 321 | _ => return Err(Error::UnsupportedKem), 322 | }; 323 | if buf.remaining() < 2 { 324 | return Err(Error::InvalidInput); 325 | } 326 | let algs_len = buf.get_u16(); 327 | let (_, rem) = ( 328 | algs_len as usize / SymAlgs::ITEM_SIZE, 329 | algs_len as usize % SymAlgs::ITEM_SIZE, 330 | ); 331 | if rem != 0 { 332 | return Err(Error::InvalidInput); 333 | } 334 | let algs = SymAlgs(buf.copy_to_bytes(algs_len as usize)); 335 | Ok(Self { 336 | id, 337 | pub_key: public_key, 338 | priv_key: None, 339 | algs, 340 | }) 341 | } 342 | 343 | /// Compose a client side config into given buffer. Note that even 344 | /// it is a server side config, the compose method won't write out 345 | /// the private key. 346 | pub fn compose(&self, buf: &mut B) -> Result<()> { 347 | if buf.remaining_mut() < 1 + 2 { 348 | return Err(Error::ShortBuf); 349 | } 350 | 351 | buf.put_u8(self.id); 352 | buf.put_u16(self.kem_id()); 353 | match &self.pub_key { 354 | PubKey::X25519HkdfSha256(k) => compose_to(buf, k.to_bytes().as_slice())?, 355 | PubKey::DhP256HkdfSha256(k) => compose_to(buf, k.to_bytes().as_slice())?, 356 | }; 357 | if buf.remaining_mut() < 2 { 358 | return Err(Error::ShortBuf); 359 | } 360 | buf.put_u16((self.algs.len() * SymAlgs::ITEM_SIZE) as u16); 361 | compose_to(buf, self.algs.0.as_ref())?; 362 | Ok(()) 363 | } 364 | 365 | /// Get a client side config. 366 | pub fn get_client(&self) -> Self { 367 | Self { 368 | id: self.id, 369 | pub_key: self.pub_key.clone(), 370 | priv_key: None, 371 | algs: self.algs.clone(), 372 | } 373 | } 374 | 375 | /// Validate a message header that parsed from the given buffer, 376 | /// check if it matches current config. 377 | pub fn validate_header(&self, buf: &[u8]) -> Result
{ 378 | let hdr = Header::from_slice(buf)?; 379 | if hdr.cid != self.id { 380 | return Err(Error::ConfigIdMismatch); 381 | } 382 | 383 | match &self.pub_key { 384 | PubKey::X25519HkdfSha256(_) if hdr.kem_id == X25519HkdfSha256::KEM_ID => (), 385 | PubKey::DhP256HkdfSha256(_) if hdr.kem_id == DhP256HkdfSha256::KEM_ID => (), 386 | _ => return Err(Error::UnsupportedKem), 387 | } 388 | 389 | let mut found = false; 390 | for i in 0..self.algs.len() { 391 | let alg = self.algs.get(i); 392 | if alg.kdf_id == hdr.kdf_id && alg.aead_id == hdr.aead_id { 393 | found = true; 394 | break; 395 | } 396 | } 397 | if !found { 398 | return Err(Error::InvalidInput); 399 | } 400 | 401 | Ok(hdr) 402 | } 403 | 404 | /// Decrypt a request, and return the plain data as well as a 405 | /// [context](Ctx), which can be used to encrypt the response 406 | /// later. 407 | pub fn decrypt_req(&self, enc_req: &[u8]) -> Result<(BytesMut, Ctx)> { 408 | let buf = BytesMut::from(enc_req); 409 | self.decrypt_req_in_place(buf) 410 | } 411 | 412 | /// Same as [Self::decrypt_req], but does it in place into the provided 413 | /// buffer. 414 | pub fn decrypt_req_in_place(&self, enc_req: B) -> Result<(B, Ctx)> 415 | where 416 | B: InPlaceMut + Deref, 417 | { 418 | let hdr = self.validate_header(&enc_req)?; 419 | match self.priv_key.as_ref() { 420 | Some(PrivKey::X25519HkdfSha256(key)) => { 421 | crypt::decrypt_req_in_place::(hdr, enc_req, key) 422 | } 423 | Some(PrivKey::DhP256HkdfSha256(key)) => { 424 | crypt::decrypt_req_in_place::(hdr, enc_req, key) 425 | } 426 | None => Err(Error::NoPrivateKey), 427 | } 428 | } 429 | } 430 | 431 | /// A builder to build config. 432 | #[derive(Default, Clone)] 433 | pub struct ConfigBuilder { 434 | id: Option, 435 | pub_key: Option, 436 | priv_key: Option, 437 | algs: BytesMut, 438 | } 439 | 440 | impl ConfigBuilder { 441 | /// Provide the config id. 442 | pub fn with_id(mut self, id: u8) -> Self { 443 | self.id = Some(id); 444 | self 445 | } 446 | 447 | /// Generate the keypair from a given ikm. 448 | pub fn gen_keypair_with(mut self, kem: id::KemId, ikm: &[u8]) -> Self { 449 | match kem { 450 | id::KemId::X25519HKDFSHA256 => { 451 | let (sk, pk) = X25519HkdfSha256::derive_keypair(ikm); 452 | self.priv_key = Some(PrivKey::X25519HkdfSha256(sk)); 453 | self.pub_key = Some(PubKey::X25519HkdfSha256(pk)); 454 | } 455 | id::KemId::DHP256HKDFSHA256 => { 456 | let (sk, pk) = DhP256HkdfSha256::derive_keypair(ikm); 457 | self.priv_key = Some(PrivKey::DhP256HkdfSha256(sk)); 458 | self.pub_key = Some(PubKey::DhP256HkdfSha256(pk)); 459 | } 460 | } 461 | self 462 | } 463 | 464 | /// Generate keypair using the provided rng. 465 | pub fn gen_keypair(self, kem: id::KemId, rng: &mut R) -> Self { 466 | let mut ikm = [0u8; IKM_SIZE]; 467 | rng.fill(&mut ikm); 468 | self.gen_keypair_with(kem, &ikm) 469 | } 470 | 471 | /// Push a symmetric algorithm pair into the support list. 472 | pub fn push_alg(mut self, kdf: id::KdfId, aead: id::AeadId) -> Self { 473 | self.algs.put_u16(kdf as u16); 474 | self.algs.put_u16(aead as u16); 475 | self 476 | } 477 | 478 | /// Consume the builder, and generate a config if all the 479 | /// necessary information have been provided. 480 | pub fn build(self) -> Result { 481 | let id = self.id.ok_or(Error::MissingId)?; 482 | let pub_key = self.pub_key.ok_or(Error::MissingPublicKey)?; 483 | let priv_key = self.priv_key; 484 | let algs = if self.algs.is_empty() { 485 | Err(Error::MissingSymAlg) 486 | } else { 487 | Ok(SymAlgs(self.algs.freeze())) 488 | }?; 489 | 490 | Ok(Config { 491 | id, 492 | pub_key, 493 | priv_key, 494 | algs, 495 | }) 496 | } 497 | } 498 | 499 | /// A context used in either client side or server side to carry 500 | /// necessary information for handling the response later. 501 | #[derive(Default)] 502 | pub struct Ctx { 503 | hdr: Header, 504 | encapped_key: Bytes, 505 | secret: Bytes, 506 | } 507 | 508 | impl Ctx { 509 | /// Used by the server side, encrypt a response. 510 | pub fn encrypt_res(&self, res: &[u8], rng: &mut R) -> Result { 511 | crypt::encrypt_res(self.hdr, res, &self.encapped_key, &self.secret, rng) 512 | } 513 | 514 | /// Used by the client side, decrypt a response. 515 | pub fn decrypt_res(&self, enc_res: &[u8]) -> Result { 516 | let buf = BytesMut::from(enc_res); 517 | self.decrypt_res_in_place(buf) 518 | } 519 | 520 | /// Like [`Self::decrypt_res`], decrypt a response in place. 521 | pub fn decrypt_res_in_place(&self, enc_res: B) -> Result 522 | where 523 | B: InPlaceMut, 524 | { 525 | crypt::decrypt_res_in_place::<_>(self.hdr, enc_res, &self.encapped_key, &self.secret) 526 | } 527 | 528 | /// Serialize the context into a given buffer. 529 | pub fn compose(&self, buf: &mut B) -> Result<()> { 530 | self.hdr.compose(buf)?; 531 | compose_to(buf, &mut self.encapped_key.as_ref())?; 532 | compose_to(buf, &mut self.secret.as_ref())?; 533 | Ok(()) 534 | } 535 | 536 | /// Deserialize the context from a given buffer. 537 | pub fn parse(buf: &mut B) -> Result { 538 | let hdr = Header::parse(buf)?; 539 | let size = match hdr.kem_id { 540 | ::KEM_ID => ::EncappedKey::size(), 541 | ::KEM_ID => ::EncappedKey::size(), 542 | _ => return Err(Error::UnsupportedKem), 543 | }; 544 | 545 | if buf.remaining() < size { 546 | return Err(Error::InvalidInput); 547 | } 548 | let encapped_key = buf.copy_to_bytes(size); 549 | let secret = buf.copy_to_bytes(buf.remaining()); 550 | Ok(Self { 551 | hdr, 552 | encapped_key, 553 | secret, 554 | }) 555 | } 556 | } 557 | 558 | #[derive(Debug, Clone)] 559 | struct SymAlgs(Bytes); 560 | 561 | impl SymAlgs { 562 | const ITEM_SIZE: usize = 4; 563 | 564 | fn len(&self) -> usize { 565 | self.0.len() / Self::ITEM_SIZE 566 | } 567 | 568 | // panic when n > self.len() 569 | fn get(&self, n: usize) -> SymAlg { 570 | let mut buf = &self.0[n * Self::ITEM_SIZE..(n + 1) * Self::ITEM_SIZE]; 571 | SymAlg { 572 | kdf_id: buf.get_u16(), 573 | aead_id: buf.get_u16(), 574 | } 575 | } 576 | 577 | fn try_get(&self, n: usize) -> Result { 578 | let end = (n + 1) * Self::ITEM_SIZE; 579 | if end > self.0.len() { 580 | return Err(Error::InvalidInput); 581 | } 582 | let mut buf = &self.0[n * Self::ITEM_SIZE..end]; 583 | Ok(SymAlg { 584 | kdf_id: buf.get_u16(), 585 | aead_id: buf.get_u16(), 586 | }) 587 | } 588 | } 589 | 590 | #[derive(Debug)] 591 | struct SymAlg { 592 | kdf_id: u16, 593 | aead_id: u16, 594 | } 595 | 596 | /// Message header is a low level data representation which contains 597 | /// various identifiers. 598 | #[derive(Debug, Clone, Copy, Default)] 599 | pub struct Header { 600 | /// Config ID 601 | pub cid: u8, 602 | /// KEM ID 603 | pub kem_id: u16, 604 | /// KDF ID 605 | pub kdf_id: u16, 606 | /// AEAD ID 607 | pub aead_id: u16, 608 | } 609 | 610 | impl Header { 611 | /// Wire size in byte of a header. 612 | pub const SIZE: usize = 1 + 2 + 2 + 2; //config id + kem id + kdf id + aead id 613 | 614 | /// Try to parse a header from given slice. 615 | pub fn from_slice(mut buf: &[u8]) -> Result { 616 | if buf.len() < Self::SIZE { 617 | return Err(Error::InvalidInput); 618 | } 619 | 620 | Ok(Self { 621 | cid: buf.get_u8(), 622 | kem_id: buf.get_u16(), 623 | kdf_id: buf.get_u16(), 624 | aead_id: buf.get_u16(), 625 | }) 626 | } 627 | 628 | fn parse(buf: &mut B) -> Result { 629 | if buf.remaining() < Self::SIZE { 630 | return Err(Error::InvalidInput); 631 | } 632 | 633 | Ok(Self { 634 | cid: buf.get_u8(), 635 | kem_id: buf.get_u16(), 636 | kdf_id: buf.get_u16(), 637 | aead_id: buf.get_u16(), 638 | }) 639 | } 640 | 641 | fn compose(&self, buf: &mut B) -> Result<()> { 642 | if buf.remaining_mut() < Self::SIZE { 643 | return Err(Error::ShortBuf); 644 | } 645 | 646 | buf.put_u8(self.cid); 647 | buf.put_u16(self.kem_id); 648 | buf.put_u16(self.kdf_id); 649 | buf.put_u16(self.aead_id); 650 | 651 | Ok(()) 652 | } 653 | } 654 | 655 | fn compose_header(cid: u8, buf: &mut B) -> Result<()> 656 | where 657 | KEM: Kem, 658 | KDF: Kdf, 659 | AEAD: Aead, 660 | B: BufMut, 661 | { 662 | if buf.remaining_mut() < Header::SIZE { 663 | return Err(Error::ShortBuf); 664 | } 665 | 666 | buf.put_u8(cid); 667 | buf.put_u16(::KEM_ID); 668 | buf.put_u16(::KDF_ID); 669 | buf.put_u16(::AEAD_ID); 670 | 671 | Ok(()) 672 | } 673 | 674 | fn compose_info(cid: u8, label: &[u8], buf: &mut B) -> Result<()> 675 | where 676 | KEM: Kem, 677 | KDF: Kdf, 678 | AEAD: Aead, 679 | B: BufMut, 680 | { 681 | if buf.remaining_mut() < label.len() + 1 + Header::SIZE { 682 | return Err(Error::ShortBuf); 683 | } 684 | 685 | buf.put(label); 686 | buf.put_u8(0); 687 | compose_header::(cid, buf) 688 | } 689 | 690 | #[cfg(test)] 691 | mod tests { 692 | use super::*; 693 | use hex_literal::hex; 694 | use hpke::aead::{Aead, AesGcm128, AesGcm256, ChaCha20Poly1305}; 695 | use hpke::kdf::{HkdfSha256, HkdfSha384, HkdfSha512}; 696 | use rand::rngs::StdRng; 697 | use rand::SeedableRng; 698 | use rstest::*; 699 | 700 | #[test] 701 | fn config() { 702 | let example_config = hex!( 703 | " 704 | 01002031 e1f05a74 01021152 20e9af91 705 | 8f738674 aec95f54 db6e04eb 705aae8e 706 | 79815500 08000100 01000100 03 707 | " 708 | ); 709 | 710 | let conf = Config::parse(&mut example_config.as_slice()).unwrap(); 711 | 712 | assert_eq!(1, conf.id); 713 | assert_eq!(X25519HkdfSha256::KEM_ID, conf.kem_id()); 714 | assert_eq!(2, conf.algs.len()); 715 | let alg = conf.algs.get(0); 716 | assert_eq!(HkdfSha256::KDF_ID, alg.kdf_id); 717 | assert_eq!(AesGcm128::AEAD_ID, alg.aead_id); 718 | let alg = conf.algs.get(1); 719 | assert_eq!(HkdfSha256::KDF_ID, alg.kdf_id); 720 | assert_eq!(ChaCha20Poly1305::AEAD_ID, alg.aead_id); 721 | assert!(conf.algs.try_get(2).is_err()); 722 | 723 | let mut buf = BytesMut::new(); 724 | conf.compose(&mut buf).unwrap(); 725 | assert_eq!(example_config.as_slice(), buf.freeze()); 726 | } 727 | 728 | fn enc_dec_with_config(conf: &Config) { 729 | // create RNG with deterministic seed 730 | let mut rng = StdRng::from_seed([0; 32]); 731 | let srv_conf = conf; 732 | let cli_conf = conf.get_client(); 733 | 734 | let req = b""; 735 | let (enc_req, _) = cli_conf.encrypt_req(0, req, &mut rng).unwrap(); 736 | let (dec_req, _) = srv_conf.decrypt_req(&enc_req).unwrap(); 737 | assert_eq!(req, dec_req.as_ref()); 738 | 739 | let req = b"hello"; 740 | let (enc_req, cli_ctx) = cli_conf.encrypt_req(0, req, &mut rng).unwrap(); 741 | let (dec_req, srv_ctx) = srv_conf.decrypt_req(&enc_req).unwrap(); 742 | assert_eq!(req, dec_req.as_ref()); 743 | 744 | let res = b"world"; 745 | let enc_res = srv_ctx.encrypt_res(&res[..], &mut rng).unwrap(); 746 | let dec_res = cli_ctx.decrypt_res(&enc_res).unwrap(); 747 | assert_eq!(&res[..], &dec_res); 748 | } 749 | 750 | #[rstest] 751 | fn crypto_algs( 752 | #[values(X25519HkdfSha256::KEM_ID, DhP256HkdfSha256::KEM_ID)] kem_id: u16, 753 | #[values(HkdfSha256::KDF_ID, HkdfSha384::KDF_ID, HkdfSha512::KDF_ID)] kdf_id: u16, 754 | #[values(AesGcm128::AEAD_ID, AesGcm256::AEAD_ID, ChaCha20Poly1305::AEAD_ID)] aead_id: u16, 755 | ) { 756 | // use static ikm to generate key pair 757 | let ikm = [0u8; 32]; 758 | 759 | let mut algs = BytesMut::new(); 760 | algs.put_u16(kdf_id); 761 | algs.put_u16(aead_id); 762 | let algs = algs.freeze(); 763 | let key_pair = match kem_id { 764 | X25519HkdfSha256::KEM_ID => { 765 | let pair = ::derive_keypair(&ikm); 766 | ( 767 | PrivKey::X25519HkdfSha256(pair.0), 768 | PubKey::X25519HkdfSha256(pair.1), 769 | ) 770 | } 771 | DhP256HkdfSha256::KEM_ID => { 772 | let pair = ::derive_keypair(&ikm); 773 | ( 774 | PrivKey::DhP256HkdfSha256(pair.0), 775 | PubKey::DhP256HkdfSha256(pair.1), 776 | ) 777 | } 778 | _ => unimplemented!(), 779 | }; 780 | let conf = Config { 781 | id: 1, 782 | priv_key: Some(key_pair.0), 783 | pub_key: key_pair.1, 784 | algs: SymAlgs(algs), 785 | }; 786 | enc_dec_with_config(&conf); 787 | } 788 | 789 | #[test] 790 | fn in_place_impl() { 791 | // create RNG with deterministic seed 792 | let mut rng = StdRng::from_seed([0; 32]); 793 | let conf = Config::builder() 794 | .with_id(1) 795 | .gen_keypair(id::KemId::X25519HKDFSHA256, &mut rng) 796 | .push_alg(id::KdfId::HKDFSHA256, id::AeadId::AESGCM128) 797 | .build() 798 | .unwrap(); 799 | 800 | let req = b"hello"; 801 | let (enc_req, cli_ctx) = conf.encrypt_req(0, req, &mut rng).unwrap(); 802 | let impl_bytes = enc_req.clone(); 803 | let (dec_req, _srv_ctx) = conf.decrypt_req_in_place(impl_bytes).unwrap(); 804 | assert_eq!(req, dec_req.as_ref()); 805 | 806 | let mut impl_slice: Vec<_> = enc_req.into(); 807 | let (dec_req, srv_ctx) = conf 808 | .decrypt_req_in_place(impl_slice.as_mut_slice()) 809 | .unwrap(); 810 | assert_eq!(req, dec_req.as_ref()); 811 | 812 | let res = b"world"; 813 | let enc_res = srv_ctx.encrypt_res(res, &mut rng).unwrap(); 814 | let impl_bytes = enc_res.clone(); 815 | let dec_res = cli_ctx.decrypt_res_in_place(impl_bytes).unwrap(); 816 | assert_eq!(res, dec_res.as_ref()); 817 | let mut impl_slice: Vec<_> = enc_res.into(); 818 | let dec_res = cli_ctx 819 | .decrypt_res_in_place(impl_slice.as_mut_slice()) 820 | .unwrap(); 821 | assert_eq!(res, dec_res.as_ref()); 822 | } 823 | } 824 | --------------------------------------------------------------------------------