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)

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 data
  • wrap(data: Any) -> Bottle - Create a bottle with CBOR-encoded data
  • wrap_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.parse(bottle) -> (Any, OpenResult) - Open and parse contents

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.2.tar.gz (29.3 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.2-py3-none-any.whl (25.3 kB view details)

Uploaded Python 3

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

Hashes for pybottle-0.1.2.tar.gz
Algorithm Hash digest
SHA256 82863076ebc23c06c39bd5f42eb3054080666dfe41f9fe635b2d011010ecb3d6
MD5 c228346807b8cca2475c040980bc5cab
BLAKE2b-256 4c090250378b0abdf1ec047f65b272e1a8482652b4c40057545f1b41f2671e33

See more details on using hashes here.

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

Hashes for pybottle-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 699a58a24826478b0378cd8b53290b90534949fb915505f2eb985f82ff280fa8
MD5 6b0a540024129f55b74e7ea8541ec290
BLAKE2b-256 c365ee2cdda76babf9fb70b2b1b42b3d41552f577887a0ae00205b667f7930a5

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