Cryptographic message containers with signing and encryption
Project description
pybottle
A Python library for cryptographic message containers with signing and encryption. This is a port of the Go cryptutil library.
Features
- Bottle: Signed, encrypted message containers with CBOR/JSON serialization
- IDCard: Identity cards linking signing keys to encryption keys
- Opener: For opening (decrypting/verifying) bottles
- Keychain: Private key storage and management
- Membership: Group membership support
Supported Key Types
- RSA (signing and encryption)
- ECDSA P-256 (signing, ECDH encryption)
- Ed25519 (signing, converts to X25519 for encryption)
- X25519 (ECDH encryption)
Installation
pip install pybottle
Or install from source:
git clone https://github.com/KarpelesLab/pybottle.git
cd pybottle
pip install -e .
Quick Start
Basic Message Signing
from cryptography.hazmat.primitives.asymmetric import ec
from pybottle import new_bottle, EMPTY_OPENER
# Generate a key
private_key = ec.generate_private_key(ec.SECP256R1())
# Create and sign a bottle
bottle = new_bottle(b"Hello, World!")
bottle.sign(private_key)
# Verify the signature
data, result = EMPTY_OPENER.open(bottle)
assert result.signed_by(private_key.public_key())
Encrypted Messages
from cryptography.hazmat.primitives.asymmetric import ec
from pybottle import new_bottle, new_opener
# Generate keys for sender and recipient
alice = ec.generate_private_key(ec.SECP256R1())
bob = ec.generate_private_key(ec.SECP256R1())
# Alice creates an encrypted, signed message for Bob
bottle = new_bottle(b"Secret message for Bob")
bottle.encrypt(bob.public_key())
bottle.bottle_up() # Wrap so signature covers encryption
bottle.sign(alice)
# Serialize to bytes
cbor_data = bottle.to_cbor()
# Bob opens the bottle
opener = new_opener(bob)
data, result = opener.open_cbor(cbor_data)
print(data) # b"Secret message for Bob"
print(result.signed_by(alice.public_key())) # True
print(result.decryption_count) # 1
Multiple Recipients
# Encrypt for multiple recipients - any of them can decrypt
bottle = new_bottle(b"Message for the group")
bottle.encrypt(bob.public_key(), charlie.public_key(), dave.public_key())
Using IDCards
IDCards allow a signing key to specify additional keys for encryption:
from pybottle import new_idcard, new_opener
from cryptography.hazmat.primitives.asymmetric import ec, x25519
# Create signing and encryption keys
sign_key = ec.generate_private_key(ec.SECP256R1())
encrypt_key = x25519.X25519PrivateKey.generate()
# Create an IDCard
idcard = new_idcard(sign_key.public_key())
idcard.add_key_purpose(encrypt_key.public_key(), "decrypt")
# Now you can encrypt to the IDCard (uses decrypt-capable keys)
bottle = new_bottle(b"Message")
bottle.encrypt(idcard)
# Open with either key
opener = new_opener(encrypt_key)
data, result = opener.open(bottle)
Wrapping Data
from pybottle import wrap, EMPTY_OPENER
# Wrap a Python object in a bottle
data = {"user": "alice", "action": "login", "timestamp": 1234567890}
bottle = wrap(data)
# The content-type is automatically set
print(bottle.header["ct"]) # "cbor"
# Parse it back
result_data, _ = EMPTY_OPENER.parse(bottle)
print(result_data) # {"user": "alice", ...}
Interoperability with Go
This library is designed to be wire-compatible with the Go cryptutil library. Bottles created in Python can be opened in Go and vice versa.
Static test keys matching the Go tests are available:
from pybottle.testkeys import get_alice, get_bob, get_chloe, get_daniel
# alice, bob: ECDSA P-256 keys
# chloe, daniel: Ed25519 keys
alice = get_alice()
bob = get_bob()
API Reference
Bottle Functions
new_bottle(data: bytes) -> Bottle- Create a new bottle with raw datawrap(data: Any) -> Bottle- Create a bottle with CBOR-encoded datawrap_json(data: Any) -> Bottle- Create a bottle with JSON-encoded data
Bottle Methods
bottle.sign(private_key)- Sign the bottlebottle.encrypt(*recipients)- Encrypt for recipientsbottle.bottle_up()- Wrap bottle in another bottle (for layering)bottle.to_cbor() -> bytes- Serialize to CBORbottle.to_json() -> bytes- Serialize to JSON
Opener Functions
new_opener(*keys) -> Opener- Create an opener with private keysEMPTY_OPENER- An opener without keys (for signature verification only)
Opener Methods
opener.open(bottle) -> (bytes, OpenResult)- Open a bottleopener.open_cbor(data) -> (bytes, OpenResult)- Open CBOR-encoded bottleopener.parse(bottle) -> (Any, OpenResult)- Open and parse contents
OpenResult
result.signed_by(key) -> bool- Check if signed by a keyresult.decryption_count- Number of decryption layersresult.signatures- List of verified signatures
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=pybottle
License
MIT License - see LICENSE for details.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pybottle-0.1.2.tar.gz.
File metadata
- Download URL: pybottle-0.1.2.tar.gz
- Upload date:
- Size: 29.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82863076ebc23c06c39bd5f42eb3054080666dfe41f9fe635b2d011010ecb3d6
|
|
| MD5 |
c228346807b8cca2475c040980bc5cab
|
|
| BLAKE2b-256 |
4c090250378b0abdf1ec047f65b272e1a8482652b4c40057545f1b41f2671e33
|
File details
Details for the file pybottle-0.1.2-py3-none-any.whl.
File metadata
- Download URL: pybottle-0.1.2-py3-none-any.whl
- Upload date:
- Size: 25.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
699a58a24826478b0378cd8b53290b90534949fb915505f2eb985f82ff280fa8
|
|
| MD5 |
6b0a540024129f55b74e7ea8541ec290
|
|
| BLAKE2b-256 |
c365ee2cdda76babf9fb70b2b1b42b3d41552f577887a0ae00205b667f7930a5
|