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
//! # 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/draft-irtf-cfrg-hpke/) hybrid encryption scheme. 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:
//!
//! ```ignore
//! # use rand::{rngs::StdRng, SeedableRng};
//! # use hpke::{
//! #     aead::ChaCha20Poly1305,
//! #     kdf::HkdfSha384,
//! #     kem::X25519HkdfSha256,
//! #     EncappedKey, 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. msg gets encrypted in place, and aad is authenticated
//! // associated data that is not encrypted.
//! let mut msg = *b"fronthand or backhand?";
//! let aad = b"a gentleman's game";
//! let auth_tag = encryption_context
//!     .seal(&mut msg, aad)
//!     .expect("encryption failed!");
//! // The msg was encrypted in-place. So rename it for clarity
//! let ciphertext = msg;
//! # let mut ciphertext = ciphertext; // Make it mutable so Bob can decrypt in place
//!
//! // ~~~
//! // 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!");
//! decryption_context.open(&mut ciphertext, aad, &auth_tag).expect("invalid ciphertext!");
//! // The ciphertext was decrypted in-place. So rename it for clarity
//! let plaintext = ciphertext;
//!
//! assert_eq!(&plaintext, b"fronthand or backhand?");
//! ```

//-------- no_std stuff --------//
#![no_std]

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

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

//-------- 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"))]
mod kat_tests;

// kat_tests requires serde
#[cfg(all(test, feature = "std", feature = "x25519", feature = "p256"))]
#[macro_use]
extern crate serde_derive;

#[cfg(test)]
mod test_util;

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

// Re-export this version of generic_array, since that's what's used everywhere in this crate
pub use generic_array;

#[macro_use]
mod util;

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

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

#[doc(inline)]
pub use crate::aead::{AeadCtxR, AeadCtxS};
#[doc(inline)]
pub use kem::{EncappedKey, Kem};
#[doc(inline)]
pub use kex::{Deserializable, Serializable};
#[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, single_shot_seal};

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

/// Describes things that can go wrong when trying to seal or open a ciphertext
#[derive(Clone, Copy, Debug)]
pub enum HpkeError {
    /// The nonce sequence counter has overflowed
    SeqOverflow,
    /// The authentication tag was invalid when opening
    InvalidTag,
    /// An unspecified error occured during encryption
    Encryption,
    /// A key exchange input or output was invalid
    InvalidKeyExchange,
    /// The KDF was asked to output too many bytes
    InvalidKdfLength,
    /// The deserializer was given a bad encoding
    InvalidEncoding,
}

impl core::fmt::Display for HpkeError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let kind = match self {
            HpkeError::SeqOverflow => "Sequence overflow",
            HpkeError::InvalidTag => "Invalid tag",
            HpkeError::Encryption => "Encryption error",
            HpkeError::InvalidKeyExchange => "Key exchange validation error",
            HpkeError::InvalidKdfLength => "Too many bytes requested from KDF",
            HpkeError::InvalidEncoding => "Cannot deserialize byte sequence: invalid encoding",
        };
        f.write_str(kind)
    }
}

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