{"id":16276550,"url":"https://github.com/virgesmith/pico-crypto-key","last_synced_at":"2025-03-16T13:31:20.077Z","repository":{"id":42515054,"uuid":"341688051","full_name":"virgesmith/pico-crypto-key","owner":"virgesmith","description":"RasPi Pico Cryptography (SHA256, AES256, ECDSA) Device","archived":false,"fork":false,"pushed_at":"2025-02-21T09:43:44.000Z","size":190,"stargazers_count":27,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-27T09:44:08.164Z","etag":null,"topics":["aes-encryption","cryptography","elliptic-curves","pico","raspberry-pi-pico","rp2040"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/virgesmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-23T20:57:17.000Z","updated_at":"2025-01-29T10:35:26.000Z","dependencies_parsed_at":"2024-05-10T07:41:46.469Z","dependency_job_id":"e4425f8f-953f-4d86-a1ce-89128e7bc950","html_url":"https://github.com/virgesmith/pico-crypto-key","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/virgesmith%2Fpico-crypto-key","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/virgesmith%2Fpico-crypto-key/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/virgesmith%2Fpico-crypto-key/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/virgesmith%2Fpico-crypto-key/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/virgesmith","download_url":"https://codeload.github.com/virgesmith/pico-crypto-key/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243817068,"owners_count":20352493,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes-encryption","cryptography","elliptic-curves","pico","raspberry-pi-pico","rp2040"],"created_at":"2024-10-10T18:48:51.234Z","updated_at":"2025-03-16T13:31:20.069Z","avatar_url":"https://github.com/virgesmith.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pico-crypto-key\n\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/fb9853538e3a421d9715812f87f3269d)](https://www.codacy.com/gh/virgesmith/pico-crypto-key/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=virgesmith/pico-crypto-key\u0026amp;utm_campaign=Badge_Grade)\n\nUsing Raspberry Pi RP2040/RP2350 microcontrollers as USB security devices that provide:\n\n- cryptographic hashing (SHA256)\n- encryption and decryption (256 bit AES)\n- public key cryptography (ECDSA - secp256k1, as Bitcoin)\n\nI'm not a security expert and the device/software is almost certainly not hardened enough for serious use (perhaps RP2350 ARM-secure will fix that?). I just did it cos it was there, and I was bored. Also, it's not fast, but that might be ok depending on your current lockdown status. Most importantly, it works. Here's some steps I took towards making it securer:\n\n- the device is pin protected. Only the SHA256 hash of the (salted) pin is stored on the device.\n- the private key is only initialised once a correct pin has been entered, and is a SHA256 hash of the (salted) unique device id. So no two devices should have the same key.\n- the private key never leaves the device and is stored only in volatile memory.\n\nPico, Pico W, Tiny2040, Pico2 and Pico2 W boards are known to work. Other RP2040/RP2350 boards have not been tested but are likely to (mostly) work. E.g. the Pico/Pico2 W requires the wifi driver purely for the LED (which is connected to the wifi chip) to function (though neither wifi nor bluetooth are enabled.)\n\n## Notes/issues\n\n- build and install picotool separately against head of sdk (which still uses mbedtls 2), otherwise the build will try building picotool against mbedtls 3, which won't work\n- Writes to the final flash block do not persist on RP2350. See [here](https://forums.raspberrypi.com/viewtopic.php?t=375912). Simple workaround is to use the penultimate block.\n- Not all prebuilt RISC-V toolchains seem to work, see [here](https://forums.raspberrypi.com/viewtopic.php?t=375713). [This one](https://github.com/raspberrypi/pico-sdk-tools/releases/download/v2.0.0-1/riscv-toolchain-14-aarch64-lin.tar.gz) worked for me.\n\n## Performance\n\n### RP2040 vs RP2350 ARM vs RP2350 RISC-V\n\nv1.4.0 introduced support for RP2350 boards. Performance improvement is fairly modest, Cortex M33 slightly outperforming the Hazard3 - but the bottleneck here is USB comms. Note that using hardware SHA256 only seems to improve hashing performance by about 6% for this (IO-bound) use case.\n\n|         | RP2040\u003cbr/\u003etime(s) | \u003cbr/\u003ebitrate(kbps) | RP2350(ARM)\u003cbr/\u003etime(s) | \u003cbr/\u003ebitrate(kbps) | \u003cbr/\u003espeedup(%) | RP2350(RISC-V)\u003cbr/\u003etime(s) | \u003cbr/\u003ebitrate(kbps) | \u003cbr/\u003espeedup(%) |\n|:--------|-------------------:|-------------------:|------------------------:|-------------------:|----------------:|---------------------------:|-------------------:|----------------:|\n| hash    |                2.6 |             3099.2 |                     1.8 |             4557.3 |            47.0 |                        1.8 |             4503.0 |            45.3 |\n| sign    |                2.7 |             2996.8 |                     1.9 |             4239.9 |            41.5 |                        1.8 |             4384.3 |            46.3 |\n| verify  |                0.5 |                    |                     0.2 |                    |           117.6 |                        0.3 |                    |            81.0 |\n| encrypt |               23.8 |              335.8 |                    11.2 |              713.5 |           112.5 |                       13.2 |              604.5 |            80.0 |\n| decrypt |               23.8 |              336.6 |                    11.2 |              714.5 |           112.3 |                       13.2 |              604.3 |            79.5 |\n\nTests run on a single core and use a 1000kB random binary data input. Binaries compiled with 10.3.1 ARM and 14.2.1 RISC-V gcc toolchains.\n\nOn thing not measured or considered here is the difference in power consumption between Cortex M33 vs Hazard3...\n\n\n### USB speed\n\nv1.1.0 switched to USB CDC rather than serial to communicate with the host which allows much faster bitrates and avoids the need to encode binary data. Performance is improved, but varies considerably by task (results are for a 1000kB input on a RP2040):\n\n| task    | CDC\u003cbr\u003etime(s) | CDC\u003cbr\u003ebitrate(kbps) | serial\u003cbr\u003etime(s) | serial\u003cbr\u003ebitrate(kbps)| Speedup(%) |\n|:--------|---------------:|---------------------:|------------------:|-----------------------:|-----------:|\n| hash    |            2.6 |               3026.3 |              19.6 |                  407.9 |      641.9 |\n| sign    |            2.8 |               2904.1 |              19.6 |                  408.3 |      611.3 |\n| verify  |            0.4 |                      |               0.5 |                        |       16.0 |\n| encrypt |           23.9 |                334.2 |              43.5 |                  183.8 |       81.9 |\n| decrypt |           23.8 |                336.0 |              43.1 |                  185.7 |       81.0 |\n\n\n## Usage\n\n`pico-crypto-key` is a python (dev) package that provides:\n\n- a simplified build process supporting multiple configurations\n- a python interface to the devices.\n\n### Dependencies/prerequisites\n\nFirst, clone/fork this repo and install the package in development (editable) mode:\n\n```sh\npip install -e .[dev]\n```\n\nIf this step fails, try upgrading to a more recent version of pip.\n\nYou will then need to:\n\n- install the compiler toolchain(s) and cmake:\n\n  ```sh\n  sudo apt install gcc-arm-none-eabi cmake\n  ```\n\n  For RISC-V, a prebuilt toolchain can be found [here](https://github.com/raspberrypi/pico-sdk-tools/releases/tag/v2.0.0-5).\n\n- clone [pico-sdk](https://github.com/raspberrypi/pico-sdk) see [here](https://www.raspberrypi.org/documentation/pico/getting-started/). Initialise submodules:\n\n  ```sh\n  git submodule update --init\n  ```\n\n- download a release of [mbedtls](https://tls.mbed.org/api/) - the `.tar.bz` asset. Currently using the 3.6.2 release/tag. **This is a different version to the one in the SDK** (which still uses v2) so must be kept separate. It also requires a custom configuration. Create a symlink in the project root to mbedtls, e.g.:\n\n  ```sh\n  ln -s ../mbedtls-3.6.2 mbedtls\n  ```\n\n## Configure\n\nIn the `config/boards.toml` file ensure settings for `PICO_TOOLCHAIN_PATH` and `PICO_SDK_PATH` are correct.\n\nIf using a fresh download of `mbedtls` - run the configuration script to customise the build for the Pico, e.g.:\n\n```sh\n./configure-mbedtls.sh\n```\n\nMore info [here](https://tls.mbed.org/discussions/generic/mbedtls-build-for-arm)\n\n## Supported boards\n\nThe target board must be specified using the `--board` option when running `check`, `clean`, `build`, `install` or `reset-pin`.\n\n- Pico: `--board pico`\n- Pico W: `--board pico_w`\n- Pimoroni Tiny2040 2MB: `--board tiny2040`\n- Pico2: `--board pico2` or `--board pico2-riscv`\n- Pico2 W: `--board pico2_w` or `--board pico2_w-riscv`\n\nUsing the correct board will ensure (amongst other things?) the LED will work. (NB images built for one RP2040 board may work on other RP2040 boards, aside from the LED. YMMV...\n\n### Board LED indicators\n\nBoard    | Init        | Ready* | Busy | Invalid | [Fatal Error](#errors)\n---------|-------------|--------|------|---------|------------\nPico     | Flash       | -      | On   | -       | Flashing\nPico W   | Flash       | -      | On   | -       | Flashing\nTiny2040 | White flash | Green  | Blue | Red     | Flashing Red\nPico 2   | Flash       | -      | On   | -       | Flashing\nPico 2 W | Flash       | -      | On   | -       | Flashing\n\n\u0026ast; \"Ready\" state is only entered after a valid pin is supplied.\n\n## Build\n\nThese steps use the `picobuild` script. (See `picobuild --help`.) Optionally check your configuration is correct then build, e.g for pico2 ARM:\n\n```sh\npicobuild check --board pico2\npicobuild build --board pico2\n```\n\nEnsure your device is connected and mounted ready to accept a new image (press `BOOTSEL` when connecting), then:\n\n```sh\npicobuild install  --board pico2 /path/to/RPI-RP2\npicobuild test\n```\n\n## PIN protection\n\nThe device is protected with a PIN, the salted hash of which is read from flash memory. Before first use (or a forgotten PIN), a hash must be written to flash (press `BOOTSEL` when connecting):\n\n```sh\npicobuild reset-pin /path/to/RPI-RP2\n```\n\nIf the device LED is flashing (red if supported by the board) after this, the reset failed - the flash memory may be worn. Otherwise now reinstall the crypto key image as above. The pin will then be \"pico\", and it can be changed - see the [example](#change-pin).\n\nThe python driver will first check for an env var `PICO_CRYPTO_KEY_PIN` and fall back to a prompt if this is not present.\n\n(NB to run the tests, either use the `--pin` command line option or set the env var)\n\n## Using the device\n\nOn boards with multicoloured LEDs (e.g. tiny2040), initialisation is green, busy is blue and error states are red.\n\nThe `CryptoKey` class provides the python interface and is context-managed to help ensure the device gets properly opened and closed. The correct pin must be provided to activate it. Methods available are:\n\n- `pubkey` return the ECDSA public key (short-form, 33 bytes)\n- `hash` compute the SHA256 hash of the input\n- `sign` compute the SHA256 hash and ECDSA signature of the input\n- `verify` verify the given hash matches the signature and public key\n- `encrypt` encrypts using AES256\n- `decrypt` decrypts using AES256\n- `register` dynamically create an ECDSA public key for verifying, along the lines of WebAuthn\n- `auth` generates a one-time ECDSA-based authentication string, along the lines of WebAuthn\n- `set_pin` set a new PIN\n- `info` returns version, board type and device time\n\nSee the examples for more details.\n\n## Errors\n\nIf there are low-level errors with any of the crypto algorithms then the device may enter an unrecoverable error state where the LED will flash. The error codes can be interpreted like so:\n\nLong flashes | Short flashes | Algorithm | mbedtls error code\n------------:|--------------:|-----------|-------------------\n1            | 0             | ECDSA     | Unknown error\n1            | 1             | ECDSA     | `MBEDTLS_ERR_ECP_BAD_INPUT_DATA`\n1            | 2             | ECDSA     | `MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL`\n1            | 3             | ECDSA     | `MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE`\n1            | 4             | ECDSA     | `MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED`\n1            | 5             | ECDSA     | `MBEDTLS_ERR_MPI_ALLOC_FAILED`\n2            | 0             | AES       | Unknown error\n2            | 1             | AES       | `MBEDTLS_ERR_AES_INVALID_KEY_LENGTH`\n3            | 0             | SHA       | Unknown error\n\n\n## Troubleshooting\n\n- If you get `[Errno 13] Permission denied: '/dev/ttyACM0'`, adding yourself to the `dialout` group and rebooting should fix.\n- If you get `usb.core.USBError: [Errno 13] Access denied (insufficient permissions)` you'll need to add a udev rule for the device, see [this stackoverflow post](https://stackoverflow.com/questions/53125118/why-is-python-pyusb-usb-core-access-denied-due-to-permissions-and-why-wont-the). This worked for me:\n\n  `SUBSYSTEMS==\"usb\", ENV{DEVTYPE}==\"usb_device\", ATTRS{idVendor}==\"aafe\", ATTRS{idProduct}==\"c0ff\", GROUP=\"plugdev\", MODE=\"0777\"`\n\n- the device can get out of sync quite easily when something goes wrong. If so, turn it off and on again ;)\n\n## Examples\n\n### Hash file\n\nThis script just prints the hash of itself.\n\n```sh\n$ python examples/hash_file.py examples/hash_file.py\nPicoCryptoKey 1.3.0-pico\nexamples/hash_file.py: f99e202cdb1c7091f291a3361eac6b8d2230eef28bd165415e86ec235b03a938\n```\n\n### Encrypt/decrypt data\n\nThis example will look for an encrypted version of the data (examples/dataframe.csv). If not found it will first encrypt the plaintext.\n\nThen it decrypts the ciphertext and loads the data into a pandas dataframe (you may need to install pandas).\n\n```sh\npython examples/decrypt_data.py\n```\n\nIf you are using the same device you used to encrypt the data, you should see something like this:\n\n```text\ndecryption took 2.56s\n           Area  DC1117EW_C_SEX  DC1117EW_C_AGE NewEthpop_ETH\n0     E02001730               2              62           WBI\n1     E02001713               2              60           WBI\n2     E02001713               1              30           WBI\n3     E02001736               1              41           OAS\n4     E02001719               2              22           WBI\n...         ...             ...             ...           ...\n5630  E02001721               1              60           WBI\n5631  E02001720               1              22           WBI\n5632  E02001729               2              24           MIX\n5633  E02006893               1              32           WBI\n5634  E02001708               1              28           OAS\n\n[5635 rows x 4 columns]\n```\n\nIf you now switch to a different device, it won't be able to decrypt the ciphertext and will return garbage.\n\n### Sign data\n\nThis example will compute a hash (SHA256) of a file and sign it. It outputs a json object containing the filename, the hash, the signature, and the device's public key.\n\n```sh\npython examples/sign_data.py\n```\n\ngives you something like\n\n```text\nPicoCryptoKey 1.3.0-pico\nsigning took 0.46s\nsignature written to signature.json\n```\n\nwhere signature.json contains\n\n```json\n{\n  \"file\": \"examples/dataframe.csv\",\n  \"hash\": \"28d839df69762085f8ac7b360cd5ee0435030247143260cfaff0b313f99a251c\",\n  \"signature\": \"30460221009531919bd13f964544fb494393e1bea1cf5a04a53d572e914ea1cbc30657166c022100e1d1394240eb9a8269eafa8d06a82d1c087a0af260576577da5f45352ebb4162\",\n  \"pubkey\": \"020a7dfbd2272ad9e9d49dd11aec4743d10ba3d9e3affc3fa3a64c8a28fd78a212\"\n}\n```\n\n### Verify data\n\nThe signature data above should be verifiable by any ECDSA validation algorithm, but you can use the device for this. First it verifies the supplied hash corresponds to the file, then it verifies the signature against the hash and the given public key. It also prints whether the public key provided matches it's own public key.\n\n```sh\npython examples/verify_data.py\n```\n\n```text\nPicoCryptoKey 1.3.0-pico\nfile hash matches file\nverifying device is the signing device\nsignature is valid\nverifying took 0.79s\n```\n\nor, if you use a different board\n\n```text\nPicoCryptoKey 1.3.0-pico\nfile hash matches file\nverifying device is not the signing device\nsignature is valid\nverifying took 0.79s\n```\n\n### Authenticate\n\nStep 1 generates registration keys for two relying parties - these are short-form ECDSA public keys.\n\nStep 2 generates a time-based auth tokens for each relying party from a challenge string. The tokens are base64-encoded ECDSA signatures of the SHA256 of the challenge appended with the timestamp rounded to the minute.\n\nThird-party code (the ecdsa python package) is then used to verify the public key-auth token pairs.\n\n```sh\npython examples/auth.py\n```\n\n```txt\nPicoCryptoKey 1.3.1-pimoroni_tiny2040_2mb 2024-07-09 18:00:41.382000+00:00\nHost-device time diff: 0.002865s\nregistered auth_user@example.com: 0334195ea7cc307c5908bd5f80b5fd0513edf5e8bf0f49c544231e089b2ea6c682\nregistered auth_user@another.org: 024f4f8fc6a8fca7069ffeb7122f545833ea82727fd3a8286e13f77bdbf6214dc9\nchallenge is: b'testing time-based auth'\nauth response auth_user@example.com: b'MEQCIG4Pp5o/wXMh6RY0Z2zvr1IOBWVhQcHoRyGeQQls8genAiBaJjKeM4R4kI3DD5s3xet4R/K/bQRncyWqBoO89QILkA=='\nauth response auth_user@another.org: b'MEYCIQDezWSAyNwvioDXbsO/xDMnDJLhZWWVGLhMLFNoNezRUwIhANC8gdkUtcfDcciGw9J2hbB2NdoqFP+o5RuyYQms30xk'\nexample.com verified: True\nanother.org verified: True\nexample.com cannot verify b'MEYCIQDezWSAyNwvioDXbsO/xDMnDJLhZWWVGLhMLFNoNezRUwIhANC8gdkUtcfDcciGw9J2hbB2NdoqFP+o5RuyYQms30xk'\nanother.org cannot verify b'MEQCIG4Pp5o/wXMh6RY0Z2zvr1IOBWVhQcHoRyGeQQls8genAiBaJjKeM4R4kI3DD5s3xet4R/K/bQRncyWqBoO89QILkA=='\n```\n\n### Authenticate (host-user)\n\nAs above, but using a webauthn-style workflow using a local fastapi instance (would normally be a remote website).\n\nFirst start the host:\n\n```sh\nfastapi run examples/webauthn_host.py\n```\n\nWhen up and running, the API endpoints should be documented at [http://localhost:8000/docs](http://localhost:8000/docs)\n\nThen use the interactive client script to interact with the crypto key and allow you to register and authenticate with the host, like in the example above:\n\n```sh\npython examples/webauthn_user.py\n```\n\nTry playing around with different users and different keys...\n\n### Change PIN\n\n```sh\npython examples/change_pin.py\n```\n\nThis just runs the PIN reset process:\n\n- initialise device\n- reset device (you'll need to enter the old PIN, even if this was set in the env)\n- enter new PIN and repeat to confirm\n- write new PIN to device\n- reset device and initialise with new PIN\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvirgesmith%2Fpico-crypto-key","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvirgesmith%2Fpico-crypto-key","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvirgesmith%2Fpico-crypto-key/lists"}