bcl module

Python library that provides a simple interface for symmetric (i.e., secret-key) and asymmetric (i.e., public-key) encryption/decryption primitives.

This library exports a number of classes (derived from bytes) for representing keys, nonces, plaintexts, and ciphertexts. It also exports two classes symmetric and asymmetric that have only static methods (for key generation and encryption/decryption).

class bcl.bcl.raw[source]

Bases: bytes

Wrapper class for a raw bytes-like object that represents a key, nonce, plaintext, or ciphertext. The derived classes secret, public, nonce, plain, and cipher all inherit the methods defined in this class.

>>> s = secret.from_base64('1P3mjNnadofjTUkzTmipYl+xdo9z/EaGLbWcJ8MAPBQ=')
>>> s.hex()
'd4fde68cd9da7687e34d49334e68a9625fb1768f73fc46862db59c27c3003c14'
>>> n = nonce.from_base64('JVN9IKBLZi3lEq/eDgkV+y6n4v7x2edI')
>>> c = symmetric.encrypt(s, 'abc'.encode(), n)
>>> c.to_base64()
'JVN9IKBLZi3lEq/eDgkV+y6n4v7x2edI9dvFXD+om1dHB6UUCt1y4BqrBw=='
classmethod from_base64(s: str) bcl.bcl.raw[source]

Convert Base64 UTF-8 string representation of a raw value.

to_base64() str[source]

Convert to equivalent Base64 UTF-8 string representation.

class bcl.bcl.nonce(argument: Optional[Union[bytes, bytearray, int]] = None)[source]

Bases: bcl.bcl.raw

Wrapper class for a bytes-like object that represents a nonce.

>>> n = nonce()
>>> n = nonce(bytes(n))
>>> isinstance(n, nonce) and isinstance(n, bytes)
True

While the constructor works like the constructor for bytes-like objects in also accepting an integer argument, an instance can only have the exact length permitted for a nonce.

>>> nonce(nonce.length).hex()
'000000000000000000000000000000000000000000000000'

The constructor for this class checks that the supplied bytes-like object or integer argument satisfy the conditions for a valid nonce.

>>> nonce('abc')
Traceback (most recent call last):
  ...
TypeError: nonce constructor argument must be a bytes-like object or an integer
>>> try:
...     nonce(bytes([1, 2, 3]))
... except ValueError as e:
...     str(e) == 'nonce must have exactly '  + str(nonce.length) + ' bytes'
True
>>> try:
...     nonce(123)
... except ValueError as e:
...     str(e) == 'nonce must have exactly '  + str(nonce.length) + ' bytes'
True
length: int

Length (in number of bytes) of nonce instances.

static __new__(cls, argument: Optional[Union[bytes, bytearray, int]] = None) bcl.bcl.nonce[source]

Create a nonce object.

class bcl.bcl.key[source]

Bases: bcl.bcl.raw

Wrapper class for a bytes-like object that represents a key. The derived classes secret and public inherit the methods defined in this class.

Any key objects (including instances of classes derived from key) have a few features and behaviors that distinguish them from bytes-like objects.

  • Comparison of keys (using the built-in == and != operators via the __eq__ and __ne__ methods) is performed in constant time.

  • Keys of different types are not equivalent even if their binary representation is identical.

    >>> b = 'd6vGTIjbxZyMolCW+/p1QFF5hjsYC5Q4x07s+RIMKK8='
    >>> secret.from_base64(b) == public.from_base64(b)
    False
    >>> secret.from_base64(b) != public.from_base64(b)
    True
    
  • Consistent with the above property, keys having different classes are distinct when used as keys or items within containers.

    >>> b = 'd6vGTIjbxZyMolCW+/p1QFF5hjsYC5Q4x07s+RIMKK8='
    >>> len({secret.from_base64(b), public.from_base64(b)})
    2
    
__eq__(other: bcl.bcl.key) bool[source]

Compare two keys (including their subclass). The portion of the method that compares byte values runs in constant time.

>>> key(bytes([0] * 32)) == key(bytes([1] * 32))
False
>>> key(bytes([1] * 32)) == key(bytes([1] * 32))
True
>>> secret(bytes([0] * 32)) == public(bytes([0] * 32))
False
__ne__(other: bcl.bcl.key) bool[source]

Compare two keys (including their subclass). The portion of the method that compares byte values runs in constant time.

>>> key(bytes([0] * 32)) != key(bytes([1] * 32))
True
>>> key(bytes([1] * 32)) != key(bytes([1] * 32))
False
>>> secret(bytes([0] * 32)) != public(bytes([0] * 32))
True
class bcl.bcl.secret(argument: Optional[Union[bytes, bytearray, int]] = None)[source]

Bases: bcl.bcl.key

Wrapper class for a bytes-like object that represents a secret key. The constructor for this class can be used to generate an instance of a secret key or to convert a bytes-like object into a secret key.

>>> s = secret()
>>> s = secret(bytes(s))
>>> isinstance(s, secret) and isinstance(s, key)and isinstance(s, bytes)
True

While the constructor works like the constructor for bytes-like objects in also accepting an integer argument, an instance can only have the exact length permitted for a secret key.

>>> secret(secret.length).hex()
'0000000000000000000000000000000000000000000000000000000000000000'

The constructor for this class checks that the supplied bytes-like object or integer argument satisfy the conditions for a valid secret key.

>>> secret('abc')
Traceback (most recent call last):
  ...
TypeError: secret key constructor argument must be a bytes-like object or an integer
>>> try:
...     secret(bytes([1, 2, 3]))
... except ValueError as e:
...     str(e) == 'secret key must have exactly '  + str(secret.length) + ' bytes'
True
>>> try:
...     secret(123)
... except ValueError as e:
...     str(e) == 'secret key must have exactly '  + str(secret.length) + ' bytes'
True

The methods symmetric.encrypt, symmetric.decrypt, and asymmetric.decrypt only accept key parameters that are objects of this class.

length: int

Length (in number of bytes) of secret key instances.

static __new__(cls, argument: Optional[Union[bytes, bytearray, int]] = None) bcl.bcl.secret[source]

Create a secret key object.

class bcl.bcl.public(argument: Optional[Union[bytes, bytearray, int]] = None)[source]

Bases: bcl.bcl.key

Wrapper class for a bytes-like object that represents a public key. The constructor for this class can be used to generate an instance of a public key or to convert a bytes-like object into a public key.

>>> p = public()
>>> p = public(bytes(p))
>>> isinstance(p, public) and isinstance(p, key)and isinstance(p, bytes)
True

While the constructor works like the constructor for bytes-like objects in also accepting an integer argument, an instance can only have the exact length permitted for a public key.

>>> public(public.length).hex()
'0000000000000000000000000000000000000000000000000000000000000000'

The constructor for this class checks that the supplied bytes-like object or integer argument satisfy the conditions for a valid public key.

>>> public('abc')
Traceback (most recent call last):
  ...
TypeError: public key constructor argument must be a bytes-like object or an integer
>>> try:
...     public(bytes([1, 2, 3]))
... except ValueError as e:
...     length = crypto_box_PUBLICKEYBYTES
...     str(e) == 'public key must have exactly '  + str(length) + ' bytes'
True
>>> try:
...     public(123)
... except ValueError as e:
...     length = crypto_box_PUBLICKEYBYTES
...     str(e) == 'public key must have exactly '  + str(length) + ' bytes'
True

The method asymmetric.encrypt only accepts key parameters that are objects of this class.

length: int

Length (in number of bytes) of public key instances.

static __new__(cls, argument: Optional[Union[bytes, bytearray, int]] = None) bcl.bcl.public[source]

Create a public key object.

class bcl.bcl.plain[source]

Bases: bcl.bcl.raw

Wrapper class for a bytes-like object that represents a plaintext.

>>> x = plain(os.urandom(1024))
>>> x == plain.from_base64(x.to_base64())
True

The methods symmetric.decrypt and asymmetric.decrypt return objects of this class.

class bcl.bcl.cipher[source]

Bases: bcl.bcl.raw

Wrapper class for a bytes-like object that represents a ciphertext.

>>> c = cipher(os.urandom(1024))
>>> c == cipher.from_base64(c.to_base64())
True

The methods symmetric.encrypt and asymmetric.encrypt return objects of this class, and the methods symmetric.decrypt and asymmetric.decrypt can only be applied to objects of this class.

class bcl.bcl.symmetric[source]

Bases: object

Symmetric (i.e., secret-key) encryption/decryption primitives. This class encapsulates only static methods and should not be instantiated.

>>> x = 'abc'.encode()
>>> s = symmetric.secret()
>>> isinstance(s, key) and isinstance(s, secret)
True
>>> s == secret.from_base64(s.to_base64())
True
>>> c = symmetric.encrypt(s, x)
>>> isinstance(c, raw) and isinstance(c, cipher)
True
>>> c == cipher.from_base64(c.to_base64())
True
>>> symmetric.decrypt(s, c) == x
True
>>> isinstance(symmetric.decrypt(s, c), plain)
True

Encryption is non-deterministic if no nonce parameter is supplied.

>>> symmetric.encrypt(s, x) == symmetric.encrypt(s, x)
False

Deterministic encryption is possible by supplying a nonce parameter.

>>> n = nonce()
>>> symmetric.encrypt(s, x, n) == symmetric.encrypt(s, x, n)
True
static secret() bcl.bcl.secret[source]

Generate a secret key.

static encrypt(secret_key: bcl.bcl.secret, plaintext: Union[bcl.bcl.plain, bytes, bytearray], noncetext: Optional[bcl.bcl.nonce] = None) bcl.bcl.cipher[source]

Encrypt a plaintext (a bytes-like object) using the supplied secret key (and an optional nonce, if applicable).

>>> m = plain(bytes([1, 2, 3]))
>>> s = symmetric.secret()
>>> c = symmetric.encrypt(s, m)
>>> m == symmetric.decrypt(s, c)
True

All parameters supplied to this method must have appropriate types.

>>> c = symmetric.encrypt(bytes([0, 0, 0]), m)
Traceback (most recent call last):
  ...
TypeError: can only encrypt using a symmetric secret key
>>> c = symmetric.encrypt(s, 'abc')
Traceback (most recent call last):
  ...
TypeError: can only encrypt a plaintext object or bytes-like object
>>> c = symmetric.encrypt(s, m, bytes([0, 0, 0]))
Traceback (most recent call last):
  ...
TypeError: nonce parameter must be a nonce object
static decrypt(secret_key: bcl.bcl.secret, ciphertext: bcl.bcl.cipher) bcl.bcl.plain[source]

Decrypt a ciphertext (an instance of cipher) using the supplied secret key.

>>> m = plain(bytes([1, 2, 3]))
>>> s = symmetric.secret()
>>> c = symmetric.encrypt(s, m)
>>> m == symmetric.decrypt(s, c)
True

All parameters supplied to this method must have appropriate types.

>>> c = symmetric.decrypt(bytes([0, 0, 0]), m)
Traceback (most recent call last):
  ...
TypeError: can only decrypt using a symmetric secret key
>>> c = symmetric.decrypt(s, 'abc')
Traceback (most recent call last):
  ...
TypeError: can only decrypt a ciphertext
>>> symmetric.decrypt(s, cipher(c + bytes([0, 0, 0])))
Traceback (most recent call last):
  ...
RuntimeError: ciphertext failed verification
class bcl.bcl.asymmetric[source]

Bases: object

Asymmetric (i.e., public-key) encryption/decryption primitives. This class encapsulates only static methods and should not be instantiated.

>>> x = 'abc'.encode()
>>> s = asymmetric.secret()
>>> isinstance(s, key) and isinstance(s, secret)
True
>>> p = asymmetric.public(s)
>>> isinstance(p, key) and isinstance(p, public)
True
>>> p == public.from_base64(p.to_base64())
True
>>> c = asymmetric.encrypt(p, x)
>>> asymmetric.decrypt(s, c) == x
True
static secret() bcl.bcl.secret[source]

Generate a secret key.

>>> s = symmetric.secret()
>>> isinstance(s, key) and isinstance(s, secret)
True
static public(secret_key: bcl.bcl.secret) bcl.bcl.public[source]

Generate a public key using a secret key.

>>> s = asymmetric.secret()
>>> p = asymmetric.public(s)
>>> isinstance(p, key) and isinstance(p, public)
True
static encrypt(public_key: bcl.bcl.public, plaintext: Union[bcl.bcl.plain, bytes, bytearray]) bcl.bcl.cipher[source]

Encrypt a plaintext (any bytes-like object) using the supplied public key.

>>> m = plain(bytes([1, 2, 3]))
>>> s = asymmetric.secret()
>>> p = asymmetric.public(s)
>>> c = asymmetric.encrypt(p, m)
>>> m == asymmetric.decrypt(s, c)
True

All parameters supplied to this method must have appropriate types.

>>> c = asymmetric.encrypt(s, m)
Traceback (most recent call last):
  ...
TypeError: can only encrypt using a public key
>>> c = asymmetric.encrypt(p, 'abc')
Traceback (most recent call last):
  ...
TypeError: can only encrypt a plaintext object or bytes-like object
static decrypt(secret_key: bcl.bcl.secret, ciphertext: bcl.bcl.cipher) bcl.bcl.plain[source]

Decrypt a ciphertext (an instance of cipher) using the supplied secret key.

>>> m = plain(bytes([1, 2, 3]))
>>> s = asymmetric.secret()
>>> p = asymmetric.public(s)
>>> c = asymmetric.encrypt(p, m)
>>> m == asymmetric.decrypt(s, c)
True

All parameters supplied to this method must have appropriate types.

>>> c = asymmetric.decrypt(p, m)
Traceback (most recent call last):
  ...
TypeError: can only decrypt using an asymmetric secret key
>>> c = asymmetric.decrypt(s, 'abc')
Traceback (most recent call last):
  ...
TypeError: can only decrypt a ciphertext
>>> try:
...     asymmetric.decrypt(s, cipher(bytes([0])))
... except ValueError as e:
...     length = crypto_box_SEALBYTES
...     str(e) == 'asymmetric ciphertext must have at least '  + str(length) + ' bytes'
True