Micropython package for doing fast rsa and elliptic curve cryptography, specifically digital signatures. ECDSA API design inspired from fastecdsa and implementation based on tomsfastmath.
Tip
If you find ucrypto useful, consider ⭐ this project and why not ... Buy me a coffee 😄
-
Signing and Verifying ufastrsa
from ufastrsa.rsa import RSA, genrsa def main(): bits = 1024 print("RSA bits", bits) r = RSA(*genrsa(bits, e=65537)) if r: print("RSA OK") data = b"a message to sign and encrypt via RSA" print("random data len:", len(data), data) assert r.pkcs_verify(r.pkcs_sign(data)) == data print("pkcs_verify OK") assert r.pkcs_decrypt(r.pkcs_encrypt(data)) == data print("pkcs_decrypt OK") if __name__ == "__main__": main()
-
Signing and Verifying ufastecdsa
try: from ufastecdsa import curve, ecdsa, keys, util get_bit_length = util.get_bit_length except ImportError: from fastecdsa import curve, ecdsa, keys, util get_bit_length = int.bit_length def main(): # private_key = 82378264402520040413352233063555671940555718680152892238371187003380781159101 # public_key = keys.get_public_key(private_key, curve.P256) private_key, public_key = keys.gen_keypair(curve.P256) print("private_key:", private_key) print("public_key:", public_key.x, public_key.y, public_key.curve.name) m = "a message to sign via ECDSA" r, s = ecdsa.sign(m, private_key) print("R:", r) print("S:", s) verified = ecdsa.verify((r, s), m, public_key) print(verified) if __name__ == "__main__": main()
-
Arbitrary Elliptic Curve Arithmetic
from _crypto import ECC P256 = ECC.Curve( 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, -0x3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 ) S = ECC.Point( 0xde2444bebc8d36e682edd27e0f271508617519b3221a8fa0b77cab3989da97c9, 0xc093ae7ff36e5380fc01a5aad1e66659702de80f53cec576b6350b243042a256, P256 ) T = ECC.Point( 0x55a8b00f8da1d44e62f6b3b25316212e39540dc861c89575bb8cf92e35e0986b, 0x5421c3209c2d6c704835d82ac4c3dd90f61a8a52598b9e7ab656e9d8c8b24316, P256 ) print("S==S = ", S == S) print("S==T = ", S == T) R = S + T print("S+T = ({:X}, {:X})".format(R.x, R.y)) R = S - T print("S-T = ({:X}, {:X})".format(R.x, R.y)) R = 2 * S print("2S = ({:X}, {:X})".format(R.x, R.y)) d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd e = 0xd37f628ece72a462f0145cbefe3f0b355ee8332d37acdd83a358016aea029db7 R = (d * S) + (e * T) print("dS+eT = ({:X}, {:X})".format(R.x, R.y)) R = S + S print("S+S = ({:X}, {:X})".format(R.x, R.y)) R = S - S print("S-S = ({:X}, {:X})".format(R.x, R.y))
-
for other examples: tests
#define TFM_NO_ASM
// #define TFM_ECC192
// #define TFM_ECC224
// #define TFM_ECC256
// #define TFM_ECC384
// #define TFM_ECC512
// #define TFM_RSA512
// #define TFM_RSA1024
// #define TFM_RSA2048
To build such a module, compile MicroPython with an extra make flag named USER_C_MODULES
set to the directory containing all modules you want included (not to the module itself).
- Example:
➜ ~ git clone https://github.com/micropython/micropython.git micropython ➜ ~ cd micropython ➜ micropython (master) ✗ git submodule update --init ➜ micropython (master) ✗ git clone https://github.com/dmazzella/ucrypto.git ports/stm32/boards/PYBD_SF6/cmodules/ucrypto ➜ micropython (master) ✗ make -j8 -C mpy-cross && make -j8 -C ports/stm32/ BOARD="PYBD_SF6" USER_C_MODULES="$(pwd)/ports/stm32/boards/PYBD_SF6/cmodules"
➜ ~ git clone https://github.com/micropython/micropython.git micropython ➜ ~ cd micropython ➜ micropython (master) ✗ git submodule update --init ➜ micropython (master) ✗ git clone https://github.com/dmazzella/ucrypto.git ports/esp32/boards/ESP32_GENERIC/cmodules/ucrypto ➜ micropython (master) ✗ make -j8 -C mpy-cross && make -j8 -C ports/esp32/ BOARD="ESP32_GENERIC" USER_C_MODULES="$(pwd)/ports/esp32/boards/ESP32_GENERIC/cmodules/ucrypto/micropython.cmake"
➜ ~ git clone https://github.com/micropython/micropython.git micropython ➜ ~ cd micropython ➜ micropython (master) ✗ git submodule update --init ➜ micropython (master) ✗ git clone https://github.com/dmazzella/ucrypto.git ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/cmodules/ucrypto ➜ micropython (master) ✗ make -j8 -C mpy-cross && make -j8 -C ports/rp2/ BOARD="ARDUINO_NANO_RP2040_CONNECT" USER_C_MODULES="$(pwd)/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/cmodules/ucrypto/micropython.cmake"
The build size depends on the asm optimizations of the tomsfastmath library that are enabled into ucrypto/tomsfastmath/tfm_mpi.h
#define TFM_ECC192
#define TFM_ECC224
#define TFM_ECC256
#define TFM_ECC384
#define TFM_ECC512
#define TFM_RSA512
#define TFM_RSA1024
#define TFM_RSA2048
- PYBD_SF6 without ucrypto:
LINK build-PYBD_SF6/firmware.elf text data bss dec hex filename 1012856 328 100576 1113760 10fea0 build-PYBD_SF6/firmware.elf
- PYBD_SF6 with ucrypto and with tomsfastmath only ECC 256 asm optimizations:
// #define TFM_ECC192 // #define TFM_ECC224 #define TFM_ECC256 // #define TFM_ECC384 // #define TFM_ECC512 // #define TFM_RSA512 // #define TFM_RSA1024 // #define TFM_RSA2048
LINK build-PYBD_SF6/firmware.elf text data bss dec hex filename 1034872 452 101600 1136924 11591c build-PYBD_SF6/firmware.elf
- PYBD_SF6 with ucrypto and without tomsfastmath RSA asm optimizations:
#define TFM_ECC192 #define TFM_ECC224 #define TFM_ECC256 #define TFM_ECC384 #define TFM_ECC512 // #define TFM_RSA512 // #define TFM_RSA1024 // #define TFM_RSA2048
LINK build-PYBD_SF6/firmware.elf text data bss dec hex filename 1042552 452 101600 1144604 11771c build-PYBD_SF6/firmware.elf
- PYBD_SF6 with ucrypto and full tomsfastmath asm optimizations:
LINK build-PYBD_SF6/firmware.elf text data bss dec hex filename 1209976 452 101600 1312028 14051c build-PYBD_SF6/firmware.elf
To see which optimizations are enabled in the build:
MicroPython v1.19.1-705-gac5934c96-dirty on 2022-11-22; PORTENTA with STM32H747
Type "help()" for more information.
>>> import _crypto
>>> print(_crypto.NUMBER.ident())
TomsFastMath v0.13.1-next
Sizeofs
fp_digit = 4
fp_word = 8
FP_MAX_SIZE = 4352
Defines:
TFM_ARM TFM_ECC192 TFM_ECC224 TFM_ECC256 TFM_ECC384 TFM_ECC512 TFM_RSA512 TFM_RSA1024 TFM_RSA2048 TFM_ASM TFM_MUL6 TFM_SQR6 TFM_MUL7 TFM_SQR7 TFM_MUL8 TFM_SQR8 TFM_MUL12 TFM_SQR12 TFM_SMALL_SET TFM_MUL17 TFM_SQR17 TFM_MUL32 TFM_SQR32 TFM_MUL64 TFM_SQR64
>>>