Skip to main content

E2EE

End-to-end encryption helper built on elliptic-curve Diffie-Hellman (ECDH), PBKDF2 key derivation, and Fernet symmetric encryption. The E2EE class coordinates server/client key exchange, mutual authentication, and message confidentiality.

Features

  • ECDH (SECP521R1) key agreement for both a public key and a salt key-pair
  • Server-signed public material to prevent tampering (ECDSA with SHA-224)
  • PBKDF2-HMAC-SHA256 derivation of a 256-bit Fernet key from the dual shared secrets
  • Simple encrypt/decrypt helpers that emit URL-safe Base64 strings

Prerequisites

  • Python >=3.9.10
  • cryptography library (pulled automatically via pyproject.toml)
  • DER-encoded ECDSA key pair available on disk:
    • PRIVATE_KEY environment variable -> path to the server signing key (PKCS#8 DER)
    • PUBLIC_KEY environment variable -> path to the public key distributed to clients (SubjectPublicKeyInfo DER)

These files are used exactly as in the tests/conftest.py fixture.

Key-Exchange Workflow

  1. Server bootstrap: instantiate E2EE() with no arguments. It becomes the authoritative peer, loading its private signing key and generating ephemeral public key/salt pairs.
  2. Publish public material: the server exposes server.public_key and server.public_salt. Each property returns the Base32-encoded point alongside a Base32 signature.
  3. Client bootstrap: instantiate E2EE(server.public_key, server.public_salt) on the client. The client verifies the signatures with the server's public signing key (pointed to by PUBLIC_KEY), generates its own ephemeral key material, and sets up symmetric encryption.
  4. Mutual exchange: the client shares its unsigned public key/salt. The server calls load_peer_public_key(client.public_key, client.public_salt) to complete ECDH.
  5. Secure channel: both peers derive the same Fernet key from the combined secrets and can call encrypt/decrypt to exchange ciphertexts.

The round-trip mirrors tests/test_e2ee.py and can be summarized as:

server = E2EE()
client = E2EE(server.public_key, server.public_salt)
server.load_peer_public_key(client.public_key, client.public_salt)

ciphertext = server.encrypt("hello")
plaintext = client.decrypt(ciphertext)

Quickstart Example

  1. Generate signing keys (dev only):
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat

priv = ec.generate_private_key(ec.SECP521R1())
pub = priv.public_key()

open("/tmp/ecdsa_private_key.pem", "wb").write(
      priv.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption())
)
open("/tmp/ecdsa_public_key.pem", "wb").write(
      pub.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
)
  1. Export the paths:
export PRIVATE_KEY=/tmp/ecdsa_private_key.pem
export PUBLIC_KEY=/tmp/ecdsa_public_key.pem
  1. Exchange messages:
from e2ee import E2EE

server = E2EE()
client = E2EE(server.public_key, server.public_salt)
server.load_peer_public_key(client.public_key, client.public_salt)

secret_from_server = server.encrypt("SECRET MESSAGE FROM SERVER!")
print(client.decrypt(secret_from_server))

secret_from_client = client.encrypt("SECRET MESSAGE FROM CLIENT!")
print(server.decrypt(secret_from_client))

API Reference

  • E2EE(public_key=None, public_salt=None): Creates a server instance when called with no arguments; otherwise acts as a client that immediately verifies and loads the remote public key material.
  • public_key / public_salt: Base32-encoded strings (plus signatures on the server). Client values do not include signatures.
  • load_peer_public_key(public_key, public_salt, exchange=True): Imports peer material. For clients, the tuple must include (encoded_key, signature) pairs; servers expect plain Base32 strings. When exchange is true (default) the instance computes shared secrets and arms Fernet encryption.
  • encrypt(message: str) -> str: Encrypts UTF-8 strings and returns URL-safe Base64 ciphertext, ideal for transport over JSON/HTTP.
  • decrypt(encoded_ciphertext: str) -> str: Reverses encrypt and returns the original plaintext string.

Running the Test Suite

pip install -e .[dev]
pytest

The unit test in tests/test_e2ee.py performs the full server/client dance, so it serves as an executable example as well as a regression check.

Metadata

Release files for E2EE 0.0.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for E2EE 0.0.4
File Size Uploaded
e2ee-0.0.4.tar.gz 42.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for E2EE 0.0.4
File Interpreter ABI Platform
e2ee-0.0.4-py3-none-any.whl Python 3 none any Details

Total release size: 71.7 kB

Release files / e2ee-0.0.4.tar.gz

Download URL e2ee-0.0.4.tar.gz
Size 42.6 kB
Tags Source
SHA-256 checksum
How to use checksums
308b0186bb5fa1f41a67cad5ca524c0db6afc91aa8a247c257d5fc97a8366934
BLAKE2b-256 checksum
How to use checksums
7566f7339ad59d8d6a9e4242fed0a790c65b7093f0e67095c944d9de925f527c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.1

Release files / e2ee-0.0.4-py3-none-any.whl

Download URL e2ee-0.0.4-py3-none-any.whl
Size 29.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
92c83cacde8d862993aa79ea244e67b250fee628fe97e641bb1ab8f3b8822513
BLAKE2b-256 checksum
How to use checksums
6ad64416ee005ce49cfbac6fdd9dd9f903e495b0af37063c942f25050a7db378
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.1

Release history Release notifications | RSS feed

This release

0.0.4 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page