Skip to main content

CryptoVault-Python

A Python implementation of the cryptovault blob format, byte-compatible with the Rust crate. AES-256-GCM-SIV under an HKDF-SHA256 key, Argon2id key derivation and DEK/KEK key wrapping, followed by the crate's forward-error correction: Reed-Solomon, an interleaver and a Viterbi code, or no FEC at all.

Status: 0.3.0, alpha. Every FEC strategy of cryptovault 0.3.0 is implemented and checked in both directions against the crate.


What it does

Two services that share a 32-byte key can encrypt and authenticate messages for each other. A blob made on either side opens on the other, Rust → Python and Python → Rust. The package opens no connections and knows nothing about transports: it turns text or key material into a blob and back, and the caller moves the blob.

The Rust crate stacks two independent layers: an AEAD that provides all of the security, then an FEC that adds channel resilience and no security. Both are implemented here, with the same four FEC strategies as the crate.

Installation

pip install cryptovault-py

The import name is cryptovault. Requirements lists the supported Python versions and the cryptography and OpenSSL floor.

Quick start

>>> from cryptovault import CryptoVault, generate_dek, generate_salt
>>> vault = CryptoVault.default()             # Argon2id + AES-256-GCM-SIV + concatenated FEC

>>> key = generate_dek()                      # or vault.derive_key(password, salt)
>>> blob = vault.encrypt_with_key(key, "sk-secret")
>>> vault.decrypt_with_key(key, blob)
'sk-secret'

>>> salt = generate_salt()                    # one per context, kept by the caller
>>> kek = generate_dek()
>>> dek = generate_dek()
>>> wrapped = vault.wrap_key(kek, salt, dek)  # the salt is bound as AAD
>>> vault.unwrap_key(kek, salt, wrapped) == dek
True

CryptoVault.default() matches CryptoVault::default() in Rust. To pick another FEC strategy, compose the vault yourself; the constructor has no defaults:

>>> from cryptovault import Aes256GcmSivCipher, Argon2Kdf, NoFec
>>> aead_only = CryptoVault(Argon2Kdf(), Aes256GcmSivCipher(), NoFec())
>>> aead_only.decrypt_with_key(key, aead_only.encrypt_with_key(key, "hi"))
'hi'

derive_key(password, salt) runs Argon2id with the crate's OWASP-2025 profile (64 MiB, three passes, four lanes). Call it once per session and keep the key.

Blob layout

blob    = base64( fec.encode( header ‖ nonce(12 B) ‖ ciphertext ‖ tag(16 B) ) )   standard, padded
header  = version (0x01) ‖ plaintext_len (u32, little-endian)
key     = HKDF-SHA256( salt = none, ikm = 32-byte master key, info = "cryptovault:v1:aead", L = 32 )
AAD     = purpose ‖ header ‖ aad_extra
purpose = 0x01 data · 0x02 wrapped key
extra   = empty for data · the salt for a wrapped key (authenticated, never stored)
cipher  = AES-256-GCM-SIV, fresh random 12-byte nonce per message

The header is bound as associated data, so changing any bit of it fails authentication. The purpose byte keeps a data blob from opening as a wrapped key, and the other way round. As for the version byte, it leaves room for the format to evolve; today only 0x01 is accepted.

FEC strategies

The FEC layer sits after the AEAD and only helps a blob survive a noisy channel. Correction is all or nothing. Within capacity the blob comes back exact; past it, decryption fails with a typed error and never returns wrong plaintext, because the AEAD tag checks the result.

Strategy Pipeline Corrects Size of a 1 KiB message
ConcatenatedFec.default() RS(255, 223) → depth-5 block interleaver → Viterbi K=7 R=1/2 scattered bit errors and bursts 3404 B
ConcatenatedFec.with_csprng_from_master(master, depth) the same, plus a keyed shuffle per window as above, weaker burst spreading 3404 B
ViterbiOnlyFec() Viterbi K=7 R=1/2 scattered bit errors 2824 B
ReedSolomonCodec() RS(255, 223) up to 16 bad bytes per 255-byte codeword 1700 B
NoFec() none nothing; any change fails authentication 1412 B

The CSPRNG variant derives its seed from the master key with HKDF (info = "cryptovault:v1:interleaver"), and both ends must build it from the same master and depth. It is obfuscation, not security.

Speed

The Reed-Solomon and Viterbi codecs come in two implementations that produce the same bytes. The platform wheels carry a compiled extension, cryptovault._native, built from the reedsolomon 0.2.0 and viterbi 0.2.0 crates that cryptovault itself uses. Everywhere else the package runs pure-Python ports of those crates. A platform with no compiled wheel gets the py3-none-any one, and on an interpreter the abi3 wheels do not cover (PyPy, free-threaded CPython) the extension does not load. Nothing needs configuring; cryptovault.fec._codecs.ACTIVE.name says which one is running. Measured with CPython 3.11 on Windows:

Strategy Codecs Encrypt 128 KiB Decrypt 128 KiB Decrypt 128 KiB, one flipped bit Decrypt 1 KiB, one flipped bit
ConcatenatedFec compiled 0.21 s 0.22 s 2.2 s 0.02 s
ConcatenatedFec Python 1.4 s 1.7 s 15.6 s 0.16 s
ViterbiOnlyFec compiled 0.03 s 0.05 s 2.6 s 0.02 s
ViterbiOnlyFec Python 0.25 s 0.66 s 14.3 s 0.12 s
ReedSolomonCodec compiled 0.02 s 0.04 s 0.04 s < 0.01 s
ReedSolomonCodec Python 1.1 s 1.0 s 0.82 s 0.01 s
NoFec none < 0.01 s < 0.01 s fails, < 0.01 s fails, < 0.01 s

A clean Viterbi sub-block decodes directly with either implementation. One with channel errors runs the full trellis: about 2 s for a full 124 950-byte sub-block with the compiled codecs, the speed of the crate's own decoder, and about 12 s in Python.

Security model

Property Mechanism Level
Confidentiality AES-256-GCM-SIV (RFC 8452) 256-bit key
Integrity / authenticity 128-bit GCM-SIV tag, header bound as AAD forgery ≤ 2⁻¹²⁸ per message
Nonce-misuse resistance SIV construction a nonce collision reveals only plaintext equality
Key separation HKDF-SHA256 with a fixed info label the AEAD key is never the raw master key
Password hardening Argon2id v1.3, m = 64 MiB, t = 3, p = 4 OWASP 2025 profile
Context binding salt and purpose bound as AAD on wrapped keys wrong salt or purpose fails authentication
Typed failures one exception class per failing stage, fixed messages no input echoed in an error
Secret hygiene no key, secret, blob or plaintext in errors, logs or repr —

All cryptographic primitives come from the cryptography package. Nothing here is home-rolled crypto.

Out of scope (what it does NOT defend against)

  • Weak passwords. Argon2id slows guessing down; it does not make a weak password strong.
  • Replay. A valid blob stays valid. Detecting repeats is the caller's protocol concern.
  • Traffic analysis. The header carries the plaintext length in the clear, and the blob size follows from it.
  • Channel errors past the FEC capacity. The blob fails closed and never decrypts to something wrong. With NoFec, a single flipped bit is enough.
  • Memory disclosure. The crate wipes secrets from memory; Python's immutable str and bytes cannot promise that, and this package does not try.
  • Host compromise. A key in process memory is as safe as the process.

Operational contracts (the caller must honor)

  • Use exactly 32 bytes of key. A shorter or longer key is an error, not something the package pads or truncates.
  • Use a fresh salt per context, from generate_salt(). The same password and salt give the same key.
  • Rate-limit derive_key for untrusted callers. Each call costs 64 MiB and noticeable CPU.
  • Rekey before ~2⁴⁸ messages per key. That is the birthday bound for random 96-bit nonces.
  • Decode with the same FEC strategy you encoded with. A blob from a default Rust vault opens with CryptoVault.default(); one from a NoFec vault needs a NoFec vault here.
  • Bound decrypt input from untrusted sources. with_max_blob_len(cap) returns a vault that rejects larger blobs before any decryption work. With FEC this also bounds the decode time, which grows with the number of corrupted Viterbi sub-blocks.
  • One blob, one message. Framing and delivery belong to the caller.

Errors

Every failure is a CryptoVaultError. The subclass names the stage that failed, and str() matches the crate's Display for the same input, word for word:

Class Raised for
InvalidInputError wrong key, salt or seed length, empty password, interleave depth out of range, oversized input, malformed blob, unknown version
EncodingError non-canonical base64, text that is not valid UTF-8
CipherError a tag that does not verify (wrong key, salt or purpose, any tampering)
KeyDerivationError Argon2id failure, or no OS entropy for a salt or DEK
ErrorCorrectionError corruption beyond the Reed-Solomon capacity, or no ChaCha20 in the backend for the CSPRNG layer

Compatibility testing

The test suite replays reference data that the crate itself produced. For NoFec: its constants, data blobs up to 128 KiB, wrapped keys, Argon2id outputs, and the exact error for 358 adversarial inputs (every single-bit flip of a blob, truncations, bad versions and lengths, non-canonical base64, wrong keys and salts, invalid UTF-8). For the FEC layer: the Reed-Solomon codewords and the outcome of 146 corrupted decodes, the interleaver permutations (the CSPRNG layer included), Viterbi encodings and 26 corrupted decodes down to the wrong bytes a heavily damaged stream decodes to, and 335 adversarial blobs under three strategies (sampled bit flips, bursts, truncation, rewritten headers, wrong keys and salts). The codec cases run against both the compiled and the Python codecs. In the other direction, a second test hands the crate blobs made in Python under every strategy and checks that it opens each one. The generator lives in interop/rust:

uv run pytest                     # full suite, Rust fixtures included
uv run pytest -m interop          # Python → Rust; needs the Rust toolchain
cargo run --release --manifest-path interop/rust/Cargo.toml -- fixtures \
    > tests/fixtures/rust_nofec.json   # regenerate the NoFec fixtures
cargo run --release --manifest-path interop/rust/Cargo.toml -- fec-fixtures \
    > tests/fixtures/rust_fec.json     # regenerate the FEC fixtures

Requirements

  • Python 3.11 or newer. Tested on Linux and Windows with 3.11 and 3.14. Compiled wheels exist for Linux (x86_64 and aarch64, glibc 2.17+ and musl), Windows x86_64 and macOS (universal2); any other platform gets the pure-Python wheel.
  • cryptography 45.0.1 or newer, linked against OpenSSL 3.2 or newer (AES-GCM-SIV and Argon2id need it; the CSPRNG layer also uses its ChaCha20). Releases before 45.0.1 reject an empty plaintext, which the crate allows. The official wheels qualify.

Development

A Rust toolchain (1.96 or newer) is needed: uv sync builds the extension in native/ with maturin, and rebuilds it whenever its sources change.

uv sync --locked
uv run ruff check . && uv run ruff format --check . && uv run mypy . && uv run pytest
uv run pip-audit
uv build
(cd native && cargo fmt --check && cargo clippy --release --all-targets -- -D warnings)

Roadmap

  1. NoFec blob format, Argon2id and key wrapping. Implemented and cross-checked against the crate in both directions.
  2. The FEC strategies. Reed-Solomon, the interleaver and Viterbi, so any cryptovault blob can be read from Python. Implemented and cross-checked in both directions.
  3. Compiled codecs. A PyO3 extension wraps the reedsolomon and viterbi crates the reference crate uses, with the pure-Python codecs as the fallback where no compiled wheel exists. Implemented; the crate fixtures check both.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this package by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

Copyright © 2026 Julian Bolivar / BolivarTech.

Metadata

Release files for cryptovault-py 0.3.0

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

Source distribution (sdist)

Source distribution for cryptovault-py 0.3.0
File Size Uploaded
cryptovault_py-0.3.0.tar.gz 37.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for cryptovault-py 0.3.0
File
cryptovault_py-0.3.0-py3-none-any.whl Python 3 none any Details
cryptovault_py-0.3.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_x86_64.whl CPython 3.11 abi3 Linux musl 1.2+ x86-64 Details
cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_aarch64.whl CPython 3.11 abi3 Linux musl 1.2+ ARM64 Details
cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
cryptovault_py-0.3.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.11 abi3 macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64 Details

Total release size: 2.1 MB

Release files / cryptovault_py-0.3.0.tar.gz

Download URL cryptovault_py-0.3.0.tar.gz
Size 37.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ab82df54ceecc4b98c3e7226cbf784363c7c6ad130561b2f129512f678217b5d
BLAKE2b-256 checksum
How to use checksums
38ba9e07865391ebf74376dd474e3e928c15b1012a9f9ce15d57f25b73d26d30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-py3-none-any.whl

Download URL cryptovault_py-0.3.0-py3-none-any.whl
Size 41.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd5c707c3772c3e775bdca45ed396c3cd3163a3d93269ee573e73f6c21c7bb66
BLAKE2b-256 checksum
How to use checksums
6d89969b31aaecfdd1012d3f2f4b9d1f1fa2bdbfb347d7b6c0c78b66f5fe20b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-win_amd64.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-win_amd64.whl
Size 155.0 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
1196a897d308201be0c6872320de4831e0a03b1d3b99a95495095d804af9900c
BLAKE2b-256 checksum
How to use checksums
17671397f8a3c1761d71529872e8e958504f8caa5e279b0eea98a2762677b35e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_x86_64.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_x86_64.whl
Size 469.2 kB
Tags CPython 3.11 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
effe45306896edec0b20dd9a258b2205b2cd8a2ebc3bba94add95831839da66f
BLAKE2b-256 checksum
How to use checksums
23da44f9fe9ad599d227d7f0c73e82eadface372f424e23425679e62fccf6eb0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_aarch64.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-musllinux_1_2_aarch64.whl
Size 435.6 kB
Tags CPython 3.11 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
d918899892cdc6a34c1b88d8341889ba4f1b571443ea0c94358a2100b63742a6
BLAKE2b-256 checksum
How to use checksums
58854e0129d9493c03852f26cad6cfde3b534998979cee8dbcacbd0a5f4b343d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 257.8 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
a55bc97bc0d8f6c03d829424fe18a828c5ab5f73afee5e30ee7668cc6c213058
BLAKE2b-256 checksum
How to use checksums
c4a999c4a0d3510ce1603b71fcff8a853a0e8c38a4dada6b7d50e2a6450c3678
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 256.9 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
6c5fcdb3d476ca14ad3c10f89e9af909525859832f4cb49c737455d15fe34f9b
BLAKE2b-256 checksum
How to use checksums
9b3eed3894e0f6ac62ab1df5c450d5988718b20596aec77a141a4233483e0099
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / cryptovault_py-0.3.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL cryptovault_py-0.3.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 452.1 kB
Tags CPython 3.11 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
139b9652924ac98eda4aa2baa82594cb209e55d037f40c6a675cea6f64835b07
BLAKE2b-256 checksum
How to use checksums
eccb6d45b7a5548f9978269eb8305bc5dd0816c8ff8e9d4b894fb464b2f45886
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

8 release files

0.2.0

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