Skip to main content

Cryptographic message containers with signing and encryption

Project description

pybottle

Tests Python 3.10+ License: MIT

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)

Marshaling Data

from pybottle import marshal, EMPTY_OPENER

# Marshal a Python object to a bottle
data = {"user": "alice", "action": "login", "timestamp": 1234567890}
bottle = marshal(data)

# The content-type is automatically set
print(bottle.header["ct"])  # "cbor"

# Unmarshal back
result_data, _ = EMPTY_OPENER.unmarshal(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 data
  • marshal(data: Any) -> Bottle - Create a bottle with CBOR-encoded data
  • marshal_json(data: Any) -> Bottle - Create a bottle with JSON-encoded data

Bottle Methods

  • bottle.sign(private_key) - Sign the bottle
  • bottle.encrypt(*recipients) - Encrypt for recipients
  • bottle.bottle_up() - Wrap bottle in another bottle (for layering)
  • bottle.to_cbor() -> bytes - Serialize to CBOR
  • bottle.to_json() -> bytes - Serialize to JSON

Opener Functions

  • new_opener(*keys) -> Opener - Create an opener with private keys
  • EMPTY_OPENER - An opener without keys (for signature verification only)

Opener Methods

  • opener.open(bottle) -> (bytes, OpenResult) - Open a bottle
  • opener.open_cbor(data) -> (bytes, OpenResult) - Open CBOR-encoded bottle
  • opener.unmarshal(bottle) -> (Any, OpenResult) - Open and unmarshal

OpenResult

  • result.signed_by(key) -> bool - Check if signed by a key
  • result.decryption_count - Number of decryption layers
  • result.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

pybottle-0.1.0.tar.gz (29.2 kB view details)

Uploaded Source

Built Distribution

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

pybottle-0.1.0-py3-none-any.whl (25.0 kB view details)

Uploaded Python 3

File details

Details for the file pybottle-0.1.0.tar.gz.

File metadata

  • Download URL: pybottle-0.1.0.tar.gz
  • Upload date:
  • Size: 29.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for pybottle-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fc7b56c30b85d41fa13ed96328bc50ef328959c93d0c3f43463d32df5da6769e
MD5 d3fb64c3b8d7abc0a54b89922f491459
BLAKE2b-256 23442b2cea04b0421f04c1e95d77f3a921a65795639e65a16d2f9b3643d98ce1

See more details on using hashes here.

File details

Details for the file pybottle-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pybottle-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 25.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for pybottle-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b17082bb4b8c1ca79550b87d7dbbfd9100c850444a0ce9621bdee8a9a5e3aba
MD5 fb903c90ec12f0d87611b9aad2f9308b
BLAKE2b-256 40c6622cb4b8689afca070dae95d399d05c41563beb8b26ab31adba79a75c756

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