Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add usage section to readme #21

Merged
merged 5 commits into from
Aug 15, 2022
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
6 changes: 6 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
default: true
- name: Lint code
run: |
make pydep
Expand Down
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ fmt:
.PHONY: lint
lint:
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings --no-deps
# temporarily allow borrow-deref-ref until this issue is resolved:
# https://github.com/rust-lang/rust-clippy/issues/8971
cargo clippy --all-targets -- -D warnings --no-deps -A clippy::borrow-deref-ref
flake8 .

.PHONY: clean
Expand Down
226 changes: 200 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,226 @@
hybrid-pke
Hybrid PKE
===============
The Hybrid Public Key Encryption (HPKE) standard in Python.

[`hpke-rs`](https://crates.io/crates/hpke-rs) :handshake: [`PyO3`](https://github.com/PyO3/pyo3)
`hybrid_pke` = [`hpke-rs`](https://github.com/franziskuskiefer/hpke-rs) :heavy_plus_sign: [`PyO3`](https://github.com/PyO3/pyo3)

This library provides Python bindings to the `hpke-rs` crate, which supports primitives from either [Rust Crypto](https://github.com/RustCrypto) or [EverCrypt](https://hacl-star.github.io/HaclValeEverCrypt.html).

<details>
<summary> Table of Contents </summary>
<ol>
<li><a href="#usage">Usage</a></li>
<li><a href="#features">Features</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#development">Development</a></li>
<li><a href="#related-projects">Related Projects</a></li>
</ol>
</details>

## Usage
### Basic
The single-shot API is intended for single message encryption/decryption. The default HPKE configuration uses the unauthenticated Base mode, an X25519 DH key encapsulation mechanism, a SHA256 key derivation mechanism, and a ChaCha20Poly1305 AEAD function.

```python
import hybrid_pke

hpke = hybrid_pke.default()
info = b"" # shared metadata, correspondance-level
aad = b"" # shared metadata, message-level
secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys, pre-generated

# ============== Sender ==============

message = b"hello from the other side!"
encap, ciphertext = hpke.seal(public_key_r, info, aad, message)

# ============= Receiver =============

plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext)
print(plaintext.decode("utf-8"))
# >> hello from the other side!
```

### Advanced

<details><summary> Sender & Receiver Contexts </summary>

The Sender Context and Receiver Context APIs allow for setting up a context for repeated encryptions and decryptions. It's recommended whenever you intend to perform several encryptions or decryptions in quick succession.
```python
info = b"quotes from your favorite aphorists"
aads = [
b"Szasz",
b"Nietzsche",
b"Morandotti",
b"Brudzinski",
b"Hubbard",
]

# ============== Sender ==============

messages = [
b"Two wrongs don't make a right, but they make a good excuse.",
b"Become who you are!",
b"Only those who aren't hungry are able to judge the quality of a meal.",
b"Under certain circumstances a wanted poster is a letter of recommendation.",
b"Nobody ever forgets where he buried the hatchet.",
]
encap, sender_context = hpke.setup_sender(public_key_r, info)

ciphertexts = []
for aad, msg in zip(aads, messages):
ciphertext = sender_context.seal(aad, msg)
ciphertexts.append(ciphertext)

# ============= Receiver =============

receiver_context = hpke.setup_receiver(encap, secret_key_r, info)
plaintexts = []
for aad, ctxt in zip(aads, ciphertexts):
plaintext = receiver_context.open(aad, ctxt)
plaintexts.append(plaintext)

print(f"\"{plaintexts[0].decode()}\" - {aad[0].decode()}")
print(f"\"{plaintexts[1].decode()}\" - {aad[1].decode()}")
# >> "Two wrongs don't make a right, but they make a good excuse." - Szasz
# >> "Become who you are!" - Nietzsche
```
</details>

<details><summary> Mode.AUTH: Authenticated Sender </summary>

Auth mode allows for signing and verifying encryptions with a previously authenticated sender key-pair.
```python
hpke = hybrid_pke.default(mode=hybrid_pke.Mode.AUTH)
secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys
secret_key_s, public_key_s = hpke.generate_key_pair() # sender keys, pre-authenticated

# ============== Sender ==============

# sign with sender's secret key
encap, ciphertext = hpke.seal(public_key_r, info, aad, message, sk_s=secret_key_s)

# ============= Receiver =============

# verify with sender's public key
plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext, pk_s=public_key_s)
```
</details>

<details><summary>Mode.PSK: Pre-shared Key Authentication</summary>

PSK mode allows for signing and verifying encryptions with a previously shared key held by both the sender and recipient.
```python
hpke = hybrid_pke.default(mode=hybrid_pke.Mode.PSK)
# pre-shared key + ID
psk = bytes.fromhex("0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82")
psk_id = bytes.fromhex("456e6e796e20447572696e206172616e204d6f726961")

# ============== Sender ==============

# sign with pre-shared key
encap, ciphertext = hpke.seal(public_key_r, info, aad, message, psk=psk, psk_id=psk_id)

# ============= Receiver =============

# verify with pre-shared key
plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext, psk=psk, psk_id=psk_id)
```
</details>

<details><summary>Mode.AUTH_PSK: Combining AUTH and PSK. </summary>

PSK mode allows for signing and verifying encryptions with a previously shared key held by both the sender and recipient.
```python
hpke = hybrid_pke.default(mode=hybrid_pke.Mode.PSK)
secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys
secret_key_s, public_key_s = hpke.generate_key_pair() # sender keys, pre-authenticated
# pre-shared key + ID
psk = bytes.fromhex("0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82")
psk_id = bytes.fromhex("456e6e796e20447572696e206172616e204d6f726961")

# ============== Sender ==============

# sign with both pre-shared key and sender's secret key
encap, ciphertext = hpke.seal(
public_key_r, info, aad, message,
psk=psk, psk_id=psk_id, sk_s=secret_key_s,
)

# ============= Receiver =============

# verify with both pre-shared key and sender's public key
plaintext = hpke.open(
encap, secret_key_r, info, aad, ciphertext,
psk=psk, psk_id=psk_id, pk_s=public_key_s,
)
```
</details>

<p align="right">(<a href="#top">back to top</a>)</p>

## Features
The modes and features available match those supported by `hpke-rs`.

- Modes
- [x] mode_base
- [x] mode_psk
- [x] mode_auth
- [x] mode_auth_psk
- AEADs
- [x] AES-128-GCM
- [x] AES-256-GCM
- [x] ChaCha20Poly1305
- [x] Export only
- KEMs
- [x] DHKEM(P-256, HKDF-SHA256)
- [ ] DHKEM(P-384, HKDF-SHA384)
- [ ] DHKEM(P-521, HKDF-SHA512)
- [x] DHKEM(X25519, HKDF-SHA256)
- [ ] DHKEM(X448, HKDF-SHA512)
- KDFs
- [x] HKDF-SHA256
- [x] HKDF-SHA384
- [x] HKDF-SHA512
The features available match those supported by `hpke-rs`.

<details><summary>HPKE Modes</summary>

- [x] mode_base
- [x] mode_psk
- [x] mode_auth
- [x] mode_auth_psk
</details>

<details><summary>KEMs: (Diffie-Hellman) Key Encapsulation Mechanisms</summary>

- [x] DHKEM(P-256, HKDF-SHA256)
- [ ] DHKEM(P-384, HKDF-SHA384)
- [ ] DHKEM(P-521, HKDF-SHA512)
- [x] DHKEM(X25519, HKDF-SHA256)
- [ ] DHKEM(X448, HKDF-SHA512)
</details>

<details><summary>KDFs: Key Derivation Functions </summary>

- [x] HKDF-SHA256
- [x] HKDF-SHA384
- [x] HKDF-SHA512
</details>

<details><summary>AEADs: Authenticated Encryption with Additional Data functions</summary>

- [x] AES-128-GCM
- [x] AES-256-GCM
- [x] ChaCha20Poly1305
- [x] Export only
</details>

<p align="right">(<a href="#top">back to top</a>)</p>

## Installation
Wheels for various platforms and architectures can be found on [PyPI](https://pypi.org/project/hybrid-pke/) or in the `wheelhouse.zip` archive from the [latest Github release](https://github.com/capeprivacy/hybrid-pke/releases).

The library can also be installed from source with [`maturin`](https://github.com/PyO3/maturin) -- see below.

<p align="right">(<a href="#top">back to top</a>)</p>

## Development

We use [`maturin`](https://github.com/PyO3/maturin) to build and distribute the PyO3 extension module as a Python wheel.

For users of `cmake`, we provide a [`Makefile`](https://github.com/capeprivacy/hybrid-pke/blob/main/Makefile) that includes some helpful development commands.

Other useful tips:
<details><summary>Some useful tips</summary>

- `maturin develop` builds & installs the Python package into your Python environment (`venv` or `conda` recommended)
- `pytest .` tests the resulting Python package
- `pytest .` tests the resulting Python package.
- `pytest -n auto .` runs the full test suite in parallel.
- `maturin build --release -o dist --sdist` builds the extension module in release-mode and produces a wheel for your environment's OS and architecture.
- The `-i`/`--interpreter` flag for `maturin` can be used to swap out different Python interpreters, if you have multiple Python installations.
</details>

<p align="right">(<a href="#top">back to top</a>)</p>

## Related Projects
- [hpke-py](https://github.com/ctz/hpke-py): An implementation of HPKE based on primitives from [cryptography.io](https://cryptography.io).

<p align="right">(<a href="#top">back to top</a>)</p>