Skip to main content

Python binding for rpgp, a Rust implementation of OpenPGP according to RFC 9580

Project description

rpgp-py

Python bindings for rpgp, exposed as the openpgp package.

  • support for RFC 9580
  • a typed Python surface (.pyi stubs ship with the package),
  • wheels for Python 3.10+,
  • high-level helpers for common signing/encryption workflows,
  • detailed inspection APIs for packets, signatures, key bindings, and generated key material.

Why use rpgp-py instead of PGPy or PGPy13?

Broadly:

  • RFC 9580 coverage: rpgp-py follows the Rust pgp crate, which targets newer OpenPGP work such as RFC 9580-compatible v6 key material and modern curves/packet handling. PGPy and PGPy13 are still RFC 4880.
  • Rust core: the cryptographic core is implemented in Rust and exposed through a Python-first API.
  • Typed builders and inspectors: the package exposes typed builders for key generation plus rich metadata for self-signatures, key flags, features, user bindings, S2K settings, and public-key parameters.
  • Python 3.13 story: PGPy still imports imghdr, which was removed from the standard library in Python 3.13. PGPy13 exists as a compatibility fork; rpgp-py targets current Python directly.

Installation

pip install rpgp-py

Reference documentation

When you need the underlying Rust semantics or want to compare behaviour against upstream docs, these are the most useful references:

Main use cases

1. Parse and inspect transferable keys

from openpgp import PublicKey, SecretKey

public_key, _ = PublicKey.from_armor(public_key_armor)
public_key.verify_bindings()

secret_key, _ = SecretKey.from_armor(secret_key_armor)
assert secret_key.to_public_key().fingerprint == public_key.fingerprint
assert public_key.public_subkey_count >= 0
assert secret_key.secret_subkey_count >= 0

This is the core entry point when you want to inspect fingerprints, key IDs, OpenPGP key versions, user IDs, subkeys, self-signatures, or packet-level metadata.

2. Sign and verify messages and detached signatures

from openpgp import DetachedSignature, Message, sign_message

signed = sign_message(b"hello world", secret_key)
message, _ = Message.from_armor(signed)
message.verify(public_key)
assert message.payload_text() == "hello world"

signature = DetachedSignature.sign_binary(b"hello world", secret_key)
signature.verify(public_key, b"hello world")
info = signature.signature_info()
assert info.signature_type == "binary"
assert info.hash_algorithm == "SHA256"

For inline or detached signatures, SignatureInfo exposes the signature packet metadata that is often needed for debugging or auditing.

3. Work with cleartext signatures

from openpgp import CleartextSignedMessage, sign_cleartext_message

armored = sign_cleartext_message("hello\n-world\n", secret_key)
message, _ = CleartextSignedMessage.from_armor(armored)

assert message.signed_text() == "hello\r\n-world\r\n"
assert message.signature_count() == 1
message.verify(public_key)

4. Encrypt and decrypt OpenPGP messages

Recipient encryption:

from openpgp import Message, encrypt_message_to_recipient

recipient_encrypted = encrypt_message_to_recipient(b"secret", public_key)
recipient_message, _ = Message.from_armor(recipient_encrypted)
recipient_decrypted = recipient_message.decrypt(secret_key)
assert recipient_decrypted.payload_bytes() == b"secret"

Password encryption:

from openpgp import Message, encrypt_message_with_password

password_encrypted = encrypt_message_with_password(b"secret", "hunter2")
password_message, _ = Message.from_armor(password_encrypted)
password_decrypted = password_message.decrypt_with_password("hunter2")
assert password_decrypted.payload_text() == "secret"

5. Generate modern RFC 9580-compatible key material

from openpgp import (
    EncryptionCaps,
    KeyType,
    Message,
    PacketHeaderVersion,
    SecretKeyParamsBuilder,
    SubkeyParamsBuilder,
    UserAttribute,
    encrypt_message_to_recipient,
    sign_message,
)

secret_key = (
    SecretKeyParamsBuilder()
    .version(6)
    .created_at(1_700_000_000)
    .key_type(KeyType.ed25519())
    .can_certify(True)
    .can_sign(True)
    .packet_version(PacketHeaderVersion.new())
    .feature_seipd_v2(True)
    .primary_user_id("Me <me@example.com>")
    .preferred_symmetric_algorithms(["aes256", "aes192", "aes128"])
    .preferred_hash_algorithms(["sha256", "sha384", "sha512", "sha224"])
    .preferred_compression_algorithms(["zlib", "zip"])
    .user_attribute(UserAttribute.image_jpeg(bytes.fromhex("ffd8ffe000104a464946000101")))
    .subkey(
        SubkeyParamsBuilder()
        .version(6)
        .created_at(1_700_000_123)
        .key_type(KeyType.x25519())
        .packet_version(PacketHeaderVersion.new())
        .can_encrypt(EncryptionCaps.all())
        .build()
    )
    .build()
    .generate()
)

public_key = secret_key.to_public_key()
secret_key.verify_bindings()
public_key.verify_bindings()

assert secret_key.version == 6
assert public_key.public_key_algorithm == "ed25519"
assert public_key.public_params.kind == "ed25519"
assert public_key.public_params.curve == "ed25519"
assert public_key.packet_version == PacketHeaderVersion.new()

signed = sign_message(b"generated payload", secret_key)
message, _ = Message.from_armor(signed)
message.verify(public_key)
assert message.payload_bytes() == b"generated payload"

encrypted = encrypt_message_to_recipient(b"secret", public_key)
encrypted_message, _ = Message.from_armor(encrypted)
assert encrypted_message.decrypt(secret_key).payload_bytes() == b"secret"

Use PacketHeaderVersion.old() when you need legacy packet-header framing for round-tripping older transferable key material.

6. Customize secret-key S2K protection for generated keys

from openpgp import (
    EncryptionCaps,
    KeyType,
    S2kParams,
    SecretKeyParamsBuilder,
    StringToKey,
    SubkeyParamsBuilder,
)

secret_key = (
    SecretKeyParamsBuilder()
    .version(6)
    .key_type(KeyType.ed25519())
    .can_certify(True)
    .can_sign(True)
    .primary_user_id("Me <me@example.com>")
    .passphrase("hunter2")
    .s2k(
        S2kParams.aead(
            "aes256",
            "ocb",
            StringToKey.argon2(3, 4, 16),
        )
    )
    .subkey(
        SubkeyParamsBuilder()
        .version(6)
        .key_type(KeyType.x25519())
        .can_encrypt(EncryptionCaps.all())
        .passphrase("hunter2")
        .s2k(
            S2kParams.cfb(
                "aes128",
                StringToKey.iterated("sha256", 96),
            )
        )
        .build()
    )
    .build()
    .generate()
)

primary_s2k = secret_key.primary_secret_s2k()
assert primary_s2k.usage == "aead"
assert primary_s2k.aead_algorithm == "ocb"
assert primary_s2k.string_to_key is not None
assert primary_s2k.string_to_key.kind == "argon2"

Feature overview

The current binding surface covers these areas:

  • parse ASCII-armored or binary transferable public keys,
  • parse ASCII-armored or binary transferable secret keys,
  • inspect fingerprints, key IDs, versions, creation times, algorithms, subkeys, user IDs, and S2K settings,
  • inspect direct-key signatures, user-ID signatures, subkey bindings, embedded primary-key-binding signatures, key flags, features, and preferred algorithm lists,
  • serialize keys back to binary packets or ASCII armor,
  • generate new secret/public keys with typed builder APIs,
  • parse OpenPGP messages into reusable Message objects,
  • inspect message metadata and read signed, literal, or compressed payloads,
  • decrypt encrypted messages with a secret key or password,
  • create password-encrypted or recipient-encrypted messages,
  • parse, serialize, create, and verify detached signatures,
  • parse, create, serialize, and verify cleartext signed messages,
  • inspect and selectively verify multi-signed inline messages,
  • verify key self-signatures and bindings,
  • convert a parsed secret key to its public-key view.

Benchmarks

Median runtime graph (1 KiB payload, lower is better)

Grouped benchmark chart for the shared workflows

rpgp-py is substantially faster: roughly 9x–71x faster for key parsing and 25x–48x faster for the sign/verify and recipient-encryption loops.

Password-encryption benchmark

Grouped benchmark chart for password encryption and decryption

This result is shown separately: rpgp-py defaults to modern SEIPDv2 + AEAD (OCB) password-protected messages, while PGPy/PGPy13 remain RFC 4880-era implementations.

Table of results

Operation rpgp-py PGPy13 PGPy
Parse armored public key 0.011 ms 0.786 ms 0.776 ms
Parse armored secret key 0.156 ms 1.473 ms 1.455 ms
Detached sign + verify 2.453 ms 61.329 ms 61.420 ms
Encrypt + decrypt to recipient 2.537 ms 122.726 ms 120.701 ms
Encrypt + decrypt with password 62.369 ms 50.346 ms 50.289 ms

Reproduction

To make that comparison reproducible, the repository now ships:

  • scripts/benchmark.py – an isolated benchmark runner,
  • docs/benchmarks/results.json – the committed raw results used below.
. "$HOME/.cargo/env"
uv run --python 3.12 python scripts/benchmark.py

Development

See the list of useful commands by running:

just

Acknowledgements

Many thanks to the rpgp contributors and maintainers for building and documenting the Rust OpenPGP implementation that powers this package.

License

This repository is distributed under the MIT License. The upstream Rust crates it wraps keep their own licenses; check their repositories and published metadata when you need to audit the full dependency chain.

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

rpgp_py-0.19.0.tar.gz (97.6 kB view details)

Uploaded Source

Built Distributions

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

rpgp_py-0.19.0-cp310-abi3-win_amd64.whl (3.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

rpgp_py-0.19.0-cp310-abi3-win32.whl (2.8 MB view details)

Uploaded CPython 3.10+Windows x86

rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_x86_64.whl (3.7 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

rpgp_py-0.19.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

rpgp_py-0.19.0-cp310-abi3-macosx_11_0_arm64.whl (3.0 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

rpgp_py-0.19.0-cp310-abi3-macosx_10_12_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

rpgp_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.4 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

Details for the file rpgp_py-0.19.0.tar.gz.

File metadata

  • Download URL: rpgp_py-0.19.0.tar.gz
  • Upload date:
  • Size: 97.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0.tar.gz
Algorithm Hash digest
SHA256 a4952fd191fc7e2c27ec93d9b872d7b66577c6e253efc5077dd4c6c1615db356
MD5 ccbd325c7141cfc37280bccdd093d83b
BLAKE2b-256 9af4328ca71f53048ce51f95a60bec5cae9597265f447924554d1bf148cdc6f3

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 c0584a8df28d246541878a5749ac3d73541358f68497cb5275971507064e2a03
MD5 655b4d0754dbeef552e4851f2ecdd2d0
BLAKE2b-256 e73b340907a1bee900f2b452019afa9d35c433cc9f3cef5ec05b088a062739ee

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-win32.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-win32.whl
  • Upload date:
  • Size: 2.8 MB
  • Tags: CPython 3.10+, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-win32.whl
Algorithm Hash digest
SHA256 8f3a6a6ed40bf01deae8d01aed10275015e962e23e1050f56d83a01f7261aa80
MD5 ed61631afb82e420afe65b9dec04273b
BLAKE2b-256 13960f6c75849c42cf5171f38e4e66eaba0280c4cbed1c344c648744b28d0f32

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 22367a931111a135b8d700d885bfb3f0bf81d5e94dbeafb8f71a7aa63c819098
MD5 a6789c717056d2d2a56cca9f7ea46e17
BLAKE2b-256 aa2a7b2a46372952e019c161fbc8a3ae162f759b78baed84a62f7b3d55c516c1

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 3.5 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 c4ed56defafbd48ddc90d7c10bbb95eadc3f68ac306981efe96e7edb76c686fd
MD5 5b93fac656075b8a43c191a222707236
BLAKE2b-256 285bc5fbe73ea44fbb5ab46592bfa7b95952c8e60246a4d55c8bd8c2751e86b1

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9aa953c95cf2ed8ac077a84efdcaf1176457c02ad460de443ce7ded6735f3c05
MD5 4004a78c658899f8b9b755a2fa8c0373
BLAKE2b-256 9a9b37cde11455e8c49de9d6a85b28a83b9415d733ac6468a0afa4faad9dff17

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c4312fa3a0a8d7c408019eb1953fbfec833d331442f9877c71d87f02265d5d52
MD5 d0dd9645eef0d2d470f614c87ed2b2c0
BLAKE2b-256 022906d00ebdd5f2cc935487e25b51df74fd6daebed4d8102a801fb6f1d32749

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 3.2 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2a365463be40f84d79dcaef306610e8cbaa7312730e6422cb204794c8fbc52a5
MD5 539495b9fdd45b24fba157d488a00252
BLAKE2b-256 9c970ab6a4e59f284e451a938bd09004d5b2417477feceb328c67f4b0a63a8e2

See more details on using hashes here.

File details

Details for the file rpgp_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: rpgp_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 3.4 MB
  • Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpgp_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e861f254b29b603164942b5472f4775279c29051dc354ede32d5569c64a16991
MD5 5dcbea1916d47064684752ba185f84d4
BLAKE2b-256 c5144c361eb93b5ad30aa3fe25e7c5e7e678aa53133bd6f41a027075ea626f64

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