diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 43a0309..6f800c9 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -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
diff --git a/Cargo.lock b/Cargo.lock
index 7e5ad49..bf923fd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -418,9 +418,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.127"
+version = "0.2.131"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
+checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
[[package]]
name = "lock_api"
@@ -692,18 +692,18 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.142"
+version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2"
+checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.142"
+version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e"
+checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Makefile b/Makefile
index 46ce6c7..bdb0ffa 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 1ff1e66..b01605f 100644
--- a/README.md
+++ b/README.md
@@ -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).
+ Table of Contents
+
+
+ Sender & Receiver Contexts
+
+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
+```
+ Mode.AUTH: Authenticated Sender
+
+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)
+```
+Mode.PSK: Pre-shared Key Authentication
+
+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)
+```
+Mode.AUTH_PSK: Combining AUTH and PSK.
+
+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,
+)
+```
+