Skip to main content

End 2 End Encryption

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

e2ee-0.0.4.tar.gz (42.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

e2ee-0.0.4-py3-none-any.whl (29.1 kB view details)

Uploaded Python 3

File details

Details for the file e2ee-0.0.4.tar.gz.

File metadata

  • Download URL: e2ee-0.0.4.tar.gz
  • Upload date:
  • Size: 42.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.1

File hashes

Hashes for e2ee-0.0.4.tar.gz
Algorithm Hash digest
SHA256 308b0186bb5fa1f41a67cad5ca524c0db6afc91aa8a247c257d5fc97a8366934
MD5 30367c1cabd8cfc918d36fdfd8b17ea7
BLAKE2b-256 7566f7339ad59d8d6a9e4242fed0a790c65b7093f0e67095c944d9de925f527c

See more details on using hashes here.

File details

Details for the file e2ee-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: e2ee-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 29.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.1

File hashes

Hashes for e2ee-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 92c83cacde8d862993aa79ea244e67b250fee628fe97e641bb1ab8f3b8822513
MD5 39d38691da48e1eab6343328906afa04
BLAKE2b-256 6ad64416ee005ce49cfbac6fdd9dd9f903e495b0af37063c942f25050a7db378

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page