lib-q-hpke 0.0.5

HPKE implementation for lib-q
Documentation

lib-q-hpke

RFC 9180–aligned Hybrid Public Key Encryption using post-quantum-only primitives (ML-KEM family for the HPKE KEM role in the default provider path; no classical KEM or classical signatures in that path).

Overview

lib-q-hpke implements HPKE for the lib-q stack: protocol logic and types in this crate, with KEM/AEAD/hash work delegated through lib-q-kem, lib-q-aead, and lib-q-hash. The high-level HpkeContext holds a lib_q_core::CryptoProvider for KemContext and an Arc<dyn HpkeCryptoProvider + Send + Sync> (default PostQuantumProvider) for encapsulation, KDF, AEAD, and exporter operations. Use HpkeContext::with_hpke_crypto to swap the HPKE backend; with_provider only replaces the inner KemContext crypto. Today the default provider wires ML-KEM only for HPKE KEM (other PQ KEMs may exist in the workspace but are not in the HpkeKem catalog yet).

Interoperability

Profiles, a mode×suite matrix, and fixture provenance are documented under the workspace interoperability.md and hpke-architecture.md. For code, see lib_q_hpke::interop. Run the integrator-oriented example (requires std):

cargo run -p lib-q-hpke --example hpke_interop_negotiation --features std

Supported Algorithms

Key Encapsulation Mechanisms (KEM)

  • ML-KEM-512 (Level 1 security)
  • ML-KEM-768 (Level 3 security)
  • ML-KEM-1024 (Level 5 security)

Key Derivation Functions (KDF)

  • HKDF-SHAKE128
  • HKDF-SHAKE256
  • HKDF-SHA3-256
  • HKDF-SHA3-512

Authenticated Encryption (AEAD)

  • Saturnin-256 (32-byte key, 16-byte nonce, 32-byte tag)
  • SHAKE256-based AEAD (16-byte tag)
  • Keccak duplex-sponge AEAD via lib-q-aead — enable Cargo feature duplex-sponge-aead on this crate (when using the umbrella lib-q crate, enable hpke-duplex-aead)
  • Export-only mode (HpkeAead::Export) for exporter-secret usage without message encryption

Quick Start

use lib_q_core::{Algorithm, KemContext, KemPublicKey, KemSecretKey};
use lib_q_hpke::HpkeContext;
use libq::LibQCryptoProvider;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create HPKE context (umbrella `lib-q` crate, Rust name `libq`)
    let provider = Box::new(LibQCryptoProvider::new()?);
    let mut hpke_ctx = HpkeContext::with_provider(provider);
    
    // Generate key pair
    let mut kem_ctx = KemContext::with_provider(Box::new(LibQCryptoProvider::new()?));
    let keypair = kem_ctx.generate_keypair(Algorithm::MlKem512)?;
    let recipient_pk = KemPublicKey::new(keypair.public_key().as_bytes().to_vec());
    let recipient_sk = KemSecretKey::new(keypair.secret_key().as_bytes().to_vec());
    
    // Encrypt message
    let message = b"Hello, HPKE!";
    let (encapsulated_key, ciphertext) = hpke_ctx.seal(
        &recipient_pk,
        b"application-info",
        b"additional-data",
        message,
    )?;
    
    // Decrypt message
    let decrypted = hpke_ctx.open(
        &encapsulated_key,
        &recipient_sk,
        b"application-info",
        b"additional-data",
        &ciphertext,
    )?;
    
    assert_eq!(decrypted, message);
    Ok(())
}

HPKE Modes

Base Mode

Standard HPKE without additional authentication.

let mut sender_ctx = hpke_ctx.setup_sender(&recipient_pk, b"session-info")?;
let ciphertext = sender_ctx.seal(b"aad", message)?;

PSK Mode

Pre-shared key authentication.

let psk = b"shared-secret-key";
let psk_id = b"psk-identifier";
let mut sender_ctx = hpke_ctx.setup_sender_psk(
    &recipient_pk,
    b"session-info",
    psk,
    psk_id,
)?;

Auth Mode

Sender authentication using asymmetric keys.

let mut sender_ctx = hpke_ctx.setup_sender_auth(
    &recipient_pk,
    b"session-info",
    &sender_sk,
    &sender_pk,
)?;

AuthPSK Mode

Combined PSK and sender authentication.

let mut sender_ctx = hpke_ctx.setup_sender_auth_psk(
    &recipient_pk,
    b"session-info",
    psk,
    psk_id,
    &sender_sk,
    &sender_pk,
)?;

PSK / AuthPSK wire format

For PSK and AuthPSK modes, the default is HpkePskWireFormat::Rfc9180 (RFC 9180 on-the-wire layout). Both peers may set HpkePskWireFormat::LibQCommitmentSuffix with HpkeContext::set_psk_wire_format to reject wrong (psk, psk_id) or a mismatched primary KEM ciphertext before decapsulation; that suffix is not interoperable with strict third-party RFC 9180 implementations.

Cargo features (summary)

Feature Purpose
std Standard library support
alloc Required for normal operation (enforced by the crate)
ml-kem ML-KEM through lib-q-kem (default)
hash HKDF hash backends via lib-q-hash (default)
saturnin / shake256 AEAD algorithms (defaults)
duplex-sponge-aead Duplex-sponge AEAD in HpkeAead::DuplexSpongeAead
wasm wasm32 bindings and helpers
secure-rng OS-backed RNG where applicable (default)

See Cargo.toml for the full feature matrix and optional dev dependencies.

Documentation

Testing

The implementation includes comprehensive test coverage:

# Run all tests
cargo test

# Run specific test suites
cargo test --test psk_mode_comprehensive_tests
cargo test --test authpsk_mode_comprehensive_tests
cargo test --test security_validation_comprehensive_tests

Wire sizes (KEM)

Public key and encapsulated ciphertext lengths match HpkeKem (public_key_len / enc_len):

KEM NIST category (approx.) Public key Encapsulated ciphertext
ML-KEM-512 1 800 B 768 B
ML-KEM-768 3 1184 B 1088 B
ML-KEM-1024 5 1568 B 1568 B

AEAD ciphertext expansion is plaintext length plus the AEAD tag (e.g. 32 bytes for Saturnin-256); see HpkeAead::tag_len in src/types.rs.

Dependencies

  • lib-q-core - Core cryptographic types and interfaces
  • lib-q-kem - Key encapsulation mechanism implementations
  • lib-q-hash - Hash function implementations
  • lib-q-aead - Authenticated encryption implementations
  • lib-q (Rust import libq) — LibQCryptoProvider for demos; production integrations may use narrower providers

License

This project is licensed under the same terms as the lib-q project.