PyHPKE is a HPKE (Hybrid Public Key Encryption) implementation written in Python.
You can install PyHPKE with pip:
$ pip install pyhpkeAnd then, you can use it as follows:
from pyhpke import AEADId, CipherSuite, KDFId, KEMId, KEMKey
# The sender side:
suite_s = CipherSuite.new(
KEMId.DHKEM_P256_HKDF_SHA256, KDFId.HKDF_SHA256, AEADId.AES128_GCM
)
pkr = KEMKey.from_jwk( # from_pem is also available.
{
"kid": "01",
"kty": "EC",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
enc, sender = suite_s.create_sender_context(pkr)
ct = sender.seal(b"Hello world!")
# The recipient side:
suite_r = CipherSuite.new(
KEMId.DHKEM_P256_HKDF_SHA256, KDFId.HKDF_SHA256, AEADId.AES128_GCM
)
skr = KEMKey.from_jwk(
{
"kid": "01",
"kty": "EC",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
recipient = suite_r.create_recipient_context(enc, skr)
pt = recipient.open(ct)
assert pt == b"Hello world!"
# deriving a KEMKeyPair
keypair = suite_s.kem.derive_key_pair(b"some_ikm_bytes_used_for_key_derivation")- Installation
- Supported HPKE Modes and Cipher Suites
- Warnings and Restrictions
- Usage
- API Reference
- Test
- Security
- Contributing
You can install PyHPKE with pip:
$ pip install pyhpkePyHPKE supports all of the HPKE modes and cipher suites defined in RFC9180 below.
- modes
- ✅ Base
- ✅ PSK
- ✅ Auth
- ✅ AuthPSK
- KEMs (Key Encapsulation Machanisms)
- ✅ DHKEM (P-256, HKDF-SHA256)
- ✅ DHKEM (P-384, HKDF-SHA384)
- ✅ DHKEM (P-521, HKDF-SHA512)
- ✅ DHKEM (X25519, HKDF-SHA256)
- ✅ DHKEM (X448, HKDF-SHA512)
- KDFs (Key Derivation Functions)
- ✅ HKDF-SHA256
- ✅ HKDF-SHA384
- ✅ HKDF-SHA512
- AEADs (Authenticated Encryption with Associated Data)
- ✅ AES-128-GCM
- ✅ AES-256-GCM
- ✅ ChaCha20Poly1305
- ✅ Export Only
Although this library has been passed all of the following official test vectors, it has not been formally audited.
from pyhpke import AEADId, CipherSuite, KDFId, KEMId, KEMKey
# The sender side:
suite_s = CipherSuite.new(
KEMId.DHKEM_P256_HKDF_SHA256, KDFId.HKDF_SHA256, AEADId.AES128_GCM
)
pkr = KEMKey.from_jwk(
{
"kid": "01",
"kty": "EC",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
enc, sender = suite_s.create_sender_context(pkr)
ct = sender.seal(b"Hello world!")
# The recipient side:
suite_r = CipherSuite.new(
KEMId.DHKEM_P256_HKDF_SHA256, KDFId.HKDF_SHA256, AEADId.AES128_GCM
)
skr = KEMKey.from_jwk(
{
"kid": "01",
"kty": "EC",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
recipient = suite_r.create_recipient_context(enc, skr)
pt = recipient.open(ct)
assert pt == b"Hello world!"See Documentation.
You can run tests from the project root after cloning with:
$ toxIf you discover a security issue, please report it via a GitHub issue and avoid disclosing exploit details publicly until we respond.
This project has not been formally audited; use it according to your risk assessment.
The Snyk badge reports results based on requirements.txt exported from uv.lock and reflects dependency scanning only.
See SECURITY.md for our security policy and reporting guidance.
We welcome all kind of contributions, filing issues, suggesting new features or sending PRs.