Skip to main content

Provides OpenPGP facilities using Sequoia-PGP library

Project description

PySequoia

PyPI version PyPI Downloads CI

This library provides OpenPGP facilities in Python through the Sequoia PGP library. If you need to work with encryption and digital signatures using an IETF standardized protocol, this package is for you!

Note: This is a work in progress. The API is not stable!

Building

set -euxo pipefail
python -m venv .env
source .env/bin/activate
pip install maturin
maturin develop

Installing

PySequoia can be installed through pip:

pip install pysequoia

Note that since pysequoia is implemented largely in Rust, a Rust toolchain is necessary for the installation to succeed.

Testing

This entire document is used for end-to-end integration tests that exercise the package's API surface.

The tests assume that these keys and cards exist:

# generate a key with password
gpg --batch --pinentry-mode loopback --passphrase hunter22 --quick-gen-key passwd@example.com
gpg --batch --pinentry-mode loopback --passphrase hunter22 --export-secret-key passwd@example.com > passwd.pgp

# generate a key without password
gpg --batch --pinentry-mode loopback --passphrase '' --quick-gen-key no-passwd@example.com future-default
gpg --batch --pinentry-mode loopback --passphrase '' --export-secret-key no-passwd@example.com > no-passwd.pgp

# initialize dummy OpenPGP Card
sh /start.sh
echo 12345678 > pin
CARD_ADMIN="opgpcard admin --card 0000:00000000 --admin-pin pin"
$CARD_ADMIN import full-key.asc
$CARD_ADMIN name "John Doe"
$CARD_ADMIN url "https://example.com/key.pgp"
$CARD_ADMIN touch --key SIG --policy Fixed
$CARD_ADMIN touch --key DEC --policy Off
$CARD_ADMIN touch --key AUT --policy Fixed

Functions

All examples assume that these basic classes have been imported:

from pysequoia import Cert

sign

Signs data and returns armored output:

from pysequoia import sign

s = Cert.from_file("signing-key.asc")
signed = sign(s.secrets.signer(), "data to be signed".encode("utf8"))
print(f"Signed data: {signed}")
assert "PGP MESSAGE" in str(signed)

verify

Verifies signed data and returns verified data:

from pysequoia import verify

# sign some data
signing_key = Cert.from_file("signing-key.asc")
signed = sign(s.secrets.signer(), "data to be signed".encode("utf8"))

def get_certs(key_ids):
  # key_ids is an array of required signing keys
  print(f"For verification, we need these keys: {key_ids}")
  return [signing_key]

# verify the data
result = verify(signed, get_certs)
assert result.bytes.decode("utf8") == "data to be signed"

# let's check the valid signature's certificate and signing subkey fingerprints
assert result.valid_sigs[0].certificate == "afcf5405e8f49dbcd5dc548a86375b854b86acf9"
assert result.valid_sigs[0].signing_key == "afcf5405e8f49dbcd5dc548a86375b854b86acf9"

The function that returns certificates (here get_certs) may return more certificates than necessary.

verify succeeds if at least one correct signature has been made by any of the certificates supplied. If you need more advanced policies they can be implemented by inspecting the valid_sigs property.

encrypt

Signs and encrypts a string to one or more recipients:

from pysequoia import encrypt

s = Cert.from_file("passwd.pgp")
r = Cert.from_bytes(open("wiktor.asc", "rb").read())
bytes = "content to encrypt".encode("utf8")
encrypted = encrypt(signer = s.secrets.signer("hunter22"), recipients = [r], bytes = bytes).decode("utf8")
print(f"Encrypted data: {encrypted}")

The signer argument is optional and when omitted the function will return an unsigned (but encrypted) message.

decrypt

Decrypts plain data:

from pysequoia import decrypt

sender = Cert.from_file("no-passwd.pgp")
receiver = Cert.from_file("passwd.pgp")

content = "Red Green Blue"

encrypted = encrypt(recipients = [receiver], bytes = content.encode("utf8"))

decrypted = decrypt(decryptor = receiver.secrets.decryptor("hunter22"), bytes = encrypted)

assert content == decrypted.bytes.decode("utf8");

# this message did not contain any valid signatures
assert len(decrypted.valid_sigs) == 0

Decrypt can also verify signatures while decrypting:

from pysequoia import decrypt

sender = Cert.from_file("no-passwd.pgp")
receiver = Cert.from_file("passwd.pgp")

content = "Red Green Blue"

encrypted = encrypt(signer = sender.secrets.signer(), recipients = [receiver], bytes = content.encode("utf8"))

def get_certs(key_ids):
  print(f"For verification after decryption, we need these keys: {key_ids}")
  return [sender]

decrypted = decrypt(decryptor = receiver.secrets.decryptor("hunter22"), bytes = encrypted, store = get_certs)

assert content == decrypted.bytes.decode("utf8");

# let's check the valid signature's certificate and signing subkey fingerprints
assert decrypted.valid_sigs[0].certificate == sender.fingerprint
assert decrypted.valid_sigs[0].signing_key == sender.fingerprint

Here, the same remarks as to verify also apply.

Certificates

The Cert class represents one OpenPGP certificate (commonly called a "public key").

This package additionally verifies the certificate using Sequoia PGP's StandardPolicy. This means that certificates using weak cryptography can fail to load, or present a different view than in other OpenPGP software (e.g. if a User ID uses SHA-1 in its back-signature, it may be missing from the list of User IDs returned by this package).

Certificates have two forms, one is ASCII armored and one is raw bytes:

cert = Cert.generate("Test <test@example.com>")

print(f"Armored cert: {cert}")
print(f"Bytes of the cert: {cert.bytes()}")

Parsing

Certificates can be parsed from files (Cert.from_file) or bytes in memory (Cert.from_bytes).

cert1 = Cert.generate("Test <test@example.com>")
buffer = cert1.bytes()

parsed_cert = Cert.from_bytes(buffer)
assert str(parsed_cert.user_ids[0]) == "Test <test@example.com>"

They can also be picked from "keyring" files (Cert.split_file) or bytes in memory (Cert.split_bytes) which are collections of binary certificates.

cert1 = Cert.generate("Test 1 <test-1@example.com>")
cert2 = Cert.generate("Test 2 <test-2@example.com>")
cert3 = Cert.generate("Test 3 <test-3@example.com>")

buffer = cert1.bytes() + cert2.bytes() + cert3.bytes()
certs = Cert.split_bytes(buffer)
assert len(certs) == 3

generate

Creates a new general purpose key with a given User ID:

alice = Cert.generate("Alice <alice@example.com>")
fpr = alice.fingerprint
print(f"Generated cert with fingerprint {fpr}:\n{alice}")

Multiple User IDs can be passed as a list to the generate function:

cert = Cert.generate(user_ids = ["First", "Second", "Third"])
assert len(cert.user_ids) == 3

Newly generated certificates are usable in both encryption and signing contexts:

alice = Cert.generate("Alice <alice@example.com>")
bob = Cert.generate("Bob <bob@example.com>")

bytes = "content to encrypt".encode("utf8")

encrypted = encrypt(signer = alice.secrets.signer(), recipients = [bob], bytes = bytes)
print(f"Encrypted data: {encrypted}")

merge

Merges packets from a new version into an old version of a certificate:

old = Cert.from_file("wiktor.asc")
new = Cert.from_file("wiktor-fresh.asc")
merged = old.merge(new)

User IDs

Listing existing User IDs:

cert = Cert.from_file("wiktor.asc")
user_id = cert.user_ids[0]
assert str(user_id).startswith("Wiktor Kwapisiewicz")

Adding new User IDs:

cert = Cert.generate("Alice <alice@example.com>")
assert len(cert.user_ids) == 1;

cert = cert.add_user_id(value = "Alice <alice@company.invalid>", certifier = cert.secrets.certifier())

assert len(cert.user_ids) == 2;

Revoking User IDs:

cert = Cert.generate("Bob <bob@example.com>")

cert = cert.add_user_id(value = "Bob <bob@company.invalid>", certifier = cert.secrets.certifier())
assert len(cert.user_ids) == 2

# create User ID revocation
revocation = cert.revoke_user_id(user_id = cert.user_ids[1], certifier = cert.secrets.certifier())

# merge the revocation with the cert
cert = Cert.from_bytes(cert.bytes() + revocation.bytes())
assert len(cert.user_ids) == 1

Notations

Notations are small pieces of data that can be attached to signatures (and, indirectly, to User IDs).

The following example reads and displays a Keyoxide proof URI:

cert = Cert.from_file("wiktor.asc")
user_id = cert.user_ids[0]
notation = user_id.notations[0]

assert notation.key == "proof@metacode.biz";
assert notation.value == "dns:metacode.biz?type=TXT";

Notations can also be added:

from pysequoia import Notation

cert = Cert.from_file("signing-key.asc")

# No notations initially
assert len(cert.user_ids[0].notations) == 0;

cert = cert.set_notations(cert.secrets.certifier(), [Notation("proof@metacode.biz", "dns:metacode.biz")])

# Has one notation now
print(str(cert.user_ids[0].notations))
assert len(cert.user_ids[0].notations) == 1;

# Check the notation data
notation = cert.user_ids[0].notations[0]

assert notation.key == "proof@metacode.biz";
assert notation.value == "dns:metacode.biz";

Key expiration

Certs have an expiration getter for retrieving the current key expiry time:

cert = Cert.from_file("signing-key.asc")

# Cert does not have any expiration date:
assert cert.expiration is None

cert = Cert.from_file("wiktor.asc")
# Cert expires on New Year's Eve
assert str(cert.expiration) == "2022-12-31 12:00:02+00:00"

Key expiration can also be adjusted with set_expiration:

from datetime import datetime

cert = Cert.from_file("signing-key.asc")

# Cert does not have any expiration date:
assert cert.expiration is None

# Set the expiration to some specified point in time
expiration = datetime.fromisoformat("2021-11-04T00:05:23+00:00")
cert = cert.set_expiration(expiration = expiration, certifier = cert.secrets.certifier())
assert str(cert.expiration) == "2021-11-04 00:05:23+00:00"

Key revocation

Certs can be revoked. While expiration makes the key unusable temporarily to encourage the user to refresh a copy revocation is irreversible.

cert = Cert.generate("Test Revocation <revoke@example.com>")
revocation = cert.revoke(certifier = cert.secrets.certifier())

# creating revocation signature does not revoke the key
assert not cert.is_revoked

# importing revocation signature marks the key as revoked
revoked_cert = Cert.from_bytes(cert.bytes() + revocation.bytes())
assert revoked_cert.is_revoked

Secret keys

Certificates generated through Cert.generate() contain secret keys and can be used for signing and decryption.

To avoid accidental leakage secret keys are never directly printed when the Cert is written to a string. To enable this behavior use Cert.secrets. secrets returns None on certificates which do not contain any secret key material ("public keys").

c = Cert.generate("Testing key <test@example.com>")
assert c.has_secret_keys

# by default only public parts are exported
public_parts = Cert.from_bytes(f"{c}".encode("utf8"))
assert not public_parts.has_secret_keys
assert public_parts.secrets is None

# to export secret parts use the following:
private_parts = Cert.from_bytes(f"{c.secrets}".encode("utf8"))
assert private_parts.has_secret_keys

OpenPGP Cards

There's an experimental feature allowing communication with OpenPGP Cards (like YubiKey or Nitrokey).

from pysequoia import Card

# enumerate all cards
all = Card.all()

# open card by card ident
card = Card.open("0000:00000000")

print(f"Card ident: {card.ident}")
assert card.cardholder == "John Doe"
assert card.cert_url == "https://example.com/key.pgp"

Cards provide keys property that can be used to see which keys are imported on the card:

keys = card.keys
print(f"Keys: {keys}")
assert len(keys) == 3

assert keys[0].fingerprint == "ddc3e03c91fb52ca2d95c2444566f2743ed5f382"
assert "sign" in keys[0].usage
assert keys[0].touch_required

assert keys[1].fingerprint == "689e152a7420be13dcaf2c142ac27adc1db9395e"
assert "decrypt" in keys[1].usage
assert not keys[1].touch_required

assert keys[2].fingerprint == "731fbca93ce9821347bf8e696444723371d3c650"
assert "authenticate" in keys[2].usage
assert keys[2].touch_required

Cards can be used for signing data:

signer = card.signer("123456")

signed = sign(signer, "data to be signed".encode("utf8"))
print(f"Signed data: {signed}")

As well as for decryption:

decryptor = card.decryptor("123456")

sender = Cert.from_file("passwd.pgp")
receiver = Cert.from_file("full-key.asc")

content = "Red Green Blue"

encrypted = encrypt(signer = sender.secrets.signer("hunter22"), recipients = [receiver], bytes = content.encode("utf8"))

print(f"Encrypted data: {encrypted}")

decrypted = decrypt(decryptor = decryptor, bytes = encrypted)

assert content == decrypted.bytes.decode("utf8");

Note that while this package allows using cards for signing and decryption, the provisioning process is not supported. OpenPGP card tools can be used to initialize the card.

License

This project is licensed under Apache License, Version 2.0.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the package by you shall be under the terms and conditions of this license, without any additional terms or conditions.

Sponsors

My work was supported by these generous organizations (alphabetical order):

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

pysequoia-0.1.23.tar.gz (274.1 kB view details)

Uploaded Source

Built Distributions

pysequoia-0.1.23-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp312-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.12 Windows x86-64

pysequoia-0.1.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.12 manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp312-cp312-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.12 macOS 11.0+ ARM64

pysequoia-0.1.23-cp312-cp312-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.12 macOS 10.12+ x86-64

pysequoia-0.1.23-cp311-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.11 Windows x86-64

pysequoia-0.1.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp311-cp311-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.11 macOS 11.0+ ARM64

pysequoia-0.1.23-cp311-cp311-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.11 macOS 10.12+ x86-64

pysequoia-0.1.23-cp310-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.10 Windows x86-64

pysequoia-0.1.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp310-cp310-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.10 macOS 11.0+ ARM64

pysequoia-0.1.23-cp310-cp310-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.10 macOS 10.12+ x86-64

pysequoia-0.1.23-cp39-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.9 Windows x86-64

pysequoia-0.1.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp38-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.8 Windows x86-64

pysequoia-0.1.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

pysequoia-0.1.23-cp37-none-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.7 Windows x86-64

pysequoia-0.1.23-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.7m manylinux: glibc 2.17+ x86-64

File details

Details for the file pysequoia-0.1.23.tar.gz.

File metadata

  • Download URL: pysequoia-0.1.23.tar.gz
  • Upload date:
  • Size: 274.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.5.1

File hashes

Hashes for pysequoia-0.1.23.tar.gz
Algorithm Hash digest
SHA256 8a965ed71023448206003b8186f72d44821f943d94d8d52957270e24119f3bc2
MD5 35863a8d16fcdd212c61bcc47984fb72
BLAKE2b-256 55a7631faa2a7f0e81c400967b0f8223d5d328e488c639ca5fdd6c2f0e602ad9

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d979e9e17137d143fef47f307415f4d10c1e90bab140e9ee4c416237672b909d
MD5 1c6b92059d004e30be8026d447b35f81
BLAKE2b-256 b94a3392480cc914db6a007ca43cf05dd8e083855e237602cf54f45757e62ddd

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4fc38d0e74a4a5f56cd27b1f665d56f04ab09e3c2d26dfea840967660231fe63
MD5 92fbc2178474625c1ec96b693c2054d6
BLAKE2b-256 c5822d44f1692feef9ae653a77ceeb6d4d19702a34011e4cb80beae731a54c18

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 eced2d1e3d0f47c4519edd5ea6ab533bfb1b8b86bbf4b7dfb7de5ab6c6290fbf
MD5 62bc8dddeb76c163de2471845e5dde0f
BLAKE2b-256 07aa46d3c2348d6aa16afa7a61b204a3c74acb7572ebf625ae73c34ef34dbaa3

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c872c349f0462adb25b78bce4bd656fc442989049f5eb3a3cd1e4541a5764783
MD5 8a980dce4c100041bf1535583e9ed06e
BLAKE2b-256 505609eaced497973adac3dc69242b351b28e39075c402a5d5b82b4fbc28da2d

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp312-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp312-none-win_amd64.whl
Algorithm Hash digest
SHA256 876f8d174745e8f2bd567c5952081fe77b32f83028e71f24e3c028cb76c31f91
MD5 88f98cb092a847c564d8509f4f691b96
BLAKE2b-256 3db156e399256e0d204bfc4b36fce8cb50f34830a6a1c128fa35c35b8b329f6d

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 61dd4fab75639fc5dc36c4b65d9a4d4de4a4e33012e4393cfec6fb3c15e42826
MD5 13b2eaaaebc74821f946e2d6a6aab25f
BLAKE2b-256 8a083c18e41fe22efdc5eb676cdb6b619ba04c1bdfca417a46b10d57099e89c6

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 43d24e3499e6c95ce994f49ae31f9e6917ed2cb8539d92843987bff7bbcf24cd
MD5 5475e98da8c684d19d20ebfac8d6a74a
BLAKE2b-256 70f6c43c83c48c1151df91448a6600f663db4f2d4c7d4da1f48114893c68e922

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 b15d92426563dc75a50f79099049d35c01aa5a8edc6bd9900f12dadc2a97d560
MD5 383bad82f9af6cc038ab9886d3eb52e9
BLAKE2b-256 29c61280cbfe6a70ce06fa107f0d35114bccacfea928f1ae43833a993c849e4f

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp311-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp311-none-win_amd64.whl
Algorithm Hash digest
SHA256 42493060026d1cdec937cd71b1df1d474c6812a921188d9814d59f15d5d0885c
MD5 104dc174e5b00fd6d6099d56fa2aa792
BLAKE2b-256 ff8a816ec9e7dc86eec244920448bbd4ec63c08f25080c428c2479c6ab0b9882

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9d7ae181c88066e755d92aef0a1f32b4f74425b9f35cd45e08b2a20649f7cff5
MD5 4f9d53a8f0640ff5e2382e0c1295e036
BLAKE2b-256 fa85fcee91c4267da058d4e5c0bc91ba7be61f6e39ce68eb5961d06f0d67eb24

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 46fb7ed4503e6df774abffb56e414de3a87c6bd899a7b0b8cbed5924fda79c9e
MD5 1166ab4e1a663b9b641b683de653cef7
BLAKE2b-256 0696dab2f166215dec6c6d93b0bffaa6eedb88b56e74cbe6fa83bcfe16752889

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9239d7dcddb070ffeef640e777f227b0c27d7050b4a76be05f0727724c48660b
MD5 c0818bd67608eba81930a1ecd60b5996
BLAKE2b-256 76aa2c84129b706619d0d21de14f1ad8f434cd3bb13af3a766f5ea0b0df30093

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp310-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp310-none-win_amd64.whl
Algorithm Hash digest
SHA256 bbbd5e24e2a01221802ee204b5ba7fec3edc399ba80a39091560be60200fd49b
MD5 fbe437bdfca43c33980b1527c16a0ede
BLAKE2b-256 4a7e6af99c8b9e92205476bd07711bacd7baa61d3215749cc126e13a04eb3770

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2735d1290acf483b9bebd732668f4fd66a1f96650748cc9aa34ddda3ef59a068
MD5 84a9d14454e17e8b782c6045125dd766
BLAKE2b-256 8e932b58636579d90199fb3e25840d862387471726a004b973c563ef33d88bff

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 49aa8d08359f0269cad807276dfa6005ff8f8f7e779ca06e675d9355c489f527
MD5 8f2049d8cdb8551f5c3b93ff6274d2bd
BLAKE2b-256 b1a54c2b95660f8f3f1ee4e1db90e7e465951defc5951aca7baa2f0d8aaac26e

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp310-cp310-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp310-cp310-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1120c8868dfb81814a1167746d11c1a95b3cae58fa3f24cfbfaf579c03491f21
MD5 7965f2a6ee66603315f1642afff533aa
BLAKE2b-256 17521a83d9c6325499c9943c69166b45dfd72b421fea04ac7e6dec9c2bffedeb

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp39-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp39-none-win_amd64.whl
Algorithm Hash digest
SHA256 c674ec3e3916ac748f72c6b97d628dd60d6f877c12257232b3abd845a4ec9ff9
MD5 532216db650e0c89d0a03eb0cc2c8153
BLAKE2b-256 b692f1ec943f9c7cbbffba9761d247814a88828a2adb4c19d7be8df134a46fbf

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5694d7ea2bf0c850628d1b8586e3474e1cb725d077d0a0c055e0d884d149db7c
MD5 5d945f0445c5001813da189c77062ec1
BLAKE2b-256 d5fc906630b34e6587f08d602039e5ca0b4358d7775dcee9a6669755cb393ad1

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp38-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp38-none-win_amd64.whl
Algorithm Hash digest
SHA256 80e2d016c18889e07c916dc1af59b1c008ea0404a7e68da7ad17df3d0ef6936b
MD5 92b76c9583b785464257fd7468eb981a
BLAKE2b-256 21522fcf9f7e96c9645b4f36217df019634c522b88cbaa6e21cd05c4f4eba9ec

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dffd72846906a4dbe28787bd216afae97f06c0d26a140d2c497a9d7bb11c812f
MD5 23e3ce7a7632551621a22a1f17a562fa
BLAKE2b-256 5193e98b30400ecd19f4c198df308ce8fe0503c456f7ffd24db9784c3732bcb4

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp37-none-win_amd64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp37-none-win_amd64.whl
Algorithm Hash digest
SHA256 5165ae2817aaffa02a67fac9bdcaf1bd536e8452e60de00c4a323c83ffc2e219
MD5 3c9c641df590325daf4e29b6b9f63724
BLAKE2b-256 c18b8481298bde5ce9ff181dd7d7e2e6c329dda405c8a33b39c1fb5dbe988ebf

See more details on using hashes here.

File details

Details for the file pysequoia-0.1.23-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pysequoia-0.1.23-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 267903dd9f7ed0d178dc38a3cf25305e562336fdd6573a3ff42ae13dba3e2231
MD5 b6b12a36da483d14acc8ae9795719696
BLAKE2b-256 9643b4f67cc9c8f5d70d5802e453fc5bc4da16e996d80d8e704648e4597aad90

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page