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
ifobj
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
ifobj
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
anddiv
.Usage:
>>> ceil_div(100, 7) 15 >>> ceil_div(100, 10) 10 >>> ceil_div(1, 4) 1
- 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:
- 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.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.
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
- 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 aadafruit_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:
- 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, useadafruit_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:
- 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 aread()
method, it is assumed to be a file-like object.priv_key (PrivateKey) – the
adafruit_rsaPrivateKey
to sign withhash_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:
- 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.
- 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
.overflow –
False
(default). If this isTrue
, noOverflowError
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 theoverflow
argument to this function to be set toFalse
otherwise, no error will be raised.