Skip to main content

A modern asymmetric encryption scheme.

Project description

Psypher - A modern asymmetric encryption scheme.

Psypher is a modern cryptography module providing high-level security recipes with simple interfaces. It is built for securing communication through insecure channels.

Installation

This module is available via PyPI:

$ pip install psypher

If you want to use the source code directly, make sure you install the cryptography dependency.

$ pip install cryptography

Basic Usage

All the interface Psypher provides is accessible through the Scheme interface. Use the default static method to obtain an instance of the default scheme (curve25519-chacha20poly1305).

>>> from psypher import Scheme
>>> s1, s2 = Scheme.default(), Scheme.default()
>>> s1.__class__.__name__
'Curve25519ChaCha20Poly1305Scheme'

The private and public key is accessible with the privateKey and publicKey properties. The basic usage of these keys are shown below.

# key export (also suitable for public keys)
key1 = s1.privateKey.export() # bytes
key2 = s2.privateKey.exportJson() # str
# key import (also suitable for public keys)
psypher.secret.Curve25519PrivateKey.importKey(key1)
psypher.secret.Curve25519PrivateKey.importJson(key2)
# encrypted key export (private keys only)
key1 = s1.privateKey.secureExport(b'password')
key2 = s2.privateKey.secureExportJson(b'password')
# encrypted key import (private keys only)
psypher.secret.Curve25519PrivateKey.importKey(key1, b'password')
psypher.secret.Curve25519PrivateKey.importJson(key2, b'password')
# key fingerprint (public keys only)
print(s1.publicKey.digest)

When using encrypted key export, the default behaviours is to derive the wrapping key from the password using PBKDF2. However, you can specify derive=False to disable this kind of behaviour and to use your material as key directly.

IPublicKey.digest is a cached property, so it might not behave as you expect it to be. See the Advanced Usage / Cache Mode to learn more.

The export, exportJson methods is also defined on the scheme as a shortcut to the public key. Use the receive and receiveJson methods to receive remote key from peers:

>>> s1.receive(s2.export())
>>> s2.receiveJson(s1.exportJson())

Use the shareSecret property to calculate the negotiated shared secret. This property is CACHED, that means it only evaluates once. For detailed information about caching, please navigate to the Advanced Usage / Cache Mode section.

>>> s1.sharedSecret == s2.sharedSecret
True

To generate the digital signature of specific message, use the sign method. Call verify on the other side to verify the message. You can only verify messages signed by your peer. The signature generated is deterministic.

>>> data = b'My telephone number is 123456!'
>>> signature = s1.sign(data)
>>> s2.verify(data, signature)
True
>>> s2.verify(b'My telephone number is 654321!', signature)
False

If you just want to check the integrity of your message therefore don't require such cryptographical strength of your signature, or if you need a shorter signature value, the integritySign method may suits you. It uses HMAC to sign the message, producing only 32 bytes output. Likewise, the integrityVerify method can be used to verify this signature:

>>> data = b'My telephone number is 123456!'
>>> signature = s1.integritySign(data)
>>> s2.integrityVerify(data, signature)
True
>>> s2.integrityVerify(b'My telephone number is 654321!', signature)
False

To encrypt your message, use the encrypt method. Call decrypt on the other side to decrypt your message. You can only decrypt messages encrypted by your peer. The ciphertext is different each time you call encrypt, even if you are encrypting the same message.

>>> data = b'The darkest secret lies here.'
>>> ciphertext = s2.encrypt(data)
>>> s1.decrypt(ciphertext) == data
True
>>> from psypher.errors import InvalidCiphertext
>>> try:
...     s1.decrypt(b'This is absolutely illegal')
... except InvalidCiphertext:
...     pass

Advanced Usage

Below are some complicated usages of this module beyond the normal usage.

Cache Mode

The cache mode is enabled by default, which means certain methods or property will only be calculated once and cached. Cached items includes IPublicKey.digest and <KeyPairClass>.sharedSecret. You can disable caching globally with the following statements:

>>> from psypher import cache
>>> cache.setenabled(False)

You can access the cache object through the unbound version of the method or property. Here are some usages:

# query
from psypher.secret import IPublicKey
IPublicKey.digest.fget.enabled
# partially disable
IPublicKey.digest.fget.enabled = False
EccKeyPair.sharedSecret.fget.enabled = False
# clear cache for all instances
Curve25519KeyPair.sharedSecret.fget.clearCache()

If you disabled the cache globally, the enabled property will return False on every instance, even if you set it to True.

Customize Scheme

The scheme object is located in the psypher.cipher module. A scheme is consisted of two parts - the key pair and the symmetric encryptor. You could customize these parts.

The module provides two built-in schemes: Curve15519ChaCha20Poly1305Scheme, which is the default, and Secp256k1AesGcmScheme, which is recommended to use in commercial circumstances. These schemes produces the ciphertext with similar structure:

Curve25519ChaCha20Poly1305

Input: plain text (X bytes)

Output:

EphKey HkdfSalt AeadNonce Ciphertext AeadTag Total
32 bytes 16 bytes 12 bytes X bytes 16 bytes 76+X bytes

Secp256k1AesGcm:

Input: plain text (X bytes)

Output:

EphKey HkdfSalt AeadNonce Ciphertext AeadTag Total
65 bytes 16 bytes 12 bytes X bytes 16 bytes 109+X bytes

These schemes can be found in the psypher.scheme module:

>>> from psypher.scheme import Secp256k1AesGcmScheme
>>> s1 = Secp256k1AesGcmScheme.generate()

You can also customize the schemes:

>>> from psypher.cipher import AesGcmEncryptor
>>> from psypher.secret import Curve25519KeyPair
>>> from psypher.scheme import Scheme
>>> s1 = Scheme(Curve25519KeyPair.generate(), AesGcmEncryptor)

To fully build your own customized scheme, you may want to inherit from the IKeyPair class. You must implement the IPrivateKey and IPublicKey interfaces too. The IEncryptor interface however, is a static interface, only containing static methods. For more details, please see the source code.

Support & License

This module is built on the pyca/cryptography library. See their Github Repository.

This software is licensed under the MIT License. For more information, see the License Text.

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

psypher-1.0.0.tar.gz (12.3 kB view hashes)

Uploaded Source

Built Distribution

psypher-1.0.0-py3-none-any.whl (11.8 kB view hashes)

Uploaded Python 3

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