Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:

# Check using Clippy with the feature combinations listed in the README.
- run: cargo clippy
- run: cargo clippy --no-default-features --features algo-all,base-mode-open,serde
- run: cargo clippy --no-default-features --features aes,hkdfsha2,nistp,serde

- name: Format
run: cargo fmt --all -- --check
Expand Down
41 changes: 12 additions & 29 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,23 @@ keywords = ["hpke", "encryption"]
categories = ["cryptography"]

[features]
base-mode-open = []
base-mode-seal = []
serde = ["serde_crate"]
algo-all = ["aead-all", "kdf-all", "kem-all"]
default = ["algo-all", "base-mode-seal", "base-mode-open"]
aead-all = ["aead-aes-gcm-128", "aead-aes-gcm-256", "aead-chacha-20-poly-1305"]
aead-aes-gcm-128 = []
aead-aes-gcm-256 = []
aead-chacha-20-poly-1305 = []
kdf-all = ["kdf-sha256", "kdf-sha384", "kdf-sha512"]
kdf-sha256 = []
kdf-sha384 = []
kdf-sha512 = []
kem-all = ["kem-dh-p256-hkdf-sha256", "kem-dh-p384-hkdf-sha384", "kem-dh-p521-hkdf-sha512", "kem-x25519-hkdf-sha256"]
kem-dh-p256-hkdf-sha256 = ["hpke/p256"]
kem-dh-p384-hkdf-sha384 = ["hpke/p384"]
kem-dh-p521-hkdf-sha512 = ["hpke/p521"]
kem-x25519-hkdf-sha256 = ["hpke/x25519"]
default = ["algo-all"]
aead-all = ["aes", "chacha"]
aes = ["hpke/aes"]
chacha = ["hpke/chacha"]
kdf-all = ["hkdfsha2", "shake"]
hkdfsha2 = ["hpke/hkdfsha2"]
shake = ["hpke/shake"]
kem-all = ["x25519", "nistp", "mlkem"]
x25519 = ["hpke/x25519"]
nistp = ["hpke/nistp"]
mlkem = ["hpke/mlkem"]

[dependencies]
rand = "0.9.2"
num_enum = "0.7.0"
cfg-if = "1.0.0"
hpke = { version = "0.13.0", default-features = false, features = ["std"] }
Comment thread
rozbb marked this conversation as resolved.
hpke = { version = "0.14", default-features = false, features = ["getrandom", "alloc"] }
zeroize = "1.6"

[dependencies.serde_crate]
Expand All @@ -46,13 +39,3 @@ package = "serde"
hex = { version = "0.4.3", features = ["serde"] }
serde_crate = { package = "serde", version = "1.0.185", features = ["derive"] }
serde_json = "1.0.105"

[package.metadata.cargo-all-features]
# These features are redundant, as they only enable other features.
denylist = ["algo-all", "aead-all", "kdf-all", "kem-all"]
# We don't use the implicit "serde_crate" feature, only our explicitly defined
# "serde" feature.
skip_optional_dependencies = true
# Allow for one of base-mode-open/base-mode-seal, one KEM, one KDF, one AEAD,
# plus serde or another feature
max_combination_size = 5
39 changes: 18 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,37 @@ time, you should use the [`hpke`][hpke] crate directly.

Currently, this crate only exposes interfaces for the Base mode (0)
described in the hpke draft, and within base mode, only stateless
single-shot message encryption/decryption, as defined in [RFC9180§6][section-6]
single-shot message encryption/decryption, as defined in [RFC9180§6][section-6].

The algorithms supported in this crate and their codepoints are as described in the [HPKE
specification][hpke-datatracker] and [draft-ietf-hpke-pq][hpke-pq].

[hpke]: https://github.com/rozbb/rust-hpke
[section-6]: https://www.rfc-editor.org/rfc/rfc9180.html#section-6
[hpke-datatracker]: https://datatracker.ietf.org/doc/draft-ietf-hpke-hpke/
[hpke-pq]: https://datatracker.ietf.org/doc/draft-ietf-hpke-pq/

## Available cargo features:

* *base-mode-open*: Enables hpke base-mode one-shot open behavior
(receiver functionality). Enabled by default.
`hpke-dispatch`'s cargo features are designed to align directly with those of [`hpke`][hpke]. See
that crate's documentation for discussion of how to selectively enable algorithms.

* *base-mode-seal*: Enables hpke base-mode one-shot seal behavior
(sender functionality). Enabled by default.
`hpke-dispatch` defines the following additional features:

* *algo-all*: enables all aead, kdf, and kem algorithms. enabled by
default.
* *algo-all*: enables all aead, kdf, and kem algorithms. Enabled by default.

* *aead-all*: Enables `aead-aes-gcm-128`, `aead-aes-gcm-256`, and
`aead-chacha-20-poly-1305` algorithm features. Enabled by default.
* *aead-all*: Enables `aes` and `chacha` algorithm features. Enabled by default.

* *kdf-all*: Enables `kdf-sha256`, `kdf-sha384`, `kdf-sha512`
algorithm features. Enabled by default.
* *kdf-all*: Enables `hkdfsha2` and `shake` algorithm features. Enabled by default.

* *kem-all*: Enables `kem-dh-p256-hkdf-sha256`,
`kem-dh-p384-hkdf-sha384`, `kem-dh-p521-hkdf-sha512`, and
`kem-x25519-hkdf-sha256` algorithm features. Enabled by default.
* *kem-all*: Enables `x25519`, `nistp` and `mlkem` algorithm features. Enabled by default.

* *serde*: enables derived serde serialization and deserialization for
all public structs and enums. Disabled by default.
* *serde*: enables derived serde serialization and deserialization for all public structs and enums.
Disabled by default.

## Example feature usage:

To depend on this crate with all algorithms,
`base-mode-open`, and `base-mode-seal`, use default features.
To depend on this crate with all algorithms, use default features.

To depend on this crate with all algorithms and serde
enabled, but without `base-mode-seal`: `default-features = false,
features = ["algo-all", "base-mode-open", "serde"]`
To depend on this crate with only AES, NIST curves, SHA-2 and Serde enabled, use
`default-features = false, features = ["aes", "hkdfsha2", "nistp", "serde"]`.
18 changes: 9 additions & 9 deletions src/aead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ encryption function, as per [RFC9180§7.3](https://www.rfc-editor.org/rfc/rfc918
)]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub enum Aead {
#[cfg(feature = "aead-aes-gcm-128")]
/// AES-128-GCM [GCM](https://doi.org/10.6028/nist.sp.800-38d)
#[cfg(feature = "aes")]
AesGcm128 = 1,
#[cfg(feature = "aead-aes-gcm-256")]
/// AES-256-GCM [GCM](https://doi.org/10.6028/nist.sp.800-38d)
#[cfg(feature = "aes")]
AesGcm256 = 2,
#[cfg(feature = "aead-chacha-20-poly-1305")]
/// ChaCha20Poly1305 [RFC8439](https://www.rfc-editor.org/info/rfc8439)
#[cfg(feature = "chacha")]
ChaCha20Poly1305 = 3,
}

Expand All @@ -31,11 +31,11 @@ impl FromStr for Aead {

fn from_str(s: &str) -> Result<Self, Self::Err> {
match &*s.to_lowercase().replace('-', "") {
#[cfg(feature = "aead-aes-gcm-128")]
#[cfg(feature = "aes")]
"aesgcm128" | "aes128gcm" => Ok(Self::AesGcm128),
#[cfg(feature = "aead-aes-gcm-256")]
#[cfg(feature = "aes")]
"aesgcm256" | "aes256gcm" => Ok(Self::AesGcm256),
#[cfg(feature = "aead-chacha-20-poly-1305")]
#[cfg(feature = "chacha")]
"chacha20poly1305" => Ok(Self::ChaCha20Poly1305),
_ => Err(IdLookupError("aead not recognized")),
}
Expand All @@ -44,10 +44,10 @@ impl FromStr for Aead {

/// An iterable slice of [`Aead`] variants
pub const AEAD_ALL: &[Aead] = &[
#[cfg(feature = "aead-aes-gcm-128")]
#[cfg(feature = "aes")]
Aead::AesGcm128,
#[cfg(feature = "aead-aes-gcm-256")]
#[cfg(feature = "aes")]
Aead::AesGcm256,
#[cfg(feature = "aead-chacha-20-poly-1305")]
#[cfg(feature = "chacha")]
Aead::ChaCha20Poly1305,
];
3 changes: 0 additions & 3 deletions src/base_mode_open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ that does not require compile time selection of an algorithm. Instead,
the selected algorithm is provided through the [`Config`] passed as
the first argument.

Requires the `base-mode-open` crate feature to be enabled.

# Errors

This will return an `Result::Err` variant if:
Expand All @@ -17,7 +15,6 @@ This will return an `Result::Err` variant if:
* there is an error in key decapsulation
* there is an error in decryption
*/
#[cfg(feature = "base-mode-open")]
pub fn base_mode_open(
config: &Config,
private_key: &[u8],
Expand Down
6 changes: 1 addition & 5 deletions src/base_mode_seal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ that does not require compile time selection of an
algorithm. Instead, the selected algorithm is provided through the
[`Config`] passed as the first argument.

Requires the `base-mode-seal` crate feature to be enabled.

# Errors

This will return an `Result::Err` variant if:
Expand All @@ -18,7 +16,6 @@ This will return an `Result::Err` variant if:
* there is an error in encryption

*/
#[cfg(feature = "base-mode-seal")]
pub fn base_mode_seal(
config: &Config,
recipient_public_key: &[u8],
Expand All @@ -42,13 +39,12 @@ where
KdfT: hpke::kdf::Kdf,
KemT: hpke::kem::Kem,
{
let (encapped_key, ciphertext) = hpke::single_shot_seal::<AeadT, KdfT, KemT, _>(
let (encapped_key, ciphertext) = hpke::single_shot_seal::<AeadT, KdfT, KemT>(
&hpke::OpModeS::Base,
&from_bytes(recipient_public_key)?,
info,
plaintext,
aad,
&mut rand::rng(),
)?;

Ok(EncappedKeyAndCiphertext {
Expand Down
15 changes: 3 additions & 12 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#[cfg(feature = "base-mode-open")]
use crate::base_mode_open;
#[cfg(feature = "base-mode-seal")]
use crate::{base_mode_seal, EncappedKeyAndCiphertext};
use crate::{Aead, IdLookupError, Kdf, Kem};
#[cfg(any(feature = "base-mode-seal", feature = "base-mode-open"))]
use crate::{
base_mode_open, base_mode_seal, Aead, EncappedKeyAndCiphertext, IdLookupError, Kdf, Kem,
};
use hpke::HpkeError;

/**
Expand Down Expand Up @@ -33,8 +30,6 @@ impl Config {
selected algorithm is provided through the [`Config`] that this
method is called on.

Requires the `base-mode-seal` crate feature to be enabled.

# Errors

This will return an `Result::Err` variant if:
Expand All @@ -44,7 +39,6 @@ impl Config {
* there is an error in encryption

*/
#[cfg(feature = "base-mode-seal")]
pub fn base_mode_seal(
&self,
recipient_public_key: &[u8],
Expand All @@ -61,8 +55,6 @@ impl Config {
algorithm. Instead, the selected algorithm is provided through the
[`Config`] that this method is called on.

Requires the `base-mode-open` crate feature to be enabled.

# Errors

This will return an `Result::Err` variant if:
Expand All @@ -72,7 +64,6 @@ impl Config {
* there is an error in decryption

*/
#[cfg(feature = "base-mode-open")]
pub fn base_mode_open(
&self,
private_key: &[u8],
Expand Down
62 changes: 46 additions & 16 deletions src/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ use crate::IdLookupError;
use num_enum::TryFromPrimitive;
use std::str::FromStr;

/**
Kdf represents an key derivation function, as per
[RFC9180§7.2](https://www.rfc-editor.org/rfc/rfc9180.html#section-7.2)
*/
/// A key derivation function used in HPKE.
///
/// Codepoints are defined in [RFC9180§7.2][1] and [draft-ietf-hpke-pq][2].
///
/// [1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.2
/// [2]: https://www.ietf.org/archive/id/draft-ietf-hpke-pq-05.html#table-1
#[non_exhaustive]
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive)]
Expand All @@ -15,39 +17,67 @@ Kdf represents an key derivation function, as per
)]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub enum Kdf {
#[cfg(feature = "kdf-sha256")]
/// Sha256 [RFC5869](https://www.rfc-editor.org/info/rfc5869)
Sha256 = 1,
#[cfg(feature = "kdf-sha384")]
#[cfg(feature = "hkdfsha2")]
Sha256 = 0x0001,
/// Sha384 [RFC5869](https://www.rfc-editor.org/info/rfc5869)
Sha384 = 2,
#[cfg(feature = "kdf-sha512")]
#[cfg(feature = "hkdfsha2")]
Sha384 = 0x0002,
/// Sha512 [RFC5869](https://www.rfc-editor.org/info/rfc5869)
Sha512 = 3,
#[cfg(feature = "hkdfsha2")]
Sha512 = 0x0003,
/// SHAKE128 [FIPS 202](https://csrc.nist.gov/pubs/fips/202/final)
#[cfg(feature = "shake")]
Shake128 = 0x0010,
/// SHAKE256 [FIPS 202](https://csrc.nist.gov/pubs/fips/202/final)
#[cfg(feature = "shake")]
Shake256 = 0x0011,
/// TurboSHAKE128 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/)
#[cfg(feature = "shake")]
TurboShake128 = 0x0012,
/// TurboSHAKE256 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/)
#[cfg(feature = "shake")]
TurboShake256 = 0x0013,
}

impl FromStr for Kdf {
type Err = IdLookupError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match &*s.to_lowercase().replace('-', "") {
#[cfg(feature = "kdf-sha256")]
#[cfg(feature = "hkdfsha2")]
"hkdfsha256" | "sha256" => Ok(Self::Sha256),
#[cfg(feature = "kdf-sha384")]
#[cfg(feature = "hkdfsha2")]
"hkdfsha384" | "sha384" => Ok(Self::Sha384),
#[cfg(feature = "kdf-sha512")]
#[cfg(feature = "hkdfsha2")]
"hkdfsha512" | "sha512" => Ok(Self::Sha512),
#[cfg(feature = "shake")]
"shake128" => Ok(Self::Shake128),
#[cfg(feature = "shake")]
"shake256" => Ok(Self::Shake256),
#[cfg(feature = "shake")]
"turboshake128" => Ok(Self::TurboShake128),
#[cfg(feature = "shake")]
"turboshake256" => Ok(Self::TurboShake256),
_ => Err(IdLookupError("kdf not recognized")),
}
}
}

/// An iterable slice of [`Kdf`] variants
pub const KDF_ALL: &[Kdf] = &[
#[cfg(feature = "kdf-sha256")]
#[cfg(feature = "hkdfsha2")]
Kdf::Sha256,
#[cfg(feature = "kdf-sha384")]
#[cfg(feature = "hkdfsha2")]
Kdf::Sha384,
#[cfg(feature = "kdf-sha512")]
#[cfg(feature = "hkdfsha2")]
Kdf::Sha512,
#[cfg(feature = "shake")]
Kdf::Shake128,
#[cfg(feature = "shake")]
Kdf::Shake256,
#[cfg(feature = "shake")]
Kdf::TurboShake128,
#[cfg(feature = "shake")]
Kdf::TurboShake256,
];
Loading
Loading