Lightweight porting of cryptography to Micropython based on ARM Mbed TLS
- Drop-in PyCA
cryptographyimport paths — the same nested imports run on MicroPython and CPython + PyCA. - Wraps ARM Mbed TLS; no extra native dependency.
- Portable across MicroPython ports: unix, esp32, stm32, rp2, …
- Compile-time feature toggles to stub unused APIs and shrink the build (see How to build).
The API mirrors PyCA cryptography at the
same import paths (cryptography.hazmat.primitives[.asymmetric].*, plus
cryptography.exceptions and cryptography.x509[.oid]), so the same nested imports
run unchanged on both MicroPython (this module) and CPython + PyCA.
(utils.RFC6979 is a ucryptography-only extension, not part of PyCA.)
| Namespace | Implemented | Not yet |
|---|---|---|
hashes |
SHA1, SHA256, SHA384, SHA512, BLAKE2s, Hash | — |
hmac |
HMAC | — |
ciphers |
Cipher; algorithms AES, TripleDES; modes CBC, ECB, GCM; AESGCM | ChaCha20Poly1305, AESCCM, AESSIV, AESOCB3, AESGCMSIV |
serialization |
load/dump public+private (DER, PEM); Encoding (DER, PEM, X962, Raw); Public/PrivateFormat; NoEncryption; BestAvailableEncryption | encrypted PKCS#8 write |
rsa |
public/private keys + numbers, generate_private_key, sign/verify, rsa_crt_iqmp/dmp1/dmq1, rsa_recover_prime_factors | — |
ec |
ECDH, ECDSA, SECP256R1/384R1/521R1, public/private keys + numbers, generate/derive, from_encoded_point | curves beyond NIST P-256/384/521 |
ed25519 |
Ed25519PrivateKey, Ed25519PublicKey | — |
padding |
PKCS1v15, PSS, OAEP, MGF1, calculate_max_pss_salt_length | — |
utils |
Prehashed, constant_time_bytes_eq, encode/decode_dss_signature, bit_length, rsa_deduce_private_exponent, RFC6979 | — |
twofactor |
HOTP, TOTP | — |
x509 |
load cert & CSR (DER, PEM); Certificate; CertificateBuilder; CSR + builder; Name, NameAttribute, ObjectIdentifier, NameOID; extensions (BasicConstraints, KeyUsage, ExtendedKeyUsage + OID, SubjectAlternativeName, DNSName, IPAddress, SubjectKeyIdentifier, AuthorityKeyIdentifier, UnrecognizedExtension); random_serial_number | chain verification (PolicyBuilder / Store / Verifier), CRL, OCSP |
exceptions |
InvalidSignature, AlreadyFinalized, UnsupportedAlgorithm, InvalidKey, InvalidToken | — |
dsa, dh, x25519, x448, ed448 |
— | whole module |
KDFs (hkdf, pbkdf2hmac, scrypt, concatkdf, x963kdf) |
— | whole module |
fernet |
— | whole module |
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
message = b"A message I want to sign"
chosen_hash = hashes.SHA256()
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(chosen_hash), salt_length=chosen_hash.digest_size
),
chosen_hash,
)
public_key = private_key.public_key()
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(chosen_hash), salt_length=chosen_hash.digest_size
),
chosen_hash,
)UNIX port (standard)
$ git clone https://github.com/micropython/micropython.git
$ cd micropython
micropython$ git submodule update --init --depth 1
micropython$ git clone https://github.com/dmazzella/ucryptography.git usercmodule/ucryptography
micropython$ cd usercmodule/ucryptography
ucryptography$ git submodule update --init --depth 1
ucryptography$ cd ../../
micropython$ make -j2 -C mpy-cross/
micropython$ make -j2 -C ports/unix/ VARIANT="standard" MICROPY_SSL_AXTLS=0 MICROPY_SSL_MBEDTLS=1 USER_C_MODULES="$(pwd)/usercmodule"ESP32 port (ESP32_GENERIC_C3)
$ git clone https://github.com/micropython/micropython.git
$ cd micropython
micropython$ git submodule update --init --depth 1
micropython$ git clone https://github.com/dmazzella/ucryptography.git usercmodule/ucryptography
micropython$ cd usercmodule/ucryptography
ucryptography$ git submodule update --init --depth 1
ucryptography$ cd ../../
micropython$ make -j2 -C mpy-cross/
micropython$ make -C ports/esp32 BOARD=ESP32_GENERIC_C3 USER_C_MODULES="$(pwd)/usercmodule/ucryptography/micropython.cmake"STM32 port (ARDUINO_PORTENTA_H7)
$ git clone https://github.com/micropython/micropython.git
$ cd micropython
micropython$ git submodule update --init --depth 1
micropython$ git clone https://github.com/dmazzella/ucryptography.git usercmodule/ucryptography
micropython$ cd usercmodule/ucryptography
ucryptography$ git submodule update --init --depth 1
ucryptography$ cd ../../
micropython$ make -j2 -C mpy-cross/
micropython$ make -C ports/stm32 BOARD=ARDUINO_PORTENTA_H7 USER_C_MODULES="$(pwd)/usercmodule"Feature toggles (optional)
Every ucryptography feature is compiled in by default. Each is guarded by a
MICROPY_PY_UCRYPTOGRAPHY_* flag (all default 1) declared in
modcryptography_features.h. Set a flag to 0 to compile the feature out: its
type stays registered (no AttributeError) but constructing it raises
NotImplementedError naming the flag to re-enable.
Override a flag either by editing modcryptography_features.h, or — on the
make-based ports (unix, stm32) — by passing -D<FLAG>=0 through CFLAGS_EXTRA:
micropython$ make -j2 -C ports/unix/ VARIANT="standard" MICROPY_SSL_AXTLS=0 MICROPY_SSL_MBEDTLS=1 \
CFLAGS_EXTRA="-DMICROPY_PY_UCRYPTOGRAPHY_TRIPLEDES=0 -DMICROPY_PY_UCRYPTOGRAPHY_OAEP=0 -DMICROPY_PY_UCRYPTOGRAPHY_PSS=0" \
USER_C_MODULES="$(pwd)/usercmodule"Flags (prefix MICROPY_PY_UCRYPTOGRAPHY_, all default 1):
| Flag suffix | Python API disabled when 0 |
|---|---|
SHA1 / SHA256 / SHA384 / SHA512 |
hashes.SHA1 … hashes.SHA512 |
BLAKE2S |
hashes.BLAKE2s |
HASH |
hashes.Hash |
HMAC |
hmac.HMAC |
AES |
ciphers.algorithms.AES |
AESGCM |
ciphers.AESGCM |
TRIPLEDES |
ciphers.algorithms.TripleDES |
PKCS1V15 |
padding.PKCS1v15 |
MGF1 |
padding.MGF1 |
OAEP |
padding.OAEP |
PSS |
padding.PSS |
RSA |
rsa.* (keys, sign, verify) |
EC |
ec.* (keys, ECDH, ECDSA) |
ED25519 |
ed25519.* |
X509 |
x509 reading (load_*, Certificate) |
X509_CREATE |
x509.CertificateBuilder |
X509_CSR |
x509 CSR read/write (load_*_x509_csr, CertificateSigningRequestBuilder) |
TWOFACTOR |
twofactor.HOTP / twofactor.TOTP |
Dependencies are enforced automatically: MGF1=0 also disables OAEP and PSS;
X509=0 also disables X509_CREATE and X509_CSR. The CSR builder additionally
needs X509_CREATE (they share the mbedtls certificate-writing stack).
Most toggles only stub the Python API — the underlying mbedtls primitive stays
because the port's TLS stack shares it. A few settings additionally shrink the
mbedtls library (they gate modules exclusive to ucryptography): TRIPLEDES=0
drops MBEDTLS_DES_C; OAEP=0 and PSS=0 drop MBEDTLS_PKCS1_V21;
X509_CREATE=0 drops MBEDTLS_X509_CREATE_C + MBEDTLS_X509_CRT_WRITE_C;
X509_CSR=0 drops MBEDTLS_X509_CSR_PARSE_C + MBEDTLS_X509_CSR_WRITE_C.
CSR read note: mbedtls natively parses only KeyUsage and SubjectAlternativeName
from a CSR; BasicConstraints and ExtendedKeyUsage are recovered from the raw
request, and any other requested extension is exposed as an
UnrecognizedExtension. A requested BasicConstraints/ExtendedKeyUsage marked
critical is rejected by mbedtls, so keep those non-critical in a CSR.
Development help is welcome: issues, pull requests and discussions are all appreciated.
Tip
If you find ucryptography useful, consider ⭐ this project and why not ... Buy me a coffee 😄