Metadata-Version: 2.4
Name: rfc9180
Version: 0.2.0
Summary: A Python implementation of Hybrid Public Key Encryption (HPKE) as specified in RFC 9180
Project-URL: Homepage, https://github.com/MFDI-hub/rfc9180-py
Project-URL: Documentation, https://github.com/MFDI-hub/rfc9180-py#readme
Project-URL: Repository, https://github.com/MFDI-hub/rfc9180-py
Project-URL: Issues, https://github.com/MFDI-hub/rfc9180-py/issues
Author: rfc9180-py Contributors
License: MIT License
        
        Copyright (c) 2024 rfc9180-py Contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
License-File: LICENSE
Keywords: cryptography,encryption,hpke,hybrid,public-key,rfc9180
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.9
Requires-Dist: cryptography>=41.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# rfc9180-py

A Python implementation of **Hybrid Public Key Encryption (HPKE)** as specified in [RFC 9180](https://www.rfc-editor.org/rfc/rfc9180.txt).

## Overview

HPKE provides a variant of public key encryption for arbitrary-sized plaintexts. It combines an asymmetric Key Encapsulation Mechanism (KEM), a Key Derivation Function (KDF), and an Authenticated Encryption with Associated Data (AEAD) algorithm to provide secure encryption with optional authentication.

This library implements the core HPKE specification using Python's `cryptography` library, providing both single-shot and multi-shot APIs for all HPKE modes.

## Features

- **All HPKE Modes**: Base, PSK, Auth, and AuthPSK modes
- **Multiple Ciphersuites**: Support for various KEM, KDF, and AEAD combinations
- **Single-Shot APIs**: Convenient one-call encryption/decryption
- **Context APIs**: Multi-message encryption contexts with automatic nonce management
- **Secret Export**: Export application secrets from rfc9180 contexts
- **RFC 9180 Compliant**: Follows the specification closely with proper domain separation and validation
- **Key Management**: Key generation, derivation, and serialization utilities
- **Message Encoding Helpers**: Self-describing message format with header encoding/parsing

## Installation

```bash
pip install rfc9180-py
```

The library requires Python 3.9+ and uses the `cryptography` library for all cryptographic primitives.

## Quick Start

```python
from rfc9180 import HPKE, KEMID, KDFID, AEADID, create_hpke

# Create HPKE instance with default ciphersuite (X25519 + HKDF-SHA256 + AES-128-GCM)
hpke = HPKE(KEMID.DHKEM_X25519_HKDF_SHA256, KDFID.HKDF_SHA256, AEADID.AES_128_GCM)

# Or use the convenience function
hpke = create_hpke()  # Uses defaults

# Generate recipient key pair
skR, pkR = hpke.generate_key_pair()

# Single-shot encryption (Base mode)
info = b"application-info"
aad = b"additional-data"
plaintext = b"secret message"
enc, ciphertext = hpke.seal_base(pkR, info, aad, plaintext)

# Single-shot decryption
decrypted = hpke.open_base(enc, skR, info, aad, ciphertext)
assert decrypted == plaintext

# Multi-shot encryption
enc, ctx_sender = hpke.setup.setup_base_sender(pkR, info)
ct1 = ctx_sender.seal(aad, b"message 1")
ct2 = ctx_sender.seal(aad, b"message 2")

# Multi-shot decryption
ctx_recipient = hpke.setup.setup_base_recipient(enc, skR, info)
pt1 = ctx_recipient.open(aad, ct1)
pt2 = ctx_recipient.open(aad, ct2)

# Export secrets
secret = ctx_sender.export(b"exporter-context", 32)
```

## Implementation Status

### ✅ Implemented (RFC 9180 Compliance)

#### Section 4: Cryptographic Dependencies
- ✅ **DHKEM (DH-Based KEM)**: Full implementation with ExtractAndExpand
- ✅ **LabeledExtract**: RFC 9180 §4 with proper "HPKE-v1" domain separation
- ✅ **LabeledExpand**: RFC 9180 §4 with length prefixing

#### Section 5: Hybrid Public Key Encryption
- ✅ **SetupBaseS/R**: Base mode setup (Section 5.1.1)
- ✅ **SetupPSKS/R**: PSK mode setup (Section 5.1.2)
- ✅ **SetupAuthS/R**: Auth mode setup (Section 5.1.3)
- ✅ **SetupAuthPSKS/R**: AuthPSK mode setup (Section 5.1.4)
- ✅ **Key Schedule**: Full implementation with proper PSK validation
- ✅ **Context Seal/Open**: Multi-message encryption with sequence number management
- ✅ **Secret Export**: Context export functionality (Section 5.3)

#### Section 6: Single-Shot APIs
- ✅ **Seal/Open APIs**: All four modes (Base, PSK, Auth, AuthPSK)
- ✅ **Export APIs**: Single-shot secret export for all modes

#### Section 7: Algorithm Identifiers

**KEMs (Section 7.1)**:
- ✅ **DHKEM_X25519_HKDF_SHA256** (0x0020): Full implementation
  - ✅ Key generation
  - ✅ Key derivation with RFC 7748 clamping
  - ✅ Serialization/deserialization
  - ✅ All-zero DH output validation
- ✅ **DHKEM_P256_HKDF_SHA256** (0x0010): Full implementation
  - ✅ Key generation
  - ✅ Key derivation with rejection sampling
  - ✅ Serialization/deserialization
  - ✅ Partial public key validation
- ✅ **DHKEM_P384_HKDF_SHA384** (0x0011): Full implementation
  - ✅ Key generation
  - ✅ Key derivation with rejection sampling
  - ✅ Serialization/deserialization
- ✅ **DHKEM_P521_HKDF_SHA512** (0x0012): Full implementation
  - ✅ Key generation
  - ✅ Key derivation with rejection sampling
  - ✅ Serialization/deserialization
- ✅ **DHKEM_X448_HKDF_SHA512** (0x0021): Full implementation
  - ✅ Key generation
  - ✅ Key derivation with RFC 7748 clamping
  - ✅ Serialization/deserialization

**KDFs (Section 7.2)**:
- ✅ **HKDF_SHA256** (0x0001): Full implementation
- ✅ **HKDF_SHA384** (0x0002): Full implementation
- ✅ **HKDF_SHA512** (0x0003): Full implementation

**AEADs (Section 7.3)**:
- ✅ **AES_128_GCM** (0x0001): Full implementation
- ✅ **AES_256_GCM** (0x0002): Full implementation
- ✅ **CHACHA20_POLY1305** (0x0003): Full implementation
- ✅ **EXPORT_ONLY** (0xFFFF): Full implementation (export-only mode)

#### Section 7.1: KEM Operations
- ✅ **SerializePublicKey/DeserializePublicKey**: Implemented for all supported KEMs
- ✅ **SerializePrivateKey/DeserializePrivateKey**: Implemented for all supported KEMs
- ✅ **DeriveKeyPair**: Implemented with proper rejection sampling/clamping
- ✅ **Validation**: Input/output validation per Section 7.1.4

#### Section 8: API Considerations
- ✅ **AAD Support**: Full support for additional authenticated data
- ✅ **Error Handling**: Comprehensive exception hierarchy

#### Section 9: Security Considerations
- ✅ **Domain Separation**: Proper suite ID construction and labeled operations
- ✅ **PSK Recommendations**: Enforced minimum entropy requirements
- ✅ **Nonce Management**: Automatic sequence number handling with overflow protection
- ✅ **Key Reuse**: Proper ephemeral key generation per message

### ❌ Not Implemented

#### Section 10: Message Encoding
- ✅ **Helpers Provided**: This library provides application-level helpers to encode/decode messages with a self-describing header per RFC §10 guidance. See below.

#### Section 9.7: Application-Level Features (Non-Goals)
The following are explicitly **not** provided by HPKE and must be handled by applications:
- ❌ **Message Ordering**: No built-in message ordering (Section 9.7.1)
- ❌ **Downgrade Prevention**: No protocol version negotiation (Section 9.7.2)
- ❌ **Replay Protection**: No built-in replay detection (Section 9.7.3)
- ❌ **Forward Secrecy**: Requires application-level key rotation (Section 9.7.4)
- ❌ **Plaintext Length Hiding**: No padding mechanisms (Section 9.7.6)

Note: These are intentional non-goals per RFC 9180 and should be handled at the application layer.

## Supported Ciphersuites

| KEM | KDF | AEAD | Status |
|-----|-----|------|--------|
| X25519 | HKDF-SHA256 | AES-128-GCM | ✅ |
| X25519 | HKDF-SHA256 | AES-256-GCM | ✅ |
| X25519 | HKDF-SHA256 | ChaCha20-Poly1305 | ✅ |
| X25519 | HKDF-SHA256 | EXPORT_ONLY | ✅ |
| P-256 | HKDF-SHA256 | AES-128-GCM | ✅ |
| P-256 | HKDF-SHA256 | AES-256-GCM | ✅ |
| P-256 | HKDF-SHA256 | ChaCha20-Poly1305 | ✅ |
| P-256 | HKDF-SHA256 | EXPORT_ONLY | ✅ |
| P-384 | HKDF-SHA384 | AES-128-GCM | ✅ |
| P-384 | HKDF-SHA384 | AES-256-GCM | ✅ |
| P-384 | HKDF-SHA384 | ChaCha20-Poly1305 | ✅ |
| P-384 | HKDF-SHA384 | EXPORT_ONLY | ✅ |
| P-521 | HKDF-SHA512 | AES-128-GCM | ✅ |
| P-521 | HKDF-SHA512 | AES-256-GCM | ✅ |
| P-521 | HKDF-SHA512 | ChaCha20-Poly1305 | ✅ |
| P-521 | HKDF-SHA512 | EXPORT_ONLY | ✅ |
| X448 | HKDF-SHA512 | AES-128-GCM | ✅ |
| X448 | HKDF-SHA512 | AES-256-GCM | ✅ |
| X448 | HKDF-SHA512 | ChaCha20-Poly1305 | ✅ |
| X448 | HKDF-SHA512 | EXPORT_ONLY | ✅ |

## API Documentation

### Package Structure

The package is installed as `rfc9180-py` but imported as `hpke`:

```python
from rfc9180 import HPKE, KEMID, KDFID, AEADID, create_hpke, append_header, parse_header
from rfc9180.constants import HPKEMode
from rfc9180.setup import HPKESetup
from rfc9180.single_shot import HPKESingleShot
from rfc9180.context import Context, ContextSender, ContextRecipient
from rfc9180.primitives.kem import DHKEM_X25519, DHKEM_X448, DHKEM_P256, DHKEM_P384, DHKEM_P521
from rfc9180.primitives.kdf import KDFBase
from rfc9180.primitives.aead import AEADBase
```

### High-Level API

#### HPKE Class

The main `HPKE` class provides a convenient interface for all HPKE operations:

```python
from rfc9180 import HPKE, KEMID, KDFID, AEADID

# Create HPKE instance
hpke = HPKE(kem_id, kdf_id, aead_id)

# Access primitives
hpke.kem_id    # KEM identifier
hpke.kdf_id    # KDF identifier
hpke.aead_id   # AEAD identifier
hpke.kem       # KEM instance
hpke.kdf       # KDF instance
hpke.aead      # AEAD instance
hpke.setup     # Setup functions (HPKESetup instance)
```

#### Convenience Function

```python
from rfc9180 import create_hpke

# Create with defaults (X25519 + HKDF-SHA256 + AES-128-GCM)
hpke = create_hpke()

# Or specify algorithms
hpke = create_hpke(
    kem_id=KEMID.DHKEM_X25519_HKDF_SHA256,
    kdf_id=KDFID.HKDF_SHA256,
    aead_id=AEADID.AES_128_GCM
)
```

### Key Management Methods

The `HPKE` class provides key management utilities:

```python
# Generate a new key pair
skR, pkR = hpke.generate_key_pair()

# Derive a key pair from a seed
seed = b"your-seed-material"  # Must be appropriate length for the KEM
skR, pkR = hpke.derive_key_pair(seed)

# Serialize keys
pk_bytes = hpke.serialize_public_key(pkR)
sk_bytes = hpke.serialize_private_key(skR)

# Keys can be passed as bytes or Key Objects - automatic deserialization
enc, ct = hpke.seal_base(pk_bytes, info, aad, pt)  # Works with bytes
enc, ct = hpke.seal_base(pkR, info, aad, pt)       # Works with Key Objects
```

### Single-Shot APIs

Single-shot APIs combine setup and encryption/decryption in one call:

```python
# Base mode
enc, ct = hpke.seal_base(pkR, info, aad, pt)
pt = hpke.open_base(enc, skR, info, aad, ct)

# PSK mode
psk = b"pre-shared-key"  # Must have at least 32 bytes of entropy
psk_id = b"psk-identifier"
enc, ct = hpke.seal_psk(pkR, info, aad, pt, psk, psk_id)
pt = hpke.open_psk(enc, skR, info, aad, ct, psk, psk_id)

# Auth mode
skS, pkS = hpke.generate_key_pair()  # Sender key pair
enc, ct = hpke.seal_auth(pkR, info, aad, pt, skS)
pt = hpke.open_auth(enc, skR, info, aad, ct, pkS)

# AuthPSK mode
enc, ct = hpke.seal_auth_psk(pkR, info, aad, pt, psk, psk_id, skS)
pt = hpke.open_auth_psk(enc, skR, info, aad, ct, psk, psk_id, pkS)
```

### Context APIs

Context APIs allow encrypting/decrypting multiple messages with the same context:

```python
# Setup contexts
enc, ctx_s = hpke.setup.setup_base_sender(pkR, info)
ctx_r = hpke.setup.setup_base_recipient(enc, skR, info)

# Encrypt/decrypt multiple messages
ct1 = ctx_s.seal(aad, pt1)
ct2 = ctx_s.seal(aad, pt2)
pt1 = ctx_r.open(aad, ct1)
pt2 = ctx_r.open(aad, ct2)

# Export secrets
secret = ctx_s.export(exporter_context, length)
```

#### Available Setup Methods

```python
# Base mode
enc, ctx_s = hpke.setup.setup_base_sender(pkR, info)
ctx_r = hpke.setup.setup_base_recipient(enc, skR, info)

# PSK mode
enc, ctx_s = hpke.setup.setup_psk_sender(pkR, info, psk, psk_id)
ctx_r = hpke.setup.setup_psk_recipient(enc, skR, info, psk, psk_id)

# Auth mode
enc, ctx_s = hpke.setup.setup_auth_sender(pkR, info, skS)
ctx_r = hpke.setup.setup_auth_recipient(enc, skR, info, pkS)

# AuthPSK mode
enc, ctx_s = hpke.setup.setup_auth_psk_sender(pkR, info, psk, psk_id, skS)
ctx_r = hpke.setup.setup_auth_psk_recipient(enc, skR, info, psk, psk_id, pkS)
```

#### Context Methods

```python
# Seal (encrypt) - only available on sender contexts
ciphertext = ctx_sender.seal(aad, plaintext)

# Open (decrypt) - only available on recipient contexts
plaintext = ctx_recipient.open(aad, ciphertext)

# Export secret - available on all contexts
secret = ctx.export(exporter_context, length)
```

### Single-Shot Export APIs

For export-only operations without encryption:

```python
from rfc9180.single_shot import HPKESingleShot

single = HPKESingleShot(hpke.setup)

# Base mode export
enc, secret = single.send_export_base(pkR, info, exporter_context, length)
secret = single.receive_export_base(enc, skR, info, exporter_context, length)

# PSK mode export
enc, secret = single.send_export_psk(pkR, info, exporter_context, length, psk, psk_id)
secret = single.receive_export_psk(enc, skR, info, exporter_context, length, psk, psk_id)

# Auth mode export
enc, secret = single.send_export_auth(pkR, info, exporter_context, length, skS)
secret = single.receive_export_auth(enc, skR, info, exporter_context, length, pkS)

# AuthPSK mode export
enc, secret = single.send_export_auth_psk(pkR, info, exporter_context, length, psk, psk_id, skS)
secret = single.receive_export_auth_psk(enc, skR, info, exporter_context, length, psk, psk_id, pkS)
```

### Message Encoding Helpers

Applications often need a wire format. This library provides simple helpers for a self-describing header:

```python
from rfc9180 import append_header, parse_header
from rfc9180.constants import HPKEMode

# Encode message with header
enc, ct = hpke.seal_base(pkR, info, aad, pt)
msg = append_header(
    enc, ct,
    int(hpke.kem_id),
    int(hpke.kdf_id),
    int(hpke.aead_id),
    int(HPKEMode.MODE_BASE)
)

# Parse message header
kem_id2, kdf_id2, aead_id2, mode2, enc2, ct2 = parse_header(
    msg,
    enc_len=hpke.kem.Nenc
)
pt2 = hpke.open_base(enc2, skR, info, aad, ct2)
```

The header format is: `"HPKE" | kem_id(2) | kdf_id(2) | aead_id(2) | mode(1) | enc | ct`

Note: This is an application-level format following RFC 9180 §10 guidance; applications may define alternative encodings.

### Constants

```python
from rfc9180 import KEMID, KDFID, AEADID
from rfc9180.constants import HPKEMode

# KEM IDs
KEMID.DHKEM_P256_HKDF_SHA256    # 0x0010
KEMID.DHKEM_P384_HKDF_SHA384    # 0x0011
KEMID.DHKEM_P521_HKDF_SHA512    # 0x0012
KEMID.DHKEM_X25519_HKDF_SHA256  # 0x0020
KEMID.DHKEM_X448_HKDF_SHA512    # 0x0021

# KDF IDs
KDFID.HKDF_SHA256  # 0x0001
KDFID.HKDF_SHA384  # 0x0002
KDFID.HKDF_SHA512  # 0x0003

# AEAD IDs
AEADID.AES_128_GCM        # 0x0001
AEADID.AES_256_GCM        # 0x0002
AEADID.CHACHA20_POLY1305  # 0x0003
AEADID.EXPORT_ONLY        # 0xFFFF

# HPKE Modes
HPKEMode.MODE_BASE     # 0x00
HPKEMode.MODE_PSK      # 0x01
HPKEMode.MODE_AUTH     # 0x02
HPKEMode.MODE_AUTH_PSK # 0x03
```

## Testing

The library includes comprehensive unit tests and support for RFC 9180 test vectors:

```bash
# Install development dependencies
pip install -e ".[dev]"

# Run all tests
pytest tests/

# Run specific test suites
pytest tests/test_kem_x25519.py
pytest tests/test_kem_p256.py
pytest tests/test_rfc9180_vectors.py
pytest tests/test_integration.py
```

## KEM/KDF Decoupling

Per RFC 9180 §7.1, each KEM mandates a specific hash/KDF. This implementation decouples the KEM's internal KDF from the ciphersuite KDF used in the key schedule (§5.1): KEMs self-select their mandated hash; the suite KDF (e.g., HKDF-SHA256) is used for key schedule and exports.

## Security Considerations

- **PSK Entropy**: PSKs must have at least 32 bytes of entropy (enforced)
- **Key Reuse**: Ephemeral keys are generated fresh for each encryption
- **Nonce Management**: Sequence numbers are automatically managed with overflow protection
- **Validation**: All inputs are validated per RFC 9180 requirements
- **Key Serialization**: Keys can be serialized/deserialized for storage and transmission
- **Context Limits**: Sequence numbers are bounded to prevent nonce reuse

## Error Handling

The library provides comprehensive error handling:

```python
from rfc9180.exceptions import (
    OpenError,              # Decryption failure
    MessageLimitReachedError,  # Sequence number overflow
)

try:
    pt = hpke.open_base(enc, skR, info, aad, ct)
except OpenError:
    # Decryption failed (wrong key, corrupted data, etc.)
    pass
```

## References

- [RFC 9180: Hybrid Public Key Encryption](https://www.rfc-editor.org/rfc/rfc9180.txt)
- [RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://www.rfc-editor.org/rfc/rfc5869.txt)
- [RFC 7748: Elliptic Curves for Security](https://www.rfc-editor.org/rfc/rfc7748.txt)

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Areas for improvement:
- Performance optimizations
- Additional test vectors
- Documentation improvements
- Additional KEM/KDF/AEAD algorithm support
