Adafruit CircuitPython RSA API

RSA module

Module for calculating large primes, and RSA encryption, decryption, signing and verification. Includes generating public and private keys.

WARNING: This implementation does not use compression of the cleartext input to prevent repetitions, or other common security improvements. Use with care.

adafruit_rsa._compat

Python compatibility wrappers.

adafruit_rsa._compat.byte(num: int) bytes

Converts a number between 0 and 255 (both inclusive) to a base-256 (byte) representation.

Use it as a replacement for chr where you are expecting a byte because this will work on all current versions of Python:

:param int num: An unsigned integer between 0 and 255 (both inclusive).
:return: A single byte.
adafruit_rsa._compat.get_word_alignment(num: int, force_arch: int = 64, _machine_word_size: Literal[64, 32] = 64) Tuple[int, int, int, str]

Returns alignment details for the given number based on the platform Python is running on.

Parameters:
  • num (int) – Unsigned integer number.

  • force_arch (int) – If you don’t want to use 64-bit unsigned chunks, set this to anything other than 64. 32-bit chunks will be preferred then. Default 64 will be used when on a 64-bit machine.

  • _machine_word_size (int) – (Internal) The machine word size used for alignment.

Returns:

4-tuple:

(word_bits, word_bytes,
 max_uint, packing_format_type)

adafruit_rsa._compat.is_bytes(obj: Any) bool

Determines whether the given value is a byte string.

Parameters:

obj – The value to test.

Returns:

True if obj is a byte string; False otherwise.

adafruit_rsa._compat.is_integer(obj: Any) bool

Determines whether the given value is an integer.

Parameters:

obj – The value to test.

Returns:

True if obj is an integer; False otherwise.

adafruit_rsa._compat.write_to_stdout(data: bytes) None

Writes bytes to stdout

Parameters:

data (bytes) – Data to write

adafruit_rsa._compat.xor_bytes(bytes_1: bytes, bytes_2: bytes) bytes

Returns the bitwise XOR result between two bytes objects, bytes_1 ^ bytes_2.

Bitwise XOR operation is commutative, so order of parameters doesn’t generate different results. If parameters have different length, extra length of the largest one is ignored.

Parameters:
  • bytes_1 (bytes) – First bytes object.

  • bytes_2 – Second bytes object.

Returns:

Bytes object, result of XOR operation.

adafruit_rsa.asn1

ASN.1 definitions.

Not all ASN.1-handling code use these definitions, but when it does, they should be here.

class adafruit_rsa.asn1.PubKeyHeader(**kwargs)

OpenSSL Public Key Header

class adafruit_rsa.asn1.OpenSSLPubKey(**kwargs)

Creates a PKCS#1 DER-encoded NamedType.

class adafruit_rsa.asn1.AsnPubKey(**kwargs)

ASN1 contents of DER encoded public key:

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER,  -- e
}

adafruit_rsa.common

Common functionality shared by several modules.

exception adafruit_rsa.common.NotRelativePrimeError(a: int, b: int, d: int, msg: str | None = None)

Raises if provided a and b not relatively prime.

adafruit_rsa.common.bit_length(int_type: int) int

Return the number of bits necessary to represent an integer in binary, excluding the sign and leading zeros

Parameters:

int_type (int) – The integer to check

adafruit_rsa.common.bit_size(num: int) int

Number of bits needed to represent a integer excluding any prefix 0 bits.

Usage:

>>> bit_size(1023)
10
>>> bit_size(1024)
11
>>> bit_size(1025)
11
Parameters:

num (int) – Integer value. If num is 0, returns 0. Only the absolute value of the number is considered. Therefore, signed integers will be abs(num) before the number’s bit length is determined.

Returns:

Returns the number of bits in the integer.

adafruit_rsa.common.byte_size(number: int) int

Returns the number of bytes required to hold a specific long number.

The number of bytes is rounded up.

Usage:

>>> byte_size(1 << 1023)
128
>>> byte_size((1 << 1024) - 1)
128
>>> byte_size(1 << 1024)
129
Parameters:

number (int) – An unsigned integer

Returns:

The number of bytes required to hold a specific long number.

adafruit_rsa.common.ceil_div(num: int, div: int) int

Returns the ceiling function of a division between num and div.

Usage:

>>> ceil_div(100, 7)
15
>>> ceil_div(100, 10)
10
>>> ceil_div(1, 4)
1
Parameters:
  • num (int) – Division’s numerator, a number

  • div (int) – Division’s divisor, a number

Returns:

Rounded up result of the division between the parameters.

adafruit_rsa.common.crt(a_values: Sequence[int], modulo_values: Sequence[int]) int

Chinese Remainder Theorem.

Calculates x such that x = a[i] (mod m[i]) for each i.

Parameters:
  • a_values (Sequence[int]) – the a-values of the above equation

  • modulo_values (Sequence[int]) – the m-values of the above equation

Returns:

x such that x = a[i] (mod m[i]) for each i

>>> crt([2, 3], [3, 5])
8
>>> crt([2, 3, 2], [3, 5, 7])
23
>>> crt([2, 3, 0], [7, 11, 15])
135
adafruit_rsa.common.extended_gcd(a: int, b: int) Tuple[int, int, int]

Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb

adafruit_rsa.common.inverse(x: int, n: int) int

Returns the inverse of x % n under multiplication, a.k.a x^-1 (mod n)

>>> inverse(7, 4)
3
>>> (inverse(143, 4) * 143) % 4
1

adafruit_rsa.core

Core mathematical operations.

This is the actual core RSA implementation, which is only defined mathematically on integers.

adafruit_rsa.core.assert_int(var: Any, name: str) None

Asserts provided variable is an integer.

adafruit_rsa.core.decrypt_int(cyphertext: int, dkey: int, n: int) int

Decrypts a cypher text using the decryption key ‘dkey’, working modulo n

adafruit_rsa.core.encrypt_int(message: int, ekey: int, n: int) int

Encrypts a message using encryption key ‘ekey’, working modulo n

adafruit_rsa.core.fast_pow(x: int, e: int, m: int) int

Performs fast modular exponentiation, saves RAM on small CPUs/micros.

Parameters:
  • x (int) – Base

  • y (int) – Exponent

  • e (int) – Second exponent

adafruit_rsa.key

RSA key generation code.

Create new keys with the newkeys() function. It will give you a PublicKey and a PrivateKey object.

Loading and saving keys requires the pyasn1 module. This module is imported as late as possible, such that other functionality will remain working in absence of pyasn1.

Note

Storing public and private keys via the pickle module is possible. However, it is insecure to load a key from an untrusted source. The pickle module is not secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source.

class adafruit_rsa.key.PrivateKey(n: int, e: int, d: int, p: int, q: int)

Represents a private RSA key.

This key is also known as the ‘decryption key’. It contains the ‘n’, ‘e’, ‘d’, ‘p’, ‘q’ and other values.

Supports attributes as well as dictionary-like access. Attribute access is faster, though.

>>> PrivateKey(3247, 65537, 833, 191, 17)
PrivateKey(3247, 65537, 833, 191, 17)

exp1, exp2 and coef will be calculated:

>>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
>>> pk.exp1
55063
>>> pk.exp2
10095
>>> pk.coef
50797
blinded_decrypt(encrypted: int) int

Decrypts the message using blinding to prevent side-channel attacks.

Parameters:

encrypted (int) – the encrypted message

Returns:

the decrypted message

Return type:

int

blinded_encrypt(message: int) int

Encrypts the message using blinding to prevent side-channel attacks.

Parameters:

message (int) – the message to encrypt

Returns:

the encrypted message

Return type:

int

class adafruit_rsa.key.PublicKey(n: int, e: int)

Represents a public RSA key.

This key is also known as the ‘encryption key’. It contains the ‘n’ and ‘e’ values.

Supports attributes as well as dictionary-like access. Attribute access is faster, though.

>>> PublicKey(5, 3)
PublicKey(5, 3)
>>> key = PublicKey(5, 3)
>>> key.n
5
>>> key['n']
5
>>> key.e
3
>>> key['e']
3
classmethod load_pkcs1_openssl_der(keyfile: bytes) PublicKey

Loads a PKCS#1 DER-encoded public key file from OpenSSL.

Parameters:

keyfile (bytes) – contents of a DER-encoded file that contains the public key, from OpenSSL.

Returns:

a PublicKey object

classmethod load_pkcs1_openssl_pem(keyfile: bytes) PublicKey

Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL.

These files can be recognised in that they start with BEGIN PUBLIC KEY rather than BEGIN RSA PUBLIC KEY.

The contents of the file before the “—–BEGIN PUBLIC KEY—–” and after the “—–END PUBLIC KEY—–” lines is ignored.

Parameters:

keyfile (bytes) – contents of a PEM-encoded file that contains the public key, from OpenSSL.

Returns:

a PublicKey object

adafruit_rsa.key.newkeys(nbits: int, accurate: bool = True, poolsize: int = 1, exponent: int = 65537, log_level: str = 'INFO') Tuple[PublicKey, PrivateKey]

Generates public and private keys, and returns them as (pub, priv).

The public key is also known as the ‘encryption key’, and is a adafruit_rsa.rsa.PublicKey object. The private key is also known as the ‘decryption key’ and is a adafruit_rsa.rsa.PrivateKey object.

Parameters:
  • nbits (int) – the number of bits required to store n = p*q.

  • accurate (bool) – when True, n will have exactly the number of bits you asked for. However, this makes key generation much slower. When False, n may have slightly less bits.

  • poolsize (int) – the number of processes to use to generate the prime numbers.

  • exponent (int) – the exponent for the key; only change this if you know what you’re doing, as the exponent influences how difficult your private key can be cracked. A very common choice for e is 65537.

  • log_level (str) – Logger level, setting to DEBUG will log info about when p and q are generating.

Returns:

a tuple (adafruit_rsa.PublicKey, adafruit_rsa.PrivateKey)

The poolsize parameter was added in Python-RSA 3.1 and requires Python 2.6 or newer.

adafruit_rsa.machine_size

Detection of 32-bit and 64-bit machines and byte alignment.

adafruit_rsa.machine_size.get_word_alignment(num: int, force_arch: int = 64, _machine_word_size: Literal[64, 32] = 64) Tuple[int, int, int, str]

Returns alignment details for the given number based on the platform Python is running on.

Parameters:
  • num (int) – Unsigned integral number.

  • force_arch – If you don’t want to use 64-bit unsigned chunks, set this to anything other than 64. 32-bit chunks will be preferred then. Default 64 will be used when on a 64-bit machine.

  • _machine_word_size – (Internal) The machine word size used for alignment.

Returns:

4-tuple:

(word_bits, word_bytes,
 max_uint, packing_format_type)

adafruit_rsa.pem

Functions that load and write PEM-encoded files.

adafruit_rsa.pem.load_pem(contents: bytes | str, pem_marker: bytes | str) bytes

Loads a PEM file.

Parameters:
  • contents (bytes|str) – the contents of the file to interpret

  • pem_marker (bytes|str) – the marker of the PEM content, such as ‘RSA PRIVATE KEY’ when your file has ‘—–BEGIN RSA PRIVATE KEY—–’ and ‘—–END RSA PRIVATE KEY—–’ markers.

Returns:

the base64-decoded content between the start and end markers.

@raise ValueError: when the content is invalid, for example when the start

marker cannot be found.

adafruit_rsa.pem.save_pem(contents: bytes, pem_marker: bytes | str) bytes

Saves a PEM file.

Parameters:
  • contents (bytes) – the contents to encode in PEM format

  • pem_marker – the marker of the PEM content, such as ‘RSA PRIVATE KEY’ when your file has ‘—–BEGIN RSA PRIVATE KEY—–’ and ‘—–END RSA PRIVATE KEY—–’ markers.

Returns:

the base64-encoded content between the start and end markers, as bytes.

adafruit_rsa.pkcs1

Functions for PKCS#1 version 1.5 encryption and signing

This module implements certain functionality from PKCS#1 version 1.5. For a very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes

At least 8 bytes of random padding is used when encrypting a message. This makes these methods much more secure than the ones in the rsa module.

WARNING: this module leaks information when decryption fails. The exceptions that are raised contain the Python traceback information, which can be used to deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION to your users.

exception adafruit_rsa.pkcs1.CryptoError

Base class for all exceptions in this module.

exception adafruit_rsa.pkcs1.DecryptionError

Raised when decryption fails.

exception adafruit_rsa.pkcs1.VerificationError

Raised when verification fails.

adafruit_rsa.pkcs1.decrypt(crypto: bytes, priv_key: PrivateKey) bytes

Decrypts the given message using PKCS#1 v1.5

The decryption is considered ‘failed’ when the resulting cleartext doesn’t start with the bytes 00 02, or when the 00 byte between the padding and the message cannot be found.

Parameters:
  • crypto (bytes) – the crypto text as returned by adafruit_rsaencrypt()

  • priv_key (PrivateKey) – the adafruit_rsaPrivateKey to decrypt with.

Raises:

DecryptionError – when the decryption fails. No details are given as to why the code thinks the decryption fails, as this would leak information about the private key.

>>> import adafruit_rsa.rsa
>>> (pub_key, priv_key) = adafruit_rsanewkeys(256)

It works with strings:

>>> crypto = encrypt(b'hello', pub_key)
>>> decrypt(crypto, priv_key)
b'hello'

And with binary data:

>>> crypto = encrypt(b'', pub_key)
>>> decrypt(crypto, priv_key)
b''

Altering the encrypted information will likely cause a adafruit_rsapkcs1.DecryptionError. If you want to be sure, use adafruit_rsasign().

Warning

Never display the stack trace of a adafruit_rsapkcs1.DecryptionError exception. It shows where in the code the exception occurred, and thus leaks information about the key. It’s only a tiny bit of information, but every bit makes cracking the keys easier.

>>> crypto = encrypt(b'hello', pub_key)
>>> crypto = crypto[0:5] + b'X' + crypto[6:] # change a byte
>>> decrypt(crypto, priv_key)
Traceback (most recent call last):
...
adafruit_rsapkcs1.DecryptionError: Decryption failed
adafruit_rsa.pkcs1.encrypt(message: bytes, pub_key: PublicKey) bytes

Encrypts the given message using PKCS#1 v1.5

Parameters:
  • message (bytes) – the message to encrypt. Must be a byte string no longer than k-11 bytes, where k is the number of bytes needed to encode the n component of the public key.

  • pub_key (PublicKey) – the adafruit_rsaPublicKey to encrypt with.

Raises:

OverflowError – when the message is too large to fit in the padded block.

>>> from adafruit_rsa.rsa import key, common
>>> (pub_key, priv_key) = key.newkeys(256)
>>> message = b'hello'
>>> crypto = encrypt(message, pub_key)

The crypto text should be just as long as the public key ‘n’ component:

>>> len(crypto) == common.byte_size(pub_key.n)
True
adafruit_rsa.pkcs1.sign(message: bytes | _FileLikeObject, priv_key: PrivateKey, hash_method: str) bytes

Signs the message with the private key.

Hashes the message, then signs the hash with the given key. This is known as a “detached signature”, because the message itself isn’t altered.

Parameters:
  • message – the message to sign. Can be an 8-bit string or a file-like object. If message has a read() method, it is assumed to be a file-like object.

  • priv_key (PrivateKey) – the adafruit_rsaPrivateKey to sign with

  • hash_method – the hash method used on the message. Use ‘MD5’, ‘SHA-1’, ‘SHA-224’, SHA-256’, ‘SHA-384’ or ‘SHA-512’.

Returns:

a message signature block.

Raises:

OverflowError – if the private key is too small to contain the requested hash.

adafruit_rsa.pkcs1.verify(message: bytes | _FileLikeObject, signature: bytes, pub_key: PublicKey) str

Verifies that the signature matches the message.

The hash method is detected automatically from the signature.

Parameters:
  • message – the signed message. Can be an 8-bit string or a file-like object. If message has a read() method, it is assumed to be a file-like object.

  • signature (bytes) – the signature block, as created with rsa.sign().

  • pub_key (PublicKey) – the adafruit_rsaPublicKey of the person signing the message.

Raises:

VerificationError – when the signature doesn’t match the message.

Returns:

the name of the used hash.

adafruit_rsa.prime

Numerical functions related to primes.

Implementation based on the book Algorithm Design by Michael T. Goodrich and Roberto Tamassia, 2002.

adafruit_rsa.prime.are_relatively_prime(a: int, b: int) bool

Returns True if a and b are relatively prime, and False if they are not.

>>> are_relatively_prime(2, 3)
True
>>> are_relatively_prime(2, 4)
False
adafruit_rsa.prime.getprime(nbits: int) int

Returns a prime number that can be stored in ‘nbits’ bits.

>>> p = getprime(128)
>>> is_prime(p-1)
False
>>> is_prime(p)
True
>>> is_prime(p+1)
False
>>> from adafruit_rsa.rsa import common
>>> common.bit_size(p) == 128
True

adafruit_rsa.randnum

Functions for generating random numbers.

adafruit_rsa.randnum.randint(maxvalue: int) int

Returns a random integer x with 1 <= x <= maxvalue

May take a very long time in specific situations. If maxvalue needs N bits to store, the closer maxvalue is to (2 ** N) - 1, the faster this function is.

adafruit_rsa.randnum.read_random_bits(nbits: int) bytes

Reads ‘nbits’ random bits.

If nbits isn’t a whole number of bytes, an extra byte will be appended with only the lower bits set.

adafruit_rsa.randnum.read_random_int(nbits: int) int

Reads a random integer of approximately nbits bits.

adafruit_rsa.randnum.read_random_odd_int(nbits: int) int

Reads a random odd integer of approximately nbits bits.

>>> read_random_odd_int(512) & 1
1

adafruit_rsa.transform

Data transformation functions.

From bytes to a number, number to bytes, etc.

adafruit_rsa.transform.bytes2int(raw_bytes: bytes) int

Converts a list of bytes or an 8-bit string to an integer.

When using unicode strings, encode it to some encoding like UTF8 first.

>>> (((128 * 256) + 64) * 256) + 15
8405007
>>> bytes2int(b'€@')
8405007
adafruit_rsa.transform.bytes_leading(raw_bytes: bytes, needle: bytes = b'\x00') int

Finds the number of prefixed byte occurrences in the haystack.

Useful when you want to deal with padding.

Parameters:
  • raw_bytes (bytes) – Raw bytes.

  • needle (bytes) – The byte to count. Default .

Returns:

The number of leading needle bytes.

adafruit_rsa.transform.int2bytes(number: int, fill_size: int | None = None, chunk_size: int | None = None, overflow: bool = False) bytes

Convert an unsigned integer to bytes (base-256 representation):: Does not preserve leading zeros if you don’t specify a chunk size or fill size.

Parameters:
  • number (int) – Integer value

  • fill_size – If the optional fill size is given the length of the resulting byte string is expected to be the fill size and will be padded with prefix zero bytes to satisfy that length.

  • chunk_size – If optional chunk size is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of chunk_size.

  • overflowFalse (default). If this is True, no OverflowError will be raised when the fill_size is shorter than the length of the generated byte sequence. Instead the byte sequence will be returned as is.

Returns:

Raw bytes (base-256 representation).

Raises:

OverflowError when fill_size is given and the number takes up more bytes than fit into the block. This requires the overflow argument to this function to be set to False otherwise, no error will be raised.