Skip to main content

AES-256-GCM encryption with Argon2id key derivation. Self-contained JSON payloads, no shared state.

Project description

AES-256-GCM Secure Vault

A symmetric encryption module implementing AES-256-GCM with Argon2id key derivation, following established envelope encryption patterns (similar to JWE/Fernet). Payloads are self-contained JSON blobs — no shared state or config files required to decrypt.

Built to understand authenticated encryption, KDF parameter binding, and defensive input validation from first principles rather than wrapping a high-level library.

Threat Model

What this protects against

  • Confidentiality at rest. An attacker with access to the encrypted blob but not the passphrase cannot recover plaintext. AES-256-GCM provides authenticated encryption with 256-bit key strength.
  • Ciphertext tampering. GCM's authentication tag detects any modification to the ciphertext. Decryption fails rather than returning corrupted data.
  • Header tampering. AAD binding means altering any unencrypted metadata field (version, KDF params, salt, nonce) also fails the authentication tag — even without touching the ciphertext itself.
  • KDF downgrade attacks. Enforced minimum bounds on Argon2id parameters (ops ≥ 2, memory ≥ 32 MiB) prevent an attacker from forging a payload with trivially weak key derivation.
  • Offline brute-force (within reason). Argon2id is memory-hard, making GPU/ASIC-based dictionary attacks significantly more expensive than with PBKDF2 or bcrypt. Effectiveness depends entirely on passphrase entropy — see limitations below.

What this does NOT protect against

  • Weak passphrases. Argon2id slows down brute-force but cannot compensate for a 4-character password. Passphrase entropy is the caller's responsibility. No strength meter, no enforcement.
  • Memory-scraping / cold boot attacks. The derived key, plaintext, and intermediate buffers exist in process memory during encryption and decryption. An attacker with memory access (malware, memory dump, cold boot) can extract secrets. This module does not pin, zero, or mlock sensitive memory — Python's garbage collector makes this unreliable anyway.
  • Side-channel / timing attacks. No constant-time comparisons beyond what the cryptography library provides internally. The Python runtime itself is not side-channel resistant. Do not use in contexts where an attacker can measure execution time or power consumption.
  • Quantum adversaries. AES-256 offers ~128-bit post-quantum security via Grover's algorithm, which is still strong. However, the key exchange (passphrase → KDF → key) is not quantum-resistant in the broader cryptographic sense. This is a symmetric-only tool — no asymmetric components are exposed.
  • Key management and rotation. There is no built-in mechanism to rotate passphrases, re-encrypt existing blobs under new keys, or expire old payloads. Each blob is independent.
  • Compromised dependencies. If the cryptography library or the underlying OpenSSL implementation has a vulnerability, this module inherits it. No independent verification of primitive correctness is performed.
  • Multi-gigabyte files. Peak RAM is ~3× payload size. The 100 MiB hard limit exists to prevent OOM. For large files, use chunked streaming (e.g., Tink or STREAM ciphers).
  • Nonce reuse. Each encryption generates a random 12-byte nonce. With random nonces, AES-GCM's birthday bound is approximately 2³² encryptions under the same key before collision risk becomes non-negligible. This module does not track nonce usage — it relies on os.urandom uniqueness.

Security Properties

Property Detail
Cipher AES-256-GCM (authenticated encryption)
Key derivation Argon2id
KDF defaults ops=3, memory=64 MiB, p=4, key_len=32 — exceeds OWASP 2023/2024 minimum baseline (19 MiB / 2 iterations). p=4 is not an OWASP recommendation; parallelism should be tuned to deployment hardware.
KDF floor (decrypt) ops ≥ 2, memory ≥ 32 MiB, p ∈ [1, 16] — weaker than defaults; exists for backwards compatibility, not as a security target
Salt 16 bytes, random per encryption
Nonce 12 bytes, random per encryption (NIST standard)
Auth tag 16 bytes, appended to ciphertext by GCM
AAD Version + KDF params + salt + nonce bound to ciphertext
Payload limit 100 MiB (enforced before encryption and after parsing)

Memory Usage

Peak RAM during a single encrypt/decrypt operation is roughly 3× the payload size (~300 MiB for a 100 MiB payload) due to base64 encoding, intermediate string allocations, and ciphertext byte arrays existing simultaneously.

Versioning

Version AAD includes key_len Status
1.0 No Supported (read-only)
2.0 Yes Current

New payloads are always written as v2.0. v1.0 payloads can be decrypted without any migration step.

Requirements

Web UI: A modern browser. No install.

Python CLI / library:

  • Python 3.8+
  • cryptography>=44.0.0 (installed automatically)

Installation

pip install aes-secure-vault

For development (includes pytest and Hypothesis):

pip install aes-secure-vault[dev]

Web UI

The recommended way to open it:

python -m http.server 8000

Then visit http://localhost:8000 in your browser. This avoids browser security restrictions on WASM loaded via file://.

Alternatively, double-click index.html in File Explorer or run start index.html — this works in most browsers but may fail silently in some (Chrome in particular blocks WASM from file://).

  • Encrypt tab — type a message, enter a passphrase, copy the encrypted blob
  • Decrypt tab — paste a blob, enter the passphrase, read the original message
  • Runs entirely client-side via the WebCrypto API and argon2-browser (loaded from CDN with SRI verification)
  • Content Security Policy restricts script execution to trusted sources
  • Payload size limits match the Python CLI (100 MiB)
  • Blobs are fully interchangeable with the CLI — encrypt in the browser, decrypt with Python, and vice versa
  • Limitation: binary payloads and file encryption require the CLI

CLI Usage

# Encrypt a file (passphrase prompted securely via stdin, with confirmation)
secure-vault encrypt --file secret.txt --out secret.enc

# Encrypt inline text
secure-vault encrypt --text "my secret data" --out secret.enc

# Decrypt to stdout
secure-vault decrypt --file secret.enc

# Decrypt binary payload to file
secure-vault decrypt --file secret.enc --out recovered.bin --bytes

python -m secure_vault also works as an alternative to secure-vault.

On encrypt, the CLI prompts for the passphrase twice to prevent typos. Ctrl+C exits cleanly at any prompt.

Library Usage

from secure_vault import SecureVault

vault = SecureVault()

# Encrypt (str or bytes)
blob = vault.encrypt("my secret data", "a-strong-passphrase-here")
blob = vault.encrypt(b"\x00\xFF binary data", "a-strong-passphrase-here")

# Decrypt to str (default)
plaintext = vault.decrypt(blob, "a-strong-passphrase-here")

# Decrypt to raw bytes
raw = vault.decrypt(blob, "a-strong-passphrase-here", return_bytes=True)

The encrypted blob is a JSON string safe to store or transmit:

{
  "header": {
    "v": "2.0",
    "kdf": { "ops": 3, "mem": 65536, "p": 4, "key_len": 32 },
    "salt": "<base64>",
    "nonce": "<base64>"
  },
  "ciphertext": "<base64 ciphertext + auth tag>"
}

Exceptions

Exception Cause
ValueError Empty data, empty passphrase, payload too large, malformed structure, unsupported version, invalid KDF params
DecryptionError Wrong passphrase, modified ciphertext, or tampered header (AAD failure)
RuntimeError Insufficient memory for Argon2, unexpected crypto error, or decrypted data is not valid UTF-8

Tests

# Full suite (includes Hypothesis fuzz tests — may take a few minutes)
pytest test_secure_vault.py -v

# Skip slow fuzz tests
pytest test_secure_vault.py -v -k "not arbitrary"

Coverage includes: roundtrip correctness, non-determinism verification, authentication/integrity failures, KDF boundary enforcement, type evasion guards, malformed payload rejection, OOM limits, MemoryError path, legacy v1.0 decryption, and property-based fuzzing with Hypothesis.

References

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

aes_secure_vault-1.0.1.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

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

aes_secure_vault-1.0.1-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file aes_secure_vault-1.0.1.tar.gz.

File metadata

  • Download URL: aes_secure_vault-1.0.1.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for aes_secure_vault-1.0.1.tar.gz
Algorithm Hash digest
SHA256 562548c129ab7481907846c22fb93ded1f41785136883abeaf303f26be960d38
MD5 e0fe7ef6fcb4987e36878c9ba548930a
BLAKE2b-256 bb2841948b48b35e592ad1dbf3d0f99cf0c84ec1f3a73c28ffec763b297eea86

See more details on using hashes here.

File details

Details for the file aes_secure_vault-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for aes_secure_vault-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2130b472daffab5f872dd2b143018d9b56b9b3f21aa7f6a241dc634c37cb2e18
MD5 b224b3cb786ef52cb30c83b3fc0ff664
BLAKE2b-256 a3c714a716bbbedfa5b0b325c9fa52275c4c8cc7ae2f59786ddf486ed2de17b2

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