1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! # hpke
//! **WARNING:** This code has not been audited. Use at your own discretion.
//!
//! This is a pure Rust implementation of the
//! [HPKE](https://datatracker.ietf.org/doc/rfc9180/) hybrid encryption scheme (RFC 9180). The
//! purpose of hybrid encryption is to use allow someone to send secure messages to an entity whose
//! public key they know. Here's an example of Alice and Bob, where Alice knows Bob's public key:
//!
//! ```
//! # #[cfg(any(feature = "alloc", feature = "std"))] {
//! # #[cfg(feature = "x25519")]
//! # {
//! # use rand::{rngs::StdRng, SeedableRng};
//! # use hpke::{
//! #     aead::ChaCha20Poly1305,
//! #     kdf::HkdfSha384,
//! #     kem::X25519HkdfSha256,
//! #     Kem as KemTrait, OpModeR, OpModeS, setup_receiver, setup_sender,
//! # };
//! // These types define the ciphersuite Alice and Bob will be using
//! type Kem = X25519HkdfSha256;
//! type Aead = ChaCha20Poly1305;
//! type Kdf = HkdfSha384;
//!
//! let mut csprng = StdRng::from_entropy();
//! # let (bob_sk, bob_pk) = Kem::gen_keypair(&mut csprng);
//!
//! // This is a description string for the session. Both Alice and Bob need to know this value.
//! // It's not secret.
//! let info_str = b"Alice and Bob's weekly chat";
//!
//! // Alice initiates a session with Bob. OpModeS::Base means that Alice is not authenticating
//! // herself at all. If she had a public key herself, or a pre-shared secret that Bob also
//! // knew, she'd be able to authenticate herself. See the OpModeS and OpModeR types for more
//! // detail.
//! let (encapsulated_key, mut encryption_context) =
//!     hpke::setup_sender::<Aead, Kdf, Kem, _>(&OpModeS::Base, &bob_pk, info_str, &mut csprng)
//!         .expect("invalid server pubkey!");
//!
//! // Alice encrypts a message to Bob. `aad` is authenticated associated data that is not
//! // encrypted.
//! let msg = b"fronthand or backhand?";
//! let aad = b"a gentleman's game";
//! // To seal without allocating:
//! //     let auth_tag = encryption_context.seal_in_place_detached(&mut msg, aad)?;
//! // To seal with allocating:
//! let ciphertext = encryption_context.seal(msg, aad).expect("encryption failed!");
//!
//! // ~~~
//! // Alice sends the encapsulated key, message ciphertext, AAD, and auth tag to Bob over the
//! // internet. Alice doesn't care if it's an insecure connection, because only Bob can read
//! // her ciphertext.
//! // ~~~
//!
//! // Somewhere far away, Bob receives the data and makes a decryption session
//! let mut decryption_context =
//!     hpke::setup_receiver::<Aead, Kdf, Kem>(
//!         &OpModeR::Base,
//!         &bob_sk,
//!         &encapsulated_key,
//!         info_str,
//!     ).expect("failed to set up receiver!");
//! // To open without allocating:
//! //     decryption_context.open_in_place_detached(&mut ciphertext, aad, &auth_tag)
//! // To open with allocating:
//! let plaintext = decryption_context.open(&ciphertext, aad).expect("invalid ciphertext!");
//!
//! assert_eq!(&plaintext, b"fronthand or backhand?");
//! # }
//! # }
//! ```

// The doc_cfg feature is only available in nightly. It lets us mark items in documentation as
// dependent on specific features.
#![cfg_attr(docsrs, feature(doc_cfg))]
//-------- no_std stuff --------//
#![no_std]

#[cfg(feature = "std")]
#[allow(unused_imports)]
#[macro_use]
extern crate std;

#[cfg(feature = "std")]
pub(crate) use std::vec::Vec;

#[cfg(all(feature = "alloc", not(feature = "std")))]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;

#[cfg(all(feature = "alloc", not(feature = "std")))]
pub(crate) use alloc::vec::Vec;

//-------- Testing stuff --------//

// kat_tests tests all the implemented ciphersuites, and thus needs all the dependencies. It also
// needs std for file IO.
#[cfg(all(
    test,
    feature = "std",
    feature = "x25519",
    feature = "p256",
    feature = "p384"
))]
mod kat_tests;

#[cfg(test)]
mod test_util;

//-------- Modules and exports--------//

// Re-export our versions of generic_array and rand_core, since their traits and types are exposed
// in this crate
pub use generic_array;
pub use rand_core;

#[macro_use]
mod util;

pub mod aead;
mod dhkex;
pub mod kdf;
pub mod kem;
mod op_mode;
mod setup;
mod single_shot;

#[cfg(feature = "serde_impls")]
mod serde_impls;

#[doc(inline)]
pub use kem::Kem;
#[doc(inline)]
pub use op_mode::{OpModeR, OpModeS, PskBundle};
#[doc(inline)]
pub use setup::{setup_receiver, setup_sender};
#[doc(inline)]
pub use single_shot::{single_shot_open_in_place_detached, single_shot_seal_in_place_detached};

#[doc(inline)]
#[cfg(any(feature = "alloc", feature = "std"))]
pub use single_shot::{single_shot_open, single_shot_seal};

//-------- Top-level types --------//

use generic_array::{typenum::marker_traits::Unsigned, ArrayLength, GenericArray};

/// Describes things that can go wrong in the HPKE protocol
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HpkeError {
    /// The allowed number of message encryptions has been reached
    MessageLimitReached,
    /// An error occurred while opening a ciphertext
    OpenError,
    /// An error occured while sealing a plaintext
    SealError,
    /// The KDF was asked to output too many bytes
    KdfOutputTooLong,
    /// An invalid input value was encountered
    ValidationError,
    /// Encapsulation failed
    EncapError,
    /// Decapsulation failed
    DecapError,
    /// An input isn't the right length. First value is the expected length, second is the given
    /// length.
    IncorrectInputLength(usize, usize),
}

impl core::fmt::Display for HpkeError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            HpkeError::MessageLimitReached => write!(f, "Message limit reached"),
            HpkeError::OpenError => write!(f, "Failed to open ciphertext"),
            HpkeError::SealError => write!(f, "Failed to seal plaintext"),
            HpkeError::KdfOutputTooLong => write!(f, "Too many bytes requested from KDF"),
            HpkeError::ValidationError => write!(f, "Input value is invalid"),
            HpkeError::EncapError => write!(f, "Encapsulation failed"),
            HpkeError::DecapError => write!(f, "Decapsulation failed"),
            HpkeError::IncorrectInputLength(expected, given) => write!(
                f,
                "Incorrect input length. Expected {} bytes. Got {}.",
                expected, given
            ),
        }
    }
}

/// Implemented by types that have a fixed-length byte representation
pub trait Serializable {
    type OutputSize: ArrayLength<u8>;

    fn to_bytes(&self) -> GenericArray<u8, Self::OutputSize>;

    /// Returns the size (in bytes) of this type when serialized
    fn size() -> usize {
        Self::OutputSize::to_usize()
    }
}

/// Implemented by types that can be deserialized from byte representation
pub trait Deserializable: Serializable + Sized {
    fn from_bytes(encoded: &[u8]) -> Result<Self, HpkeError>;
}

// An Error type is just something that's Debug and Display
#[cfg(feature = "std")]
impl std::error::Error for HpkeError {}