diff --git a/.cargo/config.toml b/.cargo/config.toml index 4631d84..310ba2a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,6 +5,8 @@ build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf --features # Available but not supported by esp-hal (yet). #build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf --features esp32c5" build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" +# Debug build (too big for target and probe-rs doesn't work anyway) +#build-esp32c6 = "build --target riscv32imac-unknown-none-elf --features esp32c6" build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf --features esp32s3" @@ -14,6 +16,8 @@ run-esp32c3 = "run --release --target riscv32imc-unknown-none-elf --features esp # Available but not supported by esp-hal (yet). #run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf --features esp32c5" run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" +# Debug build (too big for target and probe-rs doesn't work anyway) +#run-esp32c6 = "run --target riscv32imac-unknown-none-elf --features esp32c6" run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --features esp32s3" @@ -24,7 +28,8 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.riscv32imac-unknown-none-elf] -runner = "espflash flash --baud=921600 --monitor" +runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" +#runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.xtensa-esp32s2-none-elf] runner = "espflash flash --baud=921600 --monitor --chip esp32s2" @@ -33,10 +38,19 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s2"'] runner = "espflash flash --baud=921600 --monitor --chip esp32s3" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s3"'] - +# https://docs.espressif.com/projects/rust/esp-hal/1.0.0-beta.1/esp32c6/esp_hal/index.html#additional-configuration [env] -ESP_LOG="INFO" +ESP_LOG = "INFO" +#ESP_HAL_CONFIG_PLACE_SWITCH_TABLES_IN_RAM=true +#ESP_HAL_CONFIG_PLACE_ANON_IN_RAM=false +#ESP_HAL_CONFIG_FLIP_LINK=false +#ESP_HAL_CONFIG_STACK_GUARD_OFFSET=4096 +#ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773 +#ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=true + + +[build] target = "riscv32imac-unknown-none-elf" [unstable] diff --git a/.gitignore b/.gitignore index 0dd24c9..3868ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ target/ # VSCode workspace(s) *.code-workspace + +# Temporarily ignore book from this branch +docs/book diff --git a/.vscode/settings.json b/.vscode/settings.json index 979d509..f9a1f42 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { "rust-analyzer.check.allTargets": false, + // "rust-analyzer.cargo.features": [ + // "esp32c6" + // ] } diff --git a/Cargo.lock b/Cargo.lock index 5076662..501983f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,21 +20,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.98" @@ -49,23 +34,37 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] -name = "basic-toml" -version = "0.1.10" +name = "bcrypt" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" dependencies = [ - "serde", + "base64 0.22.1", + "blowfish", + "subtle", ] [[package]] @@ -85,7 +84,7 @@ checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -96,9 +95,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "block-buffer" @@ -110,16 +109,20 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.19.0" +name = "blowfish" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -127,20 +130,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" -dependencies = [ - "shlex", -] - [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chacha20" @@ -153,18 +147,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "chrono" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-link", -] - [[package]] name = "cipher" version = "0.4.4" @@ -177,10 +159,10 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "const-default" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" [[package]] name = "cpufeatures" @@ -204,6 +186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -218,9 +201,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "373b7c5dbd637569a2cca66e8d66b8c446a1e7bf064ea321d265d7b3dfe7c97e" dependencies = [ "cfg-if", "cpufeatures", @@ -240,7 +223,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -264,7 +247,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.104", ] [[package]] @@ -275,18 +258,18 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "delegate" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" +checksum = "6178a82cf56c836a3ba61a7935cdb1c49bfaa6fa4327cd5bf554a503087de26b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -320,9 +303,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -341,8 +324,8 @@ dependencies = [ "edge-nal", "edge-raw", "embassy-futures", - "embassy-time", - "heapless", + "embassy-time 0.4.0", + "heapless 0.8.0", "num_enum", "rand_core 0.6.4", ] @@ -353,8 +336,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac19c3edcdad839c71cb919cd09a632d9915d630760b37f0b74290188c08f248" dependencies = [ - "embassy-time", - "embedded-io-async", + "embassy-time 0.4.0", + "embedded-io-async 0.6.1", ] [[package]] @@ -366,8 +349,8 @@ dependencies = [ "edge-nal", "embassy-futures", "embassy-net", - "embedded-io-async", - "heapless", + "embedded-io-async 0.6.1", + "heapless 0.8.0", ] [[package]] @@ -378,13 +361,13 @@ checksum = "6207c84e9bc8df8ef3c155196df290f2a51f010bd60c2e78366e51979988bdb5" [[package]] name = "embassy-embedded-hal" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12" +checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" dependencies = [ "embassy-futures", - "embassy-sync 0.6.2", - "embassy-time", + "embassy-hal-internal", + "embassy-sync 0.7.2", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -395,32 +378,48 @@ dependencies = [ [[package]] name = "embassy-executor" -version = "0.7.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6" +checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" dependencies = [ "critical-section", "document-features", "embassy-executor-macros", + "embassy-executor-timer-queue", ] [[package]] name = "embassy-executor-macros" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" +checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472" dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + [[package]] name = "embassy-futures" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "num-traits", +] [[package]] name = "embassy-net" @@ -431,10 +430,10 @@ dependencies = [ "document-features", "embassy-net-driver", "embassy-sync 0.6.2", - "embassy-time", - "embedded-io-async", + "embassy-time 0.4.0", + "embedded-io-async 0.6.1", "embedded-nal-async", - "heapless", + "heapless 0.8.0", "managed", "smoltcp", ] @@ -453,24 +452,24 @@ checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" dependencies = [ "cfg-if", "critical-section", - "embedded-io-async", + "embedded-io-async 0.6.1", "futures-sink", "futures-util", - "heapless", + "heapless 0.8.0", ] [[package]] name = "embassy-sync" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" dependencies = [ "cfg-if", "critical-section", - "embedded-io-async", + "embedded-io-async 0.6.1", + "futures-core", "futures-sink", - "futures-util", - "heapless", + "heapless 0.8.0", ] [[package]] @@ -490,38 +489,47 @@ dependencies = [ ] [[package]] -name = "embassy-time-driver" -version = "0.2.0" +name = "embassy-time" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" dependencies = [ + "cfg-if", + "critical-section", "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", ] [[package]] -name = "embassy-time-queue-utils" -version = "0.1.0" +name = "embassy-time-driver" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" dependencies = [ - "embassy-executor", - "heapless", + "document-features", ] [[package]] name = "embassy-usb-driver" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" +checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a" +dependencies = [ + "embedded-io-async 0.6.1", +] [[package]] name = "embassy-usb-synopsys-otg" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e753b23799329780c7ac434264026d0422044d6649ed70a73441b14a6436d7" +checksum = "288751f8eaa44a5cf2613f13cee0ca8e06e6638cb96e897e6834702c79084b23" dependencies = [ "critical-section", - "embassy-sync 0.6.2", + "embassy-sync 0.7.2", "embassy-usb-driver", ] @@ -565,13 +573,28 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + [[package]] name = "embedded-io-async" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" dependencies = [ - "embedded-io", + "embedded-io 0.6.1", +] + +[[package]] +name = "embedded-io-async" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0" +dependencies = [ + "embedded-io 0.7.1", ] [[package]] @@ -589,7 +612,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76959917cd2b86f40a98c28dd5624eddd1fa69d746241c8257eac428d83cb211" dependencies = [ - "embedded-io-async", + "embedded-io-async 0.6.1", "embedded-nal", ] @@ -608,37 +631,25 @@ dependencies = [ "embedded-storage", ] -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -649,78 +660,75 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "esp-alloc" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e95f1de57ce5a6600368f3d3c931b0dfe00501661e96f5ab83bc5cdee031784" +checksum = "641e43d6a60244429117ef2fa7a47182120c7561336ea01f6fb08d634f46bae1" dependencies = [ "allocator-api2", "cfg-if", - "critical-section", "document-features", "enumset", + "esp-config", + "esp-sync", "linked_list_allocator", + "rlsf", ] [[package]] name = "esp-backtrace" -version = "0.16.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c304bbe17df32db8bc0027a9da989aa3efebbd4e7a79d58850deb29e2af577f" +checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669" dependencies = [ "cfg-if", - "esp-build", + "document-features", "esp-config", - "esp-metadata", + "esp-metadata-generated", "esp-println", - "heapless", + "heapless 0.9.2", + "riscv", "semihosting", + "xtensa-lx", ] [[package]] name = "esp-bootloader-esp-idf" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3cb352a8df9c459d0bdf06957cb8293b8bc574138e8c546949955b29c485769" +checksum = "02a56964ab5479ac20c9cf76fa3b0d3f2233b20b5d8554e81ef5d65f63c20567" dependencies = [ - "chrono", + "cfg-if", "document-features", "embedded-storage", "esp-config", - "md-5", + "esp-hal-procmacros", + "esp-metadata-generated", + "esp-rom-sys", + "jiff", "strum", ] -[[package]] -name = "esp-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "837020ff95fbf4c15c206541dda7994f1bbe6e1505e36a6a5ecb51fdb61656d7" -dependencies = [ - "quote", - "syn", - "termcolor", -] - [[package]] name = "esp-config" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8c4c95d8d6243ddb39efe1fcf2524c9becd0f86bb3e24048ed30b4f553609f" +checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c" dependencies = [ "document-features", + "esp-metadata-generated", "serde", - "serde_json", + "serde_yaml", + "somni-expr", ] [[package]] name = "esp-hal" -version = "1.0.0-beta.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d973697621cd3eef9c3f260fa8c1af77d8547cfc92734255d8e8ddf05c7d331" +checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ - "basic-toml", "bitfield", - "bitflags 2.9.0", + "bitflags 2.9.1", "bytemuck", "cfg-if", "critical-section", @@ -729,25 +737,29 @@ dependencies = [ "document-features", "embassy-embedded-hal", "embassy-futures", - "embassy-sync 0.6.2", + "embassy-sync 0.7.2", "embassy-usb-driver", "embassy-usb-synopsys-otg", "embedded-can", "embedded-hal 1.0.0", "embedded-hal-async", - "embedded-io", - "embedded-io-async", + "embedded-io 0.6.1", + "embedded-io 0.7.1", + "embedded-io-async 0.6.1", + "embedded-io-async 0.7.0", "enumset", - "esp-build", "esp-config", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata-generated", "esp-riscv-rt", + "esp-rom-sys", + "esp-sync", "esp-synopsys-usb-otg", "esp32", "esp32c2", "esp32c3", "esp32c6", + "esp32h2", "esp32s2", "esp32s3", "fugit", @@ -758,7 +770,8 @@ dependencies = [ "rand_core 0.6.4", "rand_core 0.9.3", "riscv", - "serde", + "sha1", + "sha2", "strum", "ufmt-write", "xtensa-lx", @@ -766,80 +779,139 @@ dependencies = [ ] [[package]] -name = "esp-hal-embassy" -version = "0.8.1" +name = "esp-hal-procmacros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e025a7a7a0affdb4ff913b5c4494aef96ee03d085bf83c27453ae3a71d50da6" +dependencies = [ + "document-features", + "object", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", + "termcolor", +] + +[[package]] +name = "esp-metadata-generated" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87807cdf22124ba46dbac7d7f6d3ee2e7f06c4e461f3783443464d868101647d" +checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3" + +[[package]] +name = "esp-phy" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf" dependencies = [ "cfg-if", - "critical-section", "document-features", - "embassy-executor", - "embassy-sync 0.6.2", - "embassy-time", - "embassy-time-driver", - "embassy-time-queue-utils", - "esp-build", + "esp-config", + "esp-hal", + "esp-metadata-generated", + "esp-sync", + "esp-wifi-sys", +] + +[[package]] +name = "esp-println" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592" +dependencies = [ + "document-features", + "esp-metadata-generated", + "esp-sync", + "log", + "portable-atomic", +] + +[[package]] +name = "esp-radio" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684c4de2f8907b73c9b891fbda65286a86d34fced4b856f36a7896c211f2f265" +dependencies = [ + "allocator-api2", + "cfg-if", + "document-features", + "embassy-net-driver", + "enumset", + "esp-alloc", "esp-config", "esp-hal", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata-generated", + "esp-phy", + "esp-radio-rtos-driver", + "esp-sync", + "esp-wifi-sys", + "heapless 0.9.2", + "instability", + "num-derive", + "num-traits", "portable-atomic", - "static_cell", + "portable_atomic_enum", + "xtensa-lx-rt", ] [[package]] -name = "esp-hal-procmacros" -version = "0.18.0" +name = "esp-radio-rtos-driver" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73164008cb2eada2ef85e6b0e459001d851f9b8e65e96e0d594bdfa8cf1b813b" +checksum = "543bc31d1851afd062357e7810c1a9633f282fd3993583499a841ab497cbca6c" + +[[package]] +name = "esp-riscv-rt" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502744a5b1e7268d27fd2a4e56ad45efe42ead517d6c517a6961540de949b0ee" dependencies = [ - "darling", "document-features", - "litrs", - "object", - "proc-macro-crate", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", + "riscv", + "riscv-rt", ] [[package]] -name = "esp-metadata" -version = "0.7.0" +name = "esp-rom-sys" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0154d59933c2419ef25a01938517cc6969f47b6af53ebb34c279393aa20d9654" +checksum = "cd66cccc6dd2d13e9f33668a57717ab14a6d217180ec112e6be533de93e7ecbf" dependencies = [ - "anyhow", - "basic-toml", - "serde", - "strum", + "cfg-if", + "document-features", + "esp-metadata-generated", ] [[package]] -name = "esp-println" -version = "0.14.0" +name = "esp-storage" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae8b38d5fdc1d29d823c4737f18edfb0ccf0406985cf893f87c0cfc26a6ab33" +checksum = "1495fc1f5549bdd840b52d9ceb201746200e1620d2636f46958c11e765623b80" dependencies = [ - "critical-section", "document-features", - "esp-build", - "esp-metadata", - "log", - "portable-atomic", + "embedded-storage", + "esp-hal", + "esp-hal-procmacros", + "esp-metadata-generated", + "esp-rom-sys", + "esp-sync", ] [[package]] -name = "esp-riscv-rt" -version = "0.11.0" +name = "esp-sync" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05c2badd16cbd6307d463090615332b77c17a6766b41ba5fe5bb783310e8af6" +checksum = "d44974639b4e88914f83fe60d2832c00276657d7d857628fdfc966cc7302e8a8" dependencies = [ + "cfg-if", "document-features", + "embassy-sync 0.6.2", + "embassy-sync 0.7.2", + "esp-metadata-generated", "riscv", - "riscv-rt-macros", + "xtensa-lx", ] [[package]] @@ -855,48 +927,20 @@ dependencies = [ "vcell", ] -[[package]] -name = "esp-wifi" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3700028d3d2ee57e6d2a5c5f60544711052f8d394e73a6f534b538fbfb9d058c" -dependencies = [ - "allocator-api2", - "cfg-if", - "critical-section", - "document-features", - "embassy-net-driver", - "embedded-io", - "embedded-io-async", - "enumset", - "esp-alloc", - "esp-build", - "esp-config", - "esp-hal", - "esp-metadata", - "esp-wifi-sys", - "num-derive", - "num-traits", - "portable-atomic", - "portable_atomic_enum", - "rand_core 0.9.3", - "xtensa-lx-rt", -] - [[package]] name = "esp-wifi-sys" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5438361891c431970194a733415006fb3d00b6eb70b3dcb66fd58f04d9b39" +checksum = "89b6544f6f0cb86169d1f93ba2101a8d50358a040c5043676ed86b793e09b12c" dependencies = [ "anyhow", ] [[package]] name = "esp32" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ddfdb413ef0739d84bd3fce771e6e0feb6457ac1bcdd7a1dcd6326061e919d" +checksum = "b76170a463d18f888a1ad258031901036fd827a9ef126733053ba5f8739fb0c8" dependencies = [ "critical-section", "vcell", @@ -904,9 +948,9 @@ dependencies = [ [[package]] name = "esp32c2" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ec669dc34787e73c8a7d99059fcdbd800cb0699c2ea9cd0a3d99cb5e9caefb" +checksum = "e62cf8932966b8d445b6f1832977b468178f0a84effb2e9fda89f60c24d45aa3" dependencies = [ "critical-section", "vcell", @@ -914,9 +958,9 @@ dependencies = [ [[package]] name = "esp32c3" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c0b2a8e8efa1755a28ca3ef54c85436f76531ba93dc79b55d5330349d067d7" +checksum = "356af3771d0d6536c735bf71136594f4d1cbb506abf6e0c51a6639e9bf4e7988" dependencies = [ "critical-section", "vcell", @@ -924,9 +968,19 @@ dependencies = [ [[package]] name = "esp32c6" -version = "0.20.0" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5e511df672d79cd63365c92045135e01ba952b6bddd25b660baff5e1110f6b" +dependencies = [ + "critical-section", + "vcell", +] + +[[package]] +name = "esp32h2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8009092b2a8f41532ce836ea7403b657eca7cd396c4513c650ade648831ed76" +checksum = "ed4a50bbd1380931e095e0973b9b12f782a9c481f2edf1f7c42e7eb4ff736d6d" dependencies = [ "critical-section", "vcell", @@ -934,9 +988,9 @@ dependencies = [ [[package]] name = "esp32s2" -version = "0.28.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4391e38b14eb59a675e816598bde7d9b8c8f43a695a284bd97bcd0cf3092404" +checksum = "98574d4c577fbe888fe3e6df7fc80d25a05624d9998f7d7de1500ae21fcca78f" dependencies = [ "critical-section", "vcell", @@ -944,9 +998,9 @@ dependencies = [ [[package]] name = "esp32s3" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a4c6fd31207a297fc29d2b8f4da27facf45f8c83041f7c0f978aa65ab367c9" +checksum = "1810d8ee4845ef87542af981e38eb80ab531d0ef1061e1486014ab7af74c337a" dependencies = [ "critical-section", "vcell", @@ -954,9 +1008,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "fnv" @@ -1021,9 +1075,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1041,9 +1095,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heapless" @@ -1055,6 +1109,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -1063,41 +1127,17 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.63" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "cc", + "digest", ] [[package]] @@ -1108,9 +1148,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -1133,15 +1173,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1151,20 +1191,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "js-sys" -version = "0.3.77" +name = "jiff" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ - "once_cell", - "wasm-bindgen", + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] name = "libc" -version = "0.2.171" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linked_list_allocator" @@ -1177,9 +1231,6 @@ name = "litrs" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" -dependencies = [ - "proc-macro2", -] [[package]] name = "log" @@ -1193,30 +1244,11 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "minijinja" -version = "2.9.0" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" -dependencies = [ - "serde", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "nb" @@ -1241,7 +1273,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1255,39 +1287,34 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - [[package]] name = "opaque-debug" version = "0.3.1" @@ -1334,9 +1361,18 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] [[package]] name = "portable_atomic_enum" @@ -1356,7 +1392,7 @@ checksum = "a33fa6ec7f2047f572d49317cca19c87195de99c6e5b6ee492da701cfe02b053" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1367,35 +1403,13 @@ checksum = "bbc83ee4a840062f368f9096d80077a9841ec117e17e7f700df81958f1451254" [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -1414,12 +1428,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r0" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" - [[package]] name = "ral-registers" version = "0.1.3" @@ -1443,9 +1451,9 @@ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" [[package]] name = "riscv" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" dependencies = [ "critical-section", "embedded-hal 1.0.0", @@ -1456,13 +1464,13 @@ dependencies = [ [[package]] name = "riscv-macros" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1471,15 +1479,45 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" +[[package]] +name = "riscv-rt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d07b9f3a0eff773fc4df11f44ada4fa302e529bff4b7fe7e6a4b98a65ce9174" +dependencies = [ + "riscv", + "riscv-pac", + "riscv-rt-macros", + "riscv-target-parser", +] + [[package]] name = "riscv-rt-macros" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71814687c45ba4cd1e47a54e03a2dbc62ca3667098fbae9cc6b423956758fa" +checksum = "15c3138fdd8d128b2d81829842a3e0ce771b3712f7b6318ed1476b0695e7d330" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "riscv-target-parser" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1376b15f3ff160e9b1e8ea564ce427f2f6fcf77528cc0a8bf405cb476f9cea7" + +[[package]] +name = "rlsf" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "svgbobdoc", ] [[package]] @@ -1493,9 +1531,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -1517,62 +1555,69 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] -name = "serde_json" -version = "1.0.140" +name = "serde_yaml" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ + "indexmap", "itoa", - "memchr", "ryu", "serde", + "unsafe-libyaml", ] [[package]] -name = "serde_spanned" -version = "0.6.8" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "serde", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signature" version = "2.2.0" @@ -1588,31 +1633,46 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "cfg-if", - "heapless", + "heapless 0.8.0", "managed", ] [[package]] name = "snafu" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "somni-expr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed9b7648d5e8b2df6c5e49940c54bcdd2b4dd71eafc6e8f1c714eb4581b0f53" +dependencies = [ + "somni-parser", ] +[[package]] +name = "somni-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c" + [[package]] name = "ssh-cipher" version = "0.2.0" @@ -1654,34 +1714,47 @@ dependencies = [ name = "ssh-stamp" version = "0.1.0" dependencies = [ + "bcrypt", "cfg-if", + "digest", "ed25519-dalek", "edge-dhcp", "edge-nal", "edge-nal-embassy", + "embassy-embedded-hal", "embassy-executor", "embassy-futures", "embassy-net", - "embassy-sync 0.7.0", - "embassy-time", - "embedded-io-async", + "embassy-sync 0.7.2", + "embassy-time 0.5.0", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", "esp-alloc", "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", - "esp-hal-embassy", "esp-println", - "esp-wifi", + "esp-radio", + "esp-rom-sys", + "esp-storage", "getrandom", - "heapless", + "heapless 0.9.2", "hex", + "hmac", "log", + "paste", "portable-atomic", + "pretty-hex", + "sha2", "smoltcp", + "snafu", "ssh-key", "static_cell", + "subtle", "sunset", "sunset-async", + "sunset-sshwire-derive", ] [[package]] @@ -1692,9 +1765,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_cell" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" +checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23" dependencies = [ "portable-atomic", ] @@ -1724,7 +1797,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.104", ] [[package]] @@ -1735,8 +1808,8 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" -version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=cb6c720#cb6c72008d332d6a066dbcc0b1712bacfb07ab0e" +version = "0.3.0" +source = "git+https://github.com/mkj/sunset?rev=e05ffb6#e05ffb6cb96e173b306f41bf31b87f6a20fff5b2" dependencies = [ "aes", "ascii", @@ -1746,9 +1819,9 @@ dependencies = [ "curve25519-dalek", "digest", "ed25519-dalek", - "embedded-io", + "embedded-io 0.6.1", "getrandom", - "heapless", + "heapless 0.8.0", "hmac", "log", "poly1305", @@ -1766,14 +1839,13 @@ dependencies = [ [[package]] name = "sunset-async" -version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=cb6c720#cb6c72008d332d6a066dbcc0b1712bacfb07ab0e" +version = "0.3.0" +source = "git+https://github.com/mkj/sunset?rev=e05ffb6#e05ffb6cb96e173b306f41bf31b87f6a20fff5b2" dependencies = [ "embassy-futures", - "embassy-sync 0.7.0", - "embedded-io-async", + "embassy-sync 0.7.2", + "embedded-io-async 0.6.1", "log", - "pin-utils", "portable-atomic", "sunset", ] @@ -1781,16 +1853,29 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=cb6c720#cb6c72008d332d6a066dbcc0b1712bacfb07ab0e" +source = "git+https://github.com/mkj/sunset?rev=e05ffb6#e05ffb6cb96e173b306f41bf31b87f6a20fff5b2" dependencies = [ "virtue", ] +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64 0.13.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + [[package]] name = "syn" -version = "2.0.100" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1798,45 +1883,52 @@ dependencies = [ ] [[package]] -name = "termcolor" -version = "1.4.1" +name = "syn" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ - "winapi-util", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "toml" -version = "0.8.20" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "winapi-util", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] @@ -1858,6 +1950,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "universal-hash" version = "0.5.1" @@ -1868,13 +1966,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "usb-device" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" dependencies = [ - "heapless", + "heapless 0.8.0", "portable-atomic", ] @@ -1904,67 +2008,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "winapi-util" @@ -1975,65 +2021,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2109,9 +2096,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -2129,42 +2116,33 @@ dependencies = [ [[package]] name = "xtensa-lx" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68737a6c8f32ddcd97476acf68ddc6d411697fd94f64a601af16854b74967dff" +checksum = "e012d667b0aa6d2592ace8ef145a98bff3e76cca7a644f4181ecd7a916ed289b" dependencies = [ "critical-section", - "document-features", ] [[package]] name = "xtensa-lx-rt" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235815f34d1bf9c2f9c07917e2b63efbcab5ca5ce9d8faddb97b7105eed1ade3" +checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845" dependencies = [ - "anyhow", "document-features", - "enum-as-inner", - "minijinja", - "r0", - "serde", - "strum", - "toml", "xtensa-lx", "xtensa-lx-rt-proc-macros", ] [[package]] name = "xtensa-lx-rt-proc-macros" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1ab67b22f0576b953a25c43bdfed0ff84af2e01ced85e95c29e7bac6bf2180" +checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450" dependencies = [ - "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2184,5 +2162,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] diff --git a/Cargo.toml b/Cargo.toml index 57c0d22..0128229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,3 @@ -# TODO: Potentially switch to cargo_xtask for easy re-targetting, i.e: -# https://github.com/card-io-ecg/card-io-fw/blob/main/Cargo.toml - [package] name = "ssh-stamp" version = "0.1.0" @@ -11,114 +8,145 @@ license = "MIT OR Apache-2.0" [dependencies] cfg-if = "1" ed25519-dalek = { version = "2", default-features = false } -embassy-executor = { version = "0.7"} +embassy-executor = { version = "0.9"} embassy-net = { version = "0.7", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } smoltcp = { version = "0.12", default-features = false, features = ["medium-ethernet", "socket-raw"]} -embassy-time = { version = "0.4" } -embedded-io-async = "0.6" -esp-alloc = "0.8" -esp-backtrace = { version = "0.16", features = [ - "exception-handler", +embassy-time = { version = "0.5" } +embedded-io-async = "0.7" +esp-alloc = "0.9" +esp-backtrace = { version = "0.18", features = [ "panic-handler", "println", ] } -esp-hal = { version = "1.0.0-beta.1", features = [ "unstable" ] } -esp-hal-embassy = { version = "0.8"} -esp-println = { version = "0.14", features = ["log-04"]} -esp-wifi = { version = "0.14", features = ["wifi", "esp-alloc"] } +esp-hal = { version = "1.0.0", features = [ "unstable" ] } +#esp-hal-embassy = { version = "0.9" } +esp-println = { version = "0.16", features = ["log-04"]} +esp-radio = { version = "0.17", features = ["wifi"] } hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } +sunset = { git = "https://github.com/mkj/sunset", rev = "e05ffb6", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { git = "https://github.com/mkj/sunset", rev = "e05ffb6", default-features = false, features = ["multi-thread"]} +sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "e05ffb6", default-features = false } +# sunset = { path = "../sunset", default-features = false, features = ["openssh-key", "embedded-io"]} +# sunset-async = { path = "../sunset/async", default-features = false, features = ["multi-thread"]} +# sunset-sshwire-derive = { path = "../sunset/sshwire-derive", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } -sunset = { git="https://github.com/mkj/sunset", rev = "cb6c720", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", rev = "cb6c720", default-features = false} embassy-sync = "0.7" -heapless = "0.8" +heapless = "0.9" embassy-futures = "0.1" edge-dhcp = "0.6" edge-nal = "0.5" edge-nal-embassy = "0.6" +#sequential-storage = { version = "4", features = ["heapless"] } +esp-storage = { version = "0.8" } +embedded-storage = "0.3" +embassy-embedded-hal = "0.5" +bcrypt = { version = "0.17", default-features = false } +subtle = { version = "2", default-features = false } +hmac = { version = "0.12", default-features = false } +sha2 = { version = "0.10", default-features = false } +digest = { version = "0.10", default-features = false, features = ["rand_core", "subtle"] } +embedded-storage-async = "0.4" portable-atomic = "1" -esp-bootloader-esp-idf = "0.1" +esp-bootloader-esp-idf = { version = "0.4", features = ["esp-rom-sys"] } +snafu = { version = "0.8", default-features = false } +paste = "1" +pretty-hex = { version = "0.4", default-features = false } +esp-rom-sys = { version = "0.1" } [profile.dev] # Rust debug is too slow. # For debug builds always builds with some optimization -opt-level = 3 +opt-level = 0 +debug = 2 +debug-assertions = true [profile.release] codegen-units = 1 # LLVM can perform better optimizations using a single thread debug = 2 -debug-assertions = false +debug-assertions = true incremental = false lto = 'fat' opt-level = 3 -overflow-checks = false +overflow-checks = true [profile.esp32s2] inherits = "release" opt-level = "s" # Optimize for size. -overflow-checks = false -lto = 'fat' -[features] -#default = ["esp32c6"] +[profile.dev.package.esp-storage] +opt-level = "s" + +[profile.dev.package.esp-radio] +opt-level = "s" +[features] +ipv6 = [] +default = ["esp32c6"] # MCU options esp32 = [ "esp-hal/esp32", "esp-backtrace/esp32", - "esp-wifi/esp32", - "esp-hal-embassy/esp32", + "esp-radio/esp32", + "esp-rom-sys/esp32", + "esp-bootloader-esp-idf/esp32", "esp-println/esp32", - "embassy-executor/task-arena-size-40960", + "esp-storage/esp32", ] esp32c2 = [ "esp-hal/esp32c2", "esp-backtrace/esp32c2", - "esp-wifi/esp32c2", - "esp-hal-embassy/esp32c2", + "esp-radio/esp32c2", + "esp-rom-sys/esp32c2", + "esp-bootloader-esp-idf/esp32c2", "esp-println/esp32c2", - "embassy-executor/task-arena-size-40960", + "esp-storage/esp32c2", ] esp32c3 = [ "esp-hal/esp32c3", "esp-backtrace/esp32c3", - "esp-wifi/esp32c3", - "esp-hal-embassy/esp32c3", + "esp-radio/esp32c3", + "esp-rom-sys/esp32c3", + "esp-bootloader-esp-idf/esp32c3", "esp-println/esp32c3", - "embassy-executor/task-arena-size-40960", + "esp-storage/esp32c3", ] #esp32c5 = [ # "esp-hal/esp32c5", # "esp-backtrace/esp32c5", -# "esp-wifi/esp32c5", +# "esp-radio/esp32c5", # "esp-hal-embassy/esp32c5", # "esp-println/esp32c5", +# "esp-storage/esp32c5", # "embassy-executor/task-arena-size-40960", #] esp32c6 = [ "esp-hal/esp32c6", "esp-backtrace/esp32c6", - "esp-wifi/esp32c6", - "esp-hal-embassy/esp32c6", + "esp-radio/esp32c6", + "esp-rom-sys/esp32c6", + "esp-bootloader-esp-idf/esp32c6", "esp-println/esp32c6", - "embassy-executor/task-arena-size-40960", + "esp-storage/esp32c6", ] esp32s2 = [ "esp-hal/esp32s2", "esp-backtrace/esp32s2", - "esp-wifi/esp32s2", - "esp-hal-embassy/esp32s2", + "esp-radio/esp32s2", + "esp-rom-sys/esp32s2", + "esp-bootloader-esp-idf/esp32s2", +# "esp-hal-embassy/esp32s2", "esp-println/esp32s2", - "embassy-executor/task-arena-size-32768", + "esp-storage/esp32s2", ] esp32s3 = [ "esp-hal/esp32s3", "esp-backtrace/esp32s3", - "esp-wifi/esp32s3", - "esp-hal-embassy/esp32s3", + "esp-radio/esp32s3", + "esp-rom-sys/esp32s3", + "esp-bootloader-esp-idf/esp32s3", "esp-println/esp32s3", - "embassy-executor/task-arena-size-40960", + "esp-storage/esp32s3", ] diff --git a/docs/nlnet/mou.md b/docs/nlnet/mou.md new file mode 100644 index 0000000..38bc0ea --- /dev/null +++ b/docs/nlnet/mou.md @@ -0,0 +1,369 @@ +takentaal v1.0 + + + +# SSH Stamp + + + +The aim of this project (available at https://github.com/brainstorm/ssh-stamp) is to write software that executes code on a microprocessor and allows transferring data between the internet and various electronic interfaces. In other words: a logic "bridge" between the internet and low level electronic interfaces. + + + +In its conception, the SSH Stamp is a secure bridge between wireless and a serial port (ubiquitous interface also known as "UART"), implemented in the Rust programming language. The Rust programming language offers convenient memory safety guarantees that are meaningful for this project from a systems security perspective. + + + +From a practical standpoint, SSH Stamp allows its users (from different levels of expertise) to audit, monitor, defend, operate, understand and maintain a variety of low level electronics remotely (and securely) through the internet at large. + + + +## {240} Password-based authentication (GH issue #20) + + + +Currently, SSH Stamp accepts ANY arbitrary password when logging in via the SSH protocol. This is a well known and big security issue that requires attention to be put on the password authentication handler. + + + +On the server side, there should be a persistent setting that completely disables password-based authentication (only accepting safer key-based authentication method (see related GH issues #21 and #23)). + + + +- {120} Implement password authentication server-side toggle. + +- {120} Implement handler business logic and connect it with GH issue #23. + + + +## {400} Have the server generate a unique server keypair and store it persistently (on boot) (GH issue #38) + + + +Currently, SSH Stamp accepts any (hardcoded) private key, as implied in the top-level documentation. This is a well known and big security issue that requires attention to be put on the public/private key authentication handler. + + + +Server keys are used by the client to identify the server in a secure way. Ideally those keys should be generated at first boot and optionally re-generated if the user desires to do so. + + + +- {400} Implement in-device private key generation and storage, test and audit its logic. + + + +## {200} Public key-based authentication (GH issue #21) + + + +The user should be able to provide concatenate public keys as "authorized_keys" via environment variable(s) (see related GH issue #23). + + + +- {200} Implement pubkey authentication environment variable provisioning. + + + +## {1000} Provisioning based on (SSH) environment variables (GH issue #23) + + + +Many SSH clients pass environment variables to the server for many purposes. + + + +This task is meant to exploit this commonly used pattern to simplify provisioning of new SSH Stamps. Environment variables will be used to, among other functions: + + + +- {200} Define and persist the UART bridge parameters such as BAUD speed, stop bits. + +- {200} Compile a spreadsheet of commercially available "stamp boards" and set a default, unused UART physical pins pair. Allow the user to alter that default via SSH env settings (see GH issue #23). + +- {200} Pass the password and store it for subsequent sessions, disallowing un-authenticated password changes after first initialization. + +- {200} Pass the private key(s) and/or trigger in-device private key generation. + +- {200} Have an environment variable (or command) that instructs the processor to factory reset all non-volatile storage but keeps the firmware intact. + + + +This approach avoids having complicated device-programming-time deployment(s) of sensitive cryptographic material. + + + +Furthermore, provisioning the device this way yields a superior user experience: a device can be shipped on an unprovisioned state and subsequently be **NON INTERACTIVELY** provisioned via SSH env vars. + + + +The emphasis on non-interactivity (and idempotency) is due to time tested patterns on the IT automation industry (see: Ansible, Puppet, Chef, SaltStack, etc...). + + + +## {1700} Firmware update via SFTP (GH issue #24) + + + +Instead of regular "out of band" firmware OTA update mechanisms, SSH Stamp firmware will be updated via its own secure protocol: SCP/SFTP. + + + +The reason behind this approach is that we can leverage the existing SSH server AAA mechanisms to deploy new firmware securely. + + + +Ideally, this process shouldn't have to involve the bootloader since this should happen at a "userspace + reboot" level. + + + +- {1300} Implement SCP/SFTP as per IETF RFCs. Only the functionality needed to support firmware update. + +- {300} Implement OTA mechanism on device. + +- {100} Audit and thoroughly check that new firmware cannot be altered pre-auth. + + + +The changes described above will most likely need to be applied to SSH Stamp's main current "core" dependency: "sunset". This will therefore constitute an external (to SSH Stamp) OSS contribution that will be used in our software. + + + +## {2000} FSMs + SansIO refactor (GH issue #25) + + + +This is a relatively big refactor but needed pre-requisite to introduce other low level I/O (protocols) beside UART such as SPI/I2C/CAN... it is also a research task since there's uncertainty on whether such a bridge between a (relatively slow) network stack and (generally faster) electrical protocols will tolerate strict timing demands. + + + +Moving to compile-time validated finite state machines (FSMs) will hopefully make pre-auth attacks less likely to be successful on SSH Stamp. Such attacks have been unfortunately common in recent SSH server explorations, see: + + + +https://threatprotect.qualys.com/2025/04/21/erlang-otp-ssh-server-remote-code-execution-vulnerability-cve-2025-32433/ + + + +https://www.runzero.com/blog/sshamble-unexpected-exposures-in-the-secure-shell/ + + + +The trigger of this refactoring idea is https://www.firezone.dev/blog/sans-io + + + +This approach should ideally be combined with some kind of performance profiling to ensure we don't regress after incorporating those changes. + + + +- {1100} Implement a PoC state machine on std outside SSH Stamp, validate corner cases there. + +- {500} Adapt PoC for no_std usage and structure. + +- {400} Refactor codebase for SansIO + FSM. + + + +## {900} General performance engineering (GH issue #28) + + + +Profiling and optimisation are tangentially mentioned towards the end of issue #25, but better tooling/process is needed to maximise the performance of this firmware on different fronts: + + + +- {100} Choose appropriate overall and per-task heap size allocations, i.e: bisect heap size until finding a clear performance degradation under different loads. + +- {100} Perform benchmarks for different RX/TX buffer sizes on both SSH network stack and UART packet queues. + +- {100} Minimise the build for size as much as possible without discarding security checks (overflow protections). + +- {300} Add CI/CD instrumentation to track all of the above over time, avoiding future regressions. + +- {300} Profile hot spots across the app, including cryptographic primitives, and optimise them. + + + +Those performance engineering techniques should be appropriately documented (preferably with examples) and ideally implemented in CI to monitor regressions. + + + +## {1000} Documentation (GH issue #30) + + + +Mid project, when the code based reaches relative maturity and stability, we should make a concerted effort to write and review docs at all levels: user, dev, architecture.md + + + +- {200} Make sure onboarding (and quickstart) is fully documented and flawless. + +- {200} Refer and implement an ARCHITECTURE.md document. Advice taken from Matklad's blogpost: https://matklad.github.io/2021/02/06/ARCHITECTURE.md.html + +- {120} List and document all the effects of environment variables from GH issue #23. + +- {120} List and document both supported and unsupported devices and boards. + +- {120} List and document ways to debug/profile/tweak firmware for different feature flags and scenarios. + +- {120} Have at least one real world scenario documented. + +- {120} Define clearly what's our threat model, i.e: we'll not protect against any physical attacks. OTOH, pre-auth attacks (exploit bad code logic that Rust doesn't protect against) can be the worst case scenario for SSH Stamp. + + + +## {600} Testing (GH issue #37) + + + +Test that the firmware compiles, has good coverage and there are no performance regressions as mentioned in other tasks. This task is a more focused effort so that there is comprehensive testing in both software and hardware. + + + +- {200} Leverage embedded-test Rust crate for hardware testing (HIL). + +- {200} Physically build a test harness with most of the supported targets so that they can be easily tested (ideally via CI/CD hooks). + +- {200} Test that a IPV6 address is served over DHCPv6. ULA, DHCP-PD and other common scenarios work as they should. + + + +## {500} Compile project for all Espressif ESP32 WiFi targets (GH issue #18) + + + +The current project only compiles for the ESP32-C6, as in indicated in various feature flags and linker options. + + + +Find a non-intrusive way to compile for as many Espressif targets as possible. + + + +- {400} Implement cargo build targets for each Espressif microcontroller. + +- {100} Resolve compilation issues that arise for each target. + + + +## {800} Implement firmware support for alternative wifi enabled microcontrollers (other than Espressif) (GH issue #19) + + + +Implementing and testing support for other microcontrollers from a completely different manufacturer. + + + +- {200} Compile and flash SSH Stamp into this target with UART support. + +- {600} Try to support WiFi (currently not supported at the upstream HAL). + + + +## {2000} Add mlkem768x25519 (to sunset?) and integrate into SSH-Stamp (GH issue #34) + + + +Add mlkem768x25519 (post Quantum resistant crypto) to sunset dependency and integrate into SSH-Stamp. + + + +- {1500} Implement and test MLKEM upstream in sunset. + +- {500} Test in SSH Stamp. + + + +## {1080} Bootloader audit and UX improvements (GH issue #35) + + + +Some users will expect that the bridge is established against their own dev board USB2TTL converted UART0 port instead of custom discrete pins. + + + +The current approach is to use UART1 and custom pins (soon to be GPIO9 and GPIO10 by default) so that boot messages and early SSH2UART are not intermixed. + +This task will have to: + + + +- {600} Explore how the default-shipped ESP-IDF 5.x bootloader is packaged in esp-hal's firmware. + +- {240} Conditionally modify the bootloader accordingly so no boot messages are displayed when powering up the board and no println!/debug messages from the user level firmware itself are shown either. In other words: Only bridged SSH2UART messages should be conveyed. + +- {120} Make sure those bootloader modifications are not an impediment to basic operations such as re-flashing the IC (i.e: perhaps espflash expects some bootloader output to trigger a flashing operation?). + +- {120} Document a path towards a secure bootloader that is compatible with the HAL/device **and** respects the aforementioned points. + + + +This exploration into the bootloader inner workings can also pave the way to assess whether improvements in secure boot are interesting to pursue and how. + + + +## {840} Implement WiFi STA mode (GH issue #36) + + + +Currently SSH Stamp boots up in AP (Access Point) mode and expects DHCP clients. This tasks would put SSH Stamp in STA (Station mode) or acting as a WiFi client instead. + + + +This is attractive on more permanent installations where SSH Stamp can be part of a larger local wireless network. + + + +- {240} Make appropriate changes to device HAL initialization. + +- {240} Verify that those changes do not break prior assumptions. + +- {240} Align onboarding process for that mode's particularities. + +- {120} Make the setting configurable (see GH issue #23). + + + +## {200} Licensing (GH issue #22) + + + +There's some borrowed code from SSH Stamp's main dependency: sunset. Clarify this properly and/or find the right way to compose LICENSE wording. + + + +- {200} Process feedback and apply changes from NLNet licensing experts. + + + +## {2000} Implement bridging support for other electrical protocols such as: CAN, SPI and I2C. + + + +Encapsulate transaction-oriented protocols (CAN, SPI, I2C) over a plain byte stream. This task assumes that the the processor's timing characteristics are suitable. At a protocol level, for CAN packet encapsulation there's "slcan" and "GVRET" among other more exotic encapsulation protocols. + + + +Equivalents for SPI-over-Network or I2C-over-Network are a much more experimental and uncharted territory, perhaps the Bus Pirate command protocol would be the closest match. + + + +- {1000} Implement slcan and/or GVRET for SSH_to_CAN support. + +- {1000} Investigate and implement proof of concept for SPI and/or I2C. + + + +## {600} Final release + + + +Final review on all the work done and refinement, optimization and security audit handover. + + + +- {120} Stable release. + +- {240} Process feedback from security audit and accessibility scan. + +- {240} Final release with updated documentation. diff --git a/docs/nlnet/plan.md b/docs/nlnet/plan.md new file mode 100644 index 0000000..989acea --- /dev/null +++ b/docs/nlnet/plan.md @@ -0,0 +1,34 @@ +```mermaid +gantt + title SSH Stamp (a.k.a esp-ssh-rs) development plan under NLNet grant + dateFormat YYYY-MM-DD + excludes weekends + tickInterval 1day + weekday monday + todayMarker off + axisFormat %e + section Production + Fix password auth : passwd, 2025-05-01, 24h + Fix pubkey auth : pubkey, after passwd, 24h + UART perf : uart_intr, after pubkey, 12h + section Provisioning + Provisioning : prov, after uart_intr, 16h + OTA updates : ota, after prov, 12h + section Docs + usage docs : usage_docs, after ota, 4h + dev docs : dev_docs, after ota, 2h + section Robustness + #forbid(unsafe) : no_unsafe, after ota, 12h + sans-io refactor : sans_io, after uart_intr, 16h + section Multi-target + Espressif chips : all_espressif, after no_unsafe, 12h + Other chip1 : chip1, after all_espressif, 20h + Other chip2 : chip2, after chip1, 18h + section Testing + CI/CD : ci, after chip2, 16h + hardware in test loop : HIL, after ci, 21h + Users test : user_tests, after dev_docs, 9h + section Security + Self audit : self_sec_audit, after all_espressif, 10h + NLNet security audit? : nlnet_sec_audit, after all_espressif, 45h +``` diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..8eaf718 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,8 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +app_config, data, nvs, 0x9000, 0x2000, +otadata, data, ota, 0xd000, 0x2000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x200000, +ota_0, app, ota_0, 0x210000, 0x100000, +ota_1, app, ota_1, 0x310000, 0x100000, \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 17c702b..f3683df 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,5 @@ [toolchain] -channel = "stable-2025-04-03" +#channel = "stable-2025-04-03" +channel = "nightly" components = ["rust-src"] targets = ["riscv32imac-unknown-none-elf"] diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..6917308 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,381 @@ +use core::net::Ipv4Addr; +#[cfg(feature = "ipv6")] +use core::net::Ipv6Addr; +use embassy_net::{Ipv4Cidr, StaticConfigV4}; +#[cfg(feature = "ipv6")] +use embassy_net::{Ipv6Cidr, StaticConfigV6}; +use heapless::{String}; + +use esp_println::dbg; + +use bcrypt; +use hmac::{Hmac, Mac}; +use sha2::Sha256; +use subtle::ConstantTimeEq; + +use sunset::packets::Ed25519PubKey; +use sunset::{sshwire, KeyType, Result}; +use sunset::{ + sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, + SignKey, +}; + +use crate::pins::SerdePinConfig; +use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; + +#[derive(Debug, PartialEq)] +pub struct SSHStampConfig { + //pub first_boot: bool, + pub hostkey: SignKey, + + /// Authentication + pub password_authentication: bool, + pub admin_pw: Option, + pub admin_keys: [Option; KEY_SLOTS], + + /// WiFi + pub wifi_ssid: &'static str, + pub wifi_pw: Option>, + + /// Networking + /// TODO: Populate this field from esp's hardware info or just refer it from HAL? + /// Only intended purpose I see for keeping it here is for spoofing? + pub mac: [u8; 6], + /// `None` for DHCP + pub ipv4_static: Option, + #[cfg(feature = "ipv6")] + pub ipv6_static: Option, + /// UART + pub uart_pins: SerdePinConfig, +} + +impl SSHStampConfig { + /// Bump this when the format changes + pub const CURRENT_VERSION: u8 = 6; + + /// Creates a new config with default parameters. + /// + /// Will only fail on RNG failure. + pub fn new() -> Result { + let hostkey = SignKey::generate(KeyType::Ed25519, None)?; + + // TODO: Those env events come from system's std::env / core::env (if any)... so it shouldn't be unsafe() + let wifi_ssid= DEFAULT_SSID; + let mac = random_mac()?; + let wifi_pw = None; + + let uart_pins = SerdePinConfig::default(); + + Ok(SSHStampConfig { + hostkey, + password_authentication: true, + admin_pw: None, + admin_keys: Default::default(), + wifi_ssid, + wifi_pw, + mac, + ipv4_static: None, + #[cfg(feature = "ipv6")] + ipv6_static: None, + uart_pins, + }) + } + + pub fn set_admin_pw(&mut self, pw: Option<&str>) -> Result<()> { + self.admin_pw = pw.map(|p| PwHash::new(p)).transpose()?; + Ok(()) + } + + pub fn check_admin_pw(&mut self, pw: &str) -> bool { + if let Some(ref p) = self.admin_pw { + p.check(pw) + } else { + false + } + } + + pub(crate) fn default_ssid() -> String<32> { + let mut s = String::<32>::new(); + s.push_str(DEFAULT_SSID).unwrap(); + s + } + + // pub fn config_change(&mut self, conf: SSHConfig) -> Result<()> { + // ServEvent::ConfigChange(); + // } +} + +fn random_mac() -> Result<[u8; 6]> { + let mut mac = [0u8; 6]; + sunset::random::fill_random(&mut mac)?; + // unicast, locally administered + mac[0] = (mac[0] & 0xfc) | 0x02; + Ok(mac) +} + +// a private encoding specific to demo config, not SSH defined. +fn enc_signkey(k: &SignKey, s: &mut dyn SSHSink) -> WireResult<()> { + // need to add a variant field if we support more key types. + match k { + SignKey::Ed25519(k) => k.to_bytes().enc(s), + _ => Err(WireError::UnknownVariant), + } +} + +fn dec_signkey<'de, S>(s: &mut S) -> WireResult +where + S: SSHSource<'de>, +{ + let k: ed25519_dalek::SecretKey = SSHDecode::dec(s)?; + let k = ed25519_dalek::SigningKey::from_bytes(&k); + Ok(SignKey::Ed25519(k)) +} + +// encode Option as a bool then maybe a value +pub(crate) fn enc_option(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + v.enc(s) +} + +pub(crate) fn dec_option<'de, S, T: SSHDecode<'de>>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + bool::dec(s)?.then(|| SSHDecode::dec(s)).transpose() +} + +fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + if let Some(v) = v { + v.address.address().to_bits().enc(s)?; + dbg!("enc_ipv4_config: prefix", &v.address.prefix_len()); + v.address.prefix_len().enc(s)?; + // to u32 + let gw = v.gateway.map(|a| a.to_bits()); + enc_option(&gw, s)?; + } + Ok(()) +} + +#[cfg(feature = "ipv6")] +fn enc_ipv6_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + if let Some(v) = v { + v.address.address().to_bits().enc(s)?; + v.address.prefix_len().enc(s)?; + let gw = v.gateway.map(|a| a.to_bits()); + enc_option(&gw, s)?; + } + Ok(()) +} + +fn dec_ipv4_config<'de, S>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + let opt = bool::dec(s)?; + opt.then(|| { + let ad: u32 = SSHDecode::dec(s)?; + let ad = Ipv4Addr::from_bits(ad); + let prefix: u8 = SSHDecode::dec(s)?; + if prefix > 32 { + // embassy panics, so test it here + return Err(WireError::PacketWrong); + } + let gw: Option = dec_option(s)?; + let gateway = gw.map(|gw| Ipv4Addr::from_bits(gw)); + Ok(StaticConfigV4 { + address: Ipv4Cidr::new(ad, prefix), + gateway, + dns_servers: Default::default(), + }) + }) + .transpose() +} + +#[cfg(feature = "ipv6")] +fn dec_ipv6_config<'de, S>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + let opt = bool::dec(s)?; + opt.then(|| { + let ad: u128 = SSHDecode::dec(s)?; + let ad = Ipv6Addr::from_bits(ad); + let prefix = SSHDecode::dec(s)?; + if prefix > 32 { + // embassy panics, so test it here + return Err(WireError::PacketWrong); + } + let gw: Option = dec_option(s)?; + let gateway = gw.map(|gw| Ipv6Addr::from_bits(gw)); + Ok(StaticConfigV6 { + address: Ipv6Cidr::new(ad, prefix), + gateway, + dns_servers: Vec::new(), + }) + }) + .transpose() +} + +impl SSHEncode for SSHStampConfig { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + enc_signkey(&self.hostkey, s)?; + + // Authentication + self.password_authentication.enc(s)?; + enc_option(&self.admin_pw, s)?; + + for k in self.admin_keys.iter() { + enc_option(k, s)?; + } + + self.wifi_ssid.enc(s)?; + enc_option(&self.wifi_pw, s)?; + self.mac.enc(s)?; + + enc_ipv4_config(&self.ipv4_static, s)?; + #[cfg(feature = "ipv6")] + enc_ipv6_config(&self.ipv6_static, s)?; + + // Encode PinConfig + self.uart_pins.enc(s)?; + + Ok(()) + } +} + +impl<'de> SSHDecode<'de> for SSHStampConfig { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + let hostkey = dec_signkey(s)?; + + // Authentication + let password_authentication = SSHDecode::dec(s)?; + let admin_pw = dec_option(s)?; + let mut admin_keys = [None; KEY_SLOTS]; + for k in admin_keys.iter_mut() { + *k = dec_option(s)?; + } + + let wifi_ssid = SSHDecode::dec(s)?; + let wifi_pw = dec_option(s)?; + + let mac = SSHDecode::dec(s)?; + + let ipv4_static = dec_ipv4_config(s)?; + #[cfg(feature = "ipv6")] + let ipv6_static = dec_ipv6_config(s)?; + + // Not supported by sshwire-derive nor virtue (no Option support) + // let uart_pins = SSHDecode::dec(s)?; + let uart_pins = SSHDecode::dec(s)?; + + Ok(Self { + hostkey, + password_authentication, + admin_pw, + admin_keys, + wifi_ssid, + wifi_pw, + mac, + ipv4_static, + #[cfg(feature = "ipv6")] + ipv6_static, + uart_pins, + }) + } +} + +/// Stores a bcrypt password hash. +/// +/// We use bcrypt because it seems the best password hashing option where +/// memory hardness isn't possible (the rp2040 is smaller than CPU or GPU memory). +/// +/// The cost is currently set to 6, taking ~500ms on a 125mhz rp2040. +/// Time converges to roughly 8.6ms * 2**cost +/// +/// Passwords are pre-hashed to avoid bcrypt's 72 byte limit. +/// rust-bcrypt allows nulls in passwords. +/// We use an hmac rather than plain hash to avoid password shucking +/// (an attacker bcrypts known hashes from some other breach, then +/// brute forces the weaker hash for any that match). +//#[derive(Clone, SSHEncode, SSHDecode, PartialEq)] +#[derive(Clone, PartialEq)] +pub struct PwHash { + salt: [u8; 16], + hash: [u8; 24], + cost: u8, +} + +impl PwHash { + const COST: u8 = 6; + /// `pw` must not be empty. + pub fn new(pw: &str) -> Result { + if pw.is_empty() { + return sunset::error::BadUsage.fail(); + } + + let mut salt = [0u8; 16]; + sunset::random::fill_random(&mut salt)?; + let prehash = Self::prehash(pw, &salt); + let cost = Self::COST; + let hash = bcrypt::bcrypt(cost as u32, salt, &prehash); + Ok(Self { salt, hash, cost }) + } + + pub fn check(&self, pw: &str) -> bool { + if pw.is_empty() { + return false; + } + let prehash = Self::prehash(pw, &self.salt); + let check_hash = bcrypt::bcrypt(self.cost as u32, self.salt.clone(), &prehash); + check_hash.ct_eq(&self.hash).into() + } + + fn prehash(pw: &str, salt: &[u8]) -> [u8; 32] { + // OK unwrap: can't fail, accepts any length + // TODO: Generalise, not only Espressif esp_hal + let mut prehash = Hmac::::new_from_slice(&salt).unwrap(); + prehash.update(pw.as_bytes()); + prehash.finalize().into_bytes().into() + } +} + +impl core::fmt::Debug for PwHash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PwHash").finish_non_exhaustive() + } +} + +impl SSHEncode for PwHash { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + self.salt.enc(s)?; + self.hash.enc(s)?; + self.cost.enc(s) + } +} + +impl<'de> SSHDecode<'de> for PwHash { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + let salt = <[u8; 16]>::dec(s)?; + let hash = <[u8; 24]>::dec(s)?; + let cost = u8::dec(s)?; + Ok(PwHash { salt, hash, cost }) + } +} + +pub fn roundtrip_config() { + // default config + let c1 = SSHStampConfig::new().unwrap(); + let mut buf = [0u8; 1000]; + let l = sshwire::write_ssh(&mut buf, &c1).unwrap(); + let v = &buf[..l]; + let c2: SSHStampConfig = sshwire::read_ssh(v, None).unwrap(); + assert_eq!(c1, c2); +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..8b69a87 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,12 @@ +use core::result; +use snafu::Snafu; + +pub type Result = result::Result; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Invalid PIN provided"))] + InvalidPin, + #[snafu(display("Flash storage error"))] + FlashStorageError, +} diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 71b48d4..3dbcfcd 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -51,6 +51,7 @@ impl BufferedUart { let rd_from = async { loop { let n = uart_rx.read_async(&mut uart_rx_buf).await.unwrap(); + let mut rx_slice = &uart_rx_buf[..n]; // Write rx_slice to 'inward' pipe, dropping bytes rather than blocking if diff --git a/src/espressif/hash.rs b/src/espressif/hash.rs new file mode 100644 index 0000000..8672bdc --- /dev/null +++ b/src/espressif/hash.rs @@ -0,0 +1,28 @@ +use esp_hal::hmac::Hmac; +use hmac::Hmac; +use sha2::Sha256; + +pub trait EspressifHmac { + fn new_from_slice(key: &[u8]) -> Result + where + Self: Sized; + fn update(&mut self, data: &[u8]); + fn finalize(self) -> [u8; 32]; +} + +impl EspressifHmac for Hmac { + fn new_from_slice(key: &[u8]) -> Result { + Hmac::new_from_slice(key).map_err(|_| ()) + } + + fn update(&mut self, data: &[u8]) { + self.update(data); + } + + fn finalize(self) -> [u8; 32] { + let result = self.finalize(); + let mut arr = [0u8; 32]; + arr.copy_from_slice(&result.into_bytes()); + arr + } +} \ No newline at end of file diff --git a/src/espressif/mod.rs b/src/espressif/mod.rs index 88fc177..a86e6cc 100644 --- a/src/espressif/mod.rs +++ b/src/espressif/mod.rs @@ -1,3 +1,6 @@ pub mod buffered_uart; pub mod net; pub mod rng; +// TODO: Specialise for Espressif, tricky since it seems to require burning eFuses?: +// https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/hmac.rs +//pub mod hash; diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 1a79792..d7939c9 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -6,14 +6,16 @@ use embassy_net::{tcp::TcpSocket, Stack, StackResources}; use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, StaticConfigV4}; use embassy_time::{Duration, Timer}; +use esp_hal::clock::CpuClock; use esp_hal::peripherals::WIFI; use esp_hal::rng::Rng; use esp_println::{dbg, println}; -use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiDevice}; -use esp_wifi::wifi::{WifiEvent, WifiState}; -use esp_wifi::EspWifiController; +use esp_radio::wifi::{AccessPointConfig, ModeConfig, WifiController, WifiDevice}; +use esp_radio::wifi::{WifiEvent, WifiApState}; +use heapless::String; +use sunset_async::SunsetMutex; use core::net::SocketAddrV4; use edge_dhcp; @@ -25,9 +27,10 @@ use edge_dhcp::{ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; -use super::buffered_uart::BufferedUart; +use crate::config::SSHStampConfig; +use crate::settings::{self, DEFAULT_SSID}; -const GW_IP_ADDR_ENV: Option<&'static str> = option_env!("GATEWAY_IP"); +use super::buffered_uart::BufferedUart; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html macro_rules! mk_static { @@ -41,19 +44,34 @@ macro_rules! mk_static { pub async fn if_up( spawner: Spawner, - wifi_controller: EspWifiController<'static>, + wifi_controller: WifiController<'static>, wifi: WIFI<'static>, rng: &mut Rng, + config: &'static SunsetMutex, ) -> Result, sunset::Error> { - let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); - let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); - - let gw_ip_addr_str = GW_IP_ADDR_ENV.unwrap_or("192.168.0.1"); - let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip"); - - let config = embassy_net::Config::ipv4_static(StaticConfigV4 { - address: Ipv4Cidr::new(gw_ip_addr, 24), - gateway: Some(gw_ip_addr), + let hal_config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let wifi_init = &*mk_static!(WifiController<'static>, wifi_controller); + let (controller, interfaces) = esp_radio::wifi::new(wifi.reborrow(), hal_config).unwrap(); + + let ap_config = + ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); + let res = controller.set_config(&ap_config); + println!("wifi_set_configuration returned {:?}", res); + + let gw_ip_addr_ipv4 = settings::DEFAULT_IP.clone(); + + // let _gw_ip_addr = { + // let guard = config.lock().await; + // if let Some(ref s) = guard.ip4_static { + // embassy_net::Config::ipv4_static(s.clone()) + // } else { + // embassy_net::Config::dhcpv4(Default::default()) + // } + // }; + + let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { + address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), + gateway: Some(gw_ip_addr_ipv4), dns_servers: Default::default(), }); @@ -62,14 +80,14 @@ pub async fn if_up( // Init network stack let (ap_stack, runner) = embassy_net::new( interfaces.ap, - config, + net_config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, ); - spawner.spawn(wifi_up(controller)).ok(); + spawner.spawn(wifi_up(controller, config)).ok(); spawner.spawn(net_up(runner)).ok(); - spawner.spawn(dhcp_server(ap_stack, gw_ip_addr)).ok(); + spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); loop { println!("Checking if link is up...\n"); @@ -82,13 +100,17 @@ pub async fn if_up( // TODO: Use wifi_manager instead? println!( "Connect to the AP `ssh-stamp` as a DHCP client with IP: {}", - gw_ip_addr_str + gw_ip_addr_ipv4 ); Ok(ap_stack) } -pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { +pub async fn accept_requests( + stack: Stack<'static>, + uart: &BufferedUart, + pin_channel_ref: &'static sunset_async::SunsetMutex, +) -> ! { let rx_buffer = mk_static!([u8; 1536], [0; 1536]); let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -109,7 +131,7 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } println!("Connected, port 22"); - match crate::serve::handle_ssh_client(&mut socket, uart).await { + match crate::serve::handle_ssh_client(&mut socket, uart, pin_channel_ref).await { Ok(_) => (), Err(e) => { println!("SSH client fatal error: {}", e); @@ -119,20 +141,28 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } #[embassy_executor::task] -async fn wifi_up(mut controller: WifiController<'static>) { +async fn wifi_up( + mut controller: WifiController<'static>, + config: &'static SunsetMutex, +) { println!("Device capabilities: {:?}", controller.capabilities()); + + let wifi_ssid = { + let guard = config.lock().await; + guard.wifi_ssid.clone() + // drop guard + }; + loop { - if esp_wifi::wifi::wifi_state() == WifiState::ApStarted { + if esp_radio::wifi::ap_state() == WifiApState::Started { // wait until we're no longer connected controller.wait_for_event(WifiEvent::ApStop).await; Timer::after(Duration::from_millis(5000)).await } if !matches!(controller.is_started(), Ok(true)) { - let client_config = Configuration::AccessPoint(AccessPointConfiguration { - ssid: "ssh-stamp".into(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); + let ssid_string = String::<63>::from_str(wifi_ssid).unwrap().to_ascii_lowercase(); + let client_config = ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(ssid_string)); + controller.set_config(&client_config).unwrap(); println!("Starting wifi"); controller.start_async().await.unwrap(); println!("Wifi started!"); diff --git a/src/lib.rs b/src/lib.rs index f32497f..3b4c225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,13 @@ #![no_std] #![no_main] -#![forbid(unsafe_code)] - +// #![forbid(unsafe_code)] +#[deny(clippy::mem_forget)] // avoids any UB, forces use of Drop impl instead +pub mod config; +pub mod pins; +pub mod errors; pub mod espressif; pub mod keys; pub mod serial; pub mod serve; pub mod settings; +pub mod storage; diff --git a/src/main.rs b/src/main.rs index 9ab48dd..848ddbb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,11 @@ #![no_main] use core::marker::Sized; +use embassy_embedded_hal::flash; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::AnyPin, + gpio::Pin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, @@ -15,18 +16,28 @@ use esp_hal::{ use esp_hal_embassy::InterruptExecutor; use embassy_executor::Spawner; -use ssh_stamp::espressif::{ - buffered_uart::BufferedUart, - net::{accept_requests, if_up}, - rng, +use esp_println::dbg; +use esp_storage::FlashStorage; +use ssh_stamp::pins; +use ssh_stamp::pins::GPIOConfig; +use ssh_stamp::pins::PinChannel; +use ssh_stamp::{ + config::SSHStampConfig, + espressif::{ + buffered_uart::BufferedUart, + net::{accept_requests, if_up}, + rng, + }, + storage::Fl, }; use static_cell::StaticCell; +use sunset_async::SunsetMutex; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { - // TODO: This heap size will crash at runtime, we need to fix this + // TODO: This heap size will crash at runtime (only for the ESP32S2), we need to fix this // applying ideas from https://github.com/brainstorm/ssh-stamp/pull/41#issuecomment-2964775170 esp_alloc::heap_allocator!(size: 69 * 1024); } else { @@ -38,9 +49,27 @@ async fn main(spawner: Spawner) -> ! { // System init let peripherals = esp_hal::init(esp_hal::Config::default()); - let mut rng = Rng::new(peripherals.RNG); + let mut rng = Rng::new(); let timg0 = TimerGroup::new(peripherals.TIMG0); + // Set event handlers for wifi before init to avoid missing any. + let mut connections = 0u32; + _ = event::ApStart::replace_handler(|_| println!("ap start event")); + event::ApStaConnected::update_handler(move |event| { + connections += 1; + esp_println::println!("connected {}, mac: {:?}", connections, event.mac()); + }); + event::ApStaConnected::update_handler(|event| { + esp_println::println!("connected aid: {}", event.aid()); + }); + event::ApStaDisconnected::update_handler(|event| { + println!( + "disconnected mac: {:?}, reason: {:?}", + event.mac(), + event.reason() + ); + }); + rng::register_custom_rng(rng); cfg_if::cfg_if! { @@ -54,10 +83,20 @@ async fn main(spawner: Spawner) -> ! { } } + // Read SSH configuration from Flash (if it exists) + let mut flash_storage = Fl::new(FlashStorage::new(flash::Flash::new())); + let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; + + static FLASH: StaticCell> = StaticCell::new(); + let _flash = FLASH.init(SunsetMutex::new(flash_storage)); + + static CONFIG: StaticCell> = StaticCell::new(); + let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); // Bring up the network interface and start accepting SSH connections. - let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng) + let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config) .await .unwrap(); @@ -73,27 +112,49 @@ async fn main(spawner: Spawner) -> ! { let interrupt_spawner = interrupt_executor.start(Priority::Priority10); } } - cfg_if::cfg_if! { - if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, peripherals.GPIO11.into(), peripherals.GPIO10.into())).unwrap(); - } else { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, peripherals.GPIO9.into(), peripherals.GPIO10.into())).unwrap(); - } - } - accept_requests(tcp_stack, uart_buf).await; + + let serde_pin_config = { + let guard = config.lock().await; + guard.uart_pins.clone() + }; + + let available_gpios = GPIOConfig { + gpio10: Some(peripherals.GPIO10.degrade()), + gpio11: Some(peripherals.GPIO11.degrade()), + }; + + // Initialize the global pin channel and keep the &'static reference so we can + // pass it to tasks that need to mutate pins (no unsafe globals). + let pin_channel_ref = pins::init_global_channel(PinChannel::new( + serde_pin_config, + available_gpios, + )); + + // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality + let uart1 = peripherals.UART1; + + // Use the same config reference for UART task. + // Pass pin_channel_ref into the UART task so it can acquire/release pins. + interrupt_spawner + .spawn(uart_task(uart_buf, uart1, pin_channel_ref)) + .unwrap(); + + // Pass pin_channel_ref into accept_requests (so SSH handlers can use it). + // NOTE: accept_requests signature must accept this arg; if it doesn't, + // thread the reference into whatever code spawns handle_ssh_client. + accept_requests(tcp_stack, uart_buf, pin_channel_ref).await; } static UART_BUF: StaticCell = StaticCell::new(); - static INT_EXECUTOR: StaticCell> = StaticCell::new(); -#[embassy_executor::task] +#[embassy_executor::task()] async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - rx_pin: AnyPin<'static>, - tx_pin: AnyPin<'static>, + pin_channel_ref: &'static SunsetMutex, ) { + dbg!("Spawning UART task..."); // Hardware UART setup let uart_config = Config::default().with_rx( RxConfig::default() @@ -101,12 +162,21 @@ async fn uart_task( .with_timeout(1), ); - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); + // Use the pinned reference passed in from main. + let mut pin_chan = pin_channel_ref.lock().await; + + // Sync pin config via channels + pin_chan + .with_channel(async |rx, tx| { + let uart = Uart::new(uart_periph, uart_config) + .unwrap() + .with_rx(rx) + .with_tx(tx) + .into_async(); - // Run the main buffered TX/RX loop - buffer.run(uart).await; + // Run the main buffered TX/RX loop + buffer.run(uart).await; + }) + .await + .unwrap(); } diff --git a/src/pins.rs b/src/pins.rs new file mode 100644 index 0000000..73c47f1 --- /dev/null +++ b/src/pins.rs @@ -0,0 +1,287 @@ +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel}; +use esp_hal::gpio::AnyPin; +use esp_hal::peripherals; +use sunset::sshwire::{ SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult }; +use static_cell::StaticCell; +use sunset_async::SunsetMutex; + +use crate::{config::{dec_option, enc_option}, errors}; + +#[derive(Debug, Clone, PartialEq)] +pub struct SerdePinConfig { + pub tx: u8, + pub rx: u8, + pub rts: Option, + pub cts: Option, +} + +impl SerdePinConfig { + /// Create a new SerdePinConfig with default values. + pub fn new() -> Self { + Self::default() + } + + /// Set the TX pin, returning an updated SerdePinConfig (builder style). + pub fn with_tx(mut self, tx: u8) -> Self { + self.tx = tx; + self + } + + /// Set the RX pin, returning an updated SerdePinConfig (builder style). + pub fn with_rx(mut self, rx: u8) -> Self { + self.rx = rx; + self + } + + /// Set the RTS pin (optional), returning an updated SerdePinConfig. + pub fn with_rts(mut self, rts: Option) -> Self { + self.rts = rts; + self + } + + /// Set the CTS pin (optional), returning an updated SerdePinConfig. + pub fn with_cts(mut self, cts: Option) -> Self { + self.cts = cts; + self + } +} + +impl Default for SerdePinConfig { + fn default() -> Self { + Self { + tx: 10, + rx: 11, + rts: None, + cts: None, + } + } +} + +impl SSHEncode for SerdePinConfig { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + self.tx.enc(s)?; + self.rx.enc(s)?; + enc_option(&self.rts, s)?; + enc_option(&self.cts, s) + } +} + + +impl<'de> SSHDecode<'de> for SerdePinConfig { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + // Decoding Options is problematic since encode only writes them if they exist. + let mut pin_config = SerdePinConfig::default(); + pin_config.tx = u8::dec(s)?; + pin_config.rx = u8::dec(s)?; + + pin_config.rts = dec_option(s)?; + pin_config.cts = dec_option(s)?; + + Ok(pin_config) + } +} + +#[derive(Default)] +pub struct GPIOConfig { + pub gpio10: Option>, + pub gpio11: Option>, +} + +pub struct PinChannel { + pub config: SerdePinConfig, + pub gpios: GPIOConfig, + pub tx: Channel, + pub rx: Channel, + // TODO: cts/rts pins +} + +impl PinChannel { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { + Self { + config, + gpios, + tx: Channel::::new(), + rx: Channel::::new(), + } + } + + pub async fn recv_tx(&mut self) -> errors::Result> { + // tx needs to lock here. + //self.tx.receive().await; + + Ok(match self.config.tx { + 10 => self + .gpios + .gpio10 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self + .gpios + .gpio11 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin), + }) + } + + pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.tx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin), + }; + + // tx lock needs to be released. + self.tx.send(()).await; + Ok(()) + } + + pub async fn recv_rx(&mut self) -> errors::Result> { + let res = Ok(match self.config.rx { + 10 => self + .gpios + .gpio10 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self + .gpios + .gpio11 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin), + }); + // rx needs to lock here. + // dbg!("recv_rx: before rx.receive.await"); + // self.rx.receive().await; + // dbg!("recv_rx: after rx.receive.await"); + + res + } + + pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.rx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin), + }; + + // rx lock needs to be released. + self.rx.send(()).await; + Ok(()) + } + + pub async fn with_channel(&mut self, f: F) -> errors::Result<()> + where + F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>), + { + let mut rx = self.recv_rx().await?; + let mut tx = self.recv_tx().await?; + + f(rx.reborrow(), tx.reborrow()).await; + + self.send_rx(rx).await.unwrap(); + self.send_tx(tx).await.unwrap(); + + Ok(()) + } + + // Update the runtime config's TX pin number. This only changes the + // u8 config; actual AnyPin movement happens when the uart task next + // reacquires pins via recv_tx/recv_rx. + pub fn set_tx_pin(&mut self, tx: u8) -> errors::Result<()> { + if tx == self.config.rx { + return Err(errors::Error::InvalidPin); + } + match tx { + 10 | 11 => { + self.config.tx = tx; + Ok(()) + } + _ => Err(errors::Error::InvalidPin), + } + } + + pub fn set_rx_pin(&mut self, rx: u8) -> errors::Result<()> { + if rx == self.config.tx { + return Err(errors::Error::InvalidPin); + } + match rx { + 10 | 11 => { + self.config.rx = rx; + Ok(()) + } + _ => Err(errors::Error::InvalidPin), + } + } +} + +// Global PinChannel holder: initialize from main() and access from other modules. +// We keep a StaticCell but avoid any unsafe global pointer; callers receive +// the &'static SunsetMutex returned by init_global_channel and must retain it. +static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); + +pub fn init_global_channel(ch: PinChannel) -> &'static SunsetMutex { + // Initialize the StaticCell and return the &'static reference. + GLOBAL_PIN_CHANNEL.init(SunsetMutex::new(ch)) +} + +pub struct PinConfig { + pub tx: AnyPin<'static>, + pub rx: AnyPin<'static>, +} + +pub struct PinConfigAlt { + pub peripherals: peripherals::Peripherals, +} + +impl PinConfigAlt { + pub fn new(peripherals: peripherals::Peripherals) -> Self { + Self { peripherals } + } + + pub fn take_pin<'a>(&'a mut self, pin: u8) -> AnyPin<'a> { + match pin { + 0 => self.peripherals.GPIO0.reborrow().into(), + 1 => self.peripherals.GPIO1.reborrow().into(), + _ => panic!(), + } + } +} + +impl PinConfig { + pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { + if config_inner.rx == config_inner.tx { + return Err(errors::Error::InvalidPin); + } + + // SAFETY: Safe because moved in peripherals. + Ok(Self { + rx: match config_inner.rx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + }, + tx: match config_inner.tx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + }, + }) + } + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn initialize_pin( + peripherals: peripherals::Peripherals, + pin_number: u8, + ) -> errors::Result> { + match pin_number { + 0 => Ok(peripherals.GPIO0.into()), + + _ => Err(errors::Error::InvalidPin), + } + } +} \ No newline at end of file diff --git a/src/serve.rs b/src/serve.rs index bc5eb0f..c2e0435 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -1,7 +1,7 @@ use core::option::Option::{self, None, Some}; use core::result::Result; -use core::writeln; +use crate::pins::PinChannel; use crate::espressif::buffered_uart::BufferedUart; use crate::keys; use crate::serial::serial_bridge; @@ -12,6 +12,7 @@ use embassy_net::tcp::TcpSocket; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; +use sunset_async::SunsetMutex; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; @@ -19,9 +20,15 @@ use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; +enum SessionType { + Bridge(ChanHandle), + //Sftp(ChanHandle), +} + async fn connection_loop( serv: &SSHServer<'_>, - chan_pipe: &Channel, + chan_pipe: &Channel, + pin_channel_ref: &'static SunsetMutex, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -36,9 +43,11 @@ async fn connection_loop( match ev { ServEvent::SessionShell(a) => { if let Some(ch) = session.take() { + debug_assert!(ch.num() == a.channel()); + a.succeed()?; dbg!("We got shell"); - let _ = chan_pipe.try_send(ch); + let _ = chan_pipe.try_send(SessionType::Bridge(ch)); } else { a.fail()?; } @@ -70,6 +79,64 @@ async fn connection_loop( } } } + ServEvent::SessionEnv(a) => { + dbg!("Got ENV request"); + dbg!(a.name()?); + dbg!(a.value()?); + + // TODO: Logic to serialise/validate env vars? I.e: + // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem + // + // config.change(c): Apply the config change to the relevant subsystem. + // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. + match a.name()? { + "SAVE_CONFIG" => { + if a.value()? == "1" { + dbg!("Triggering config save..."); + todo!("Implement config save to flash"); + } + } + // If the env var is UART_TX_PIN or UART_RX_PIN + "UART_TX_PIN" => { + let val = a.value()?; + dbg!("Updating UART TX pin to ", val); + if let Ok(pin_num) = val.parse::() { + let mut ch = pin_channel_ref.lock().await; + if ch.set_tx_pin(pin_num).is_err() { + dbg!("Failed to update TX pin"); + } else { + dbg!("TX pin updated"); + } + } else { + dbg!("Invalid TX pin value"); + } + } + "UART_RX_PIN" => { + let val = a.value()?; + dbg!("Updating UART RX pin to ", val); + if let Ok(pin_num) = val.parse::() { + let mut ch = pin_channel_ref.lock().await; + if ch.set_rx_pin(pin_num).is_err() { + dbg!("Failed to update RX pin"); + } else { + dbg!("RX pin updated"); + } + } else { + dbg!("Invalid RX pin value"); + } + } + _ => { + dbg!("Unknown/unsupported ENV var"); + } + } + + + + // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 + // that serialises current config to flash + + a.succeed()?; + } ServEvent::SessionPty(a) => { a.succeed()?; } @@ -80,15 +147,15 @@ async fn connection_loop( println!("Expected caller to handle event"); error::BadUsage.fail()? } - ServEvent::PollAgain => (), - ServEvent::SessionSubsystem(_) => (), - } + _ => (), + }; } } pub(crate) async fn handle_ssh_client( stream: &mut TcpSocket<'_>, uart: &BufferedUart, + pin_channel_ref: &'static SunsetMutex, ) -> Result<(), sunset::Error> { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; 4096]; @@ -97,19 +164,31 @@ pub(crate) async fn handle_ssh_client( let ssh_server = SSHServer::new(&mut inbuf, &mut outbuf); let (mut rsock, mut wsock) = stream.split(); - let chan_pipe = Channel::::new(); + let chan_pipe = Channel::::new(); println!("Calling connection_loop from handle_ssh_client"); - let conn_loop = connection_loop(&ssh_server, &chan_pipe); + let conn_loop = connection_loop(&ssh_server, &chan_pipe, pin_channel_ref); println!("Running server from handle_ssh_client()"); let server = ssh_server.run(&mut rsock, &mut wsock); println!("Setting up serial bridge"); + + // TODO: Maybe loop forever here and/or handle disconnection/terminations gracefully? let bridge = async { - let ch = chan_pipe.receive().await; - let stdio = ssh_server.stdio(ch).await?; - let stdio2 = stdio.clone(); - serial_bridge(stdio, stdio2, uart).await + let session_type = chan_pipe.receive().await; + + match session_type { + SessionType::Bridge(ch) => { + let stdio = ssh_server.stdio(ch).await?; + let stdio2 = stdio.clone(); + serial_bridge(stdio, stdio2, uart).await? + } + // SessionType::Sftp(_ch) => { + // // Handle SFTP session + // todo!() + // } + }; + Ok(()) }; println!("Main select() in handle_ssh_client()"); diff --git a/src/settings.rs b/src/settings.rs index 098e322..b15d1c7 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,10 +1,15 @@ // Static settings +use core::net::Ipv4Addr; + // SSH server settings //pub(crate) const MTU: usize = 1536; //pub(crate) const PORT: u16 = 22; -pub(crate) const _SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; - +pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; +//pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; +pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? + //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; +pub(crate) const DEFAULT_IP: &Ipv4Addr = &Ipv4Addr::new(192, 168, 4, 1); // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; //pub(crate) const UART_SETTINGS: &str = "8N1"; diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000..3d63542 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,165 @@ +use embedded_storage::ReadStorage; +use esp_bootloader_esp_idf::partitions; +use esp_println::{dbg, println}; +use esp_storage::FlashStorage; + +use pretty_hex::PrettyHex; +use sha2::Digest; + +use core::borrow::Borrow; + +use embedded_storage::nor_flash::NorFlash; + +use crate::errors::Error as SSHStampError; +use sunset::error::Error as SunsetError; + +use sunset::sshwire::{self, OwnOrBorrow}; +use sunset_sshwire_derive::*; + +use crate::config::SSHStampConfig; + +pub const CONFIG_VERSION_SIZE: usize = 4; +pub const CONFIG_HASH_SIZE: usize = 32; +pub const CONFIG_AREA_SIZE: usize = 4096; +pub const CONFIG_OFFSET: usize = 0x9000; + +pub struct Fl<'a> { + flash: FlashStorage<'a>, + // Only a single task can write to flash at a time, + // keeping a buffer here saves duplicated buffer space in each task. + buf: [u8; FlashConfig::BUF_SIZE], +} + +impl<'a> Fl<'a> { + pub fn new(flash: FlashStorage<'a>) -> Self { + Self { + flash, + buf: [0u8; FlashConfig::BUF_SIZE], + } + } +} + +// SSHConfig::CURRENT_VERSION must be bumped if any of this struct +#[derive(SSHEncode, SSHDecode)] +struct FlashConfig<'a> { + version: u8, + config: OwnOrBorrow<'a, SSHStampConfig>, + /// sha256 hash of config + hash: [u8; 32], +} + +impl FlashConfig<'_> { + const BUF_SIZE: usize = 460; // Must be enough to hold the whole config + + // TODO: Rework Error mapping with esp_storage errors + /// Finds the NVS partitions and retrieves information about it. + pub fn find_config_partition(flash: &mut FlashStorage<'_>) -> Result<(), SSHStampError> { + println!("Flash size = {} bytes", flash.capacity()); + + let mut pt_mem = [0u8; partitions::PARTITION_TABLE_MAX_LEN]; + let pt = partitions::read_partition_table(flash, &mut pt_mem).unwrap(); + let nvs = pt + .find_partition(partitions::PartitionType::Data( + partitions::DataPartitionSubType::Nvs, + )) + .unwrap() + .unwrap(); + + let nvs_partition = nvs.as_embedded_storage(flash); + + println!("NVS partition size = {}", nvs_partition.capacity()); + println!("NVS partition offset = 0x{:x}", nvs.offset()); + + Ok(()) + } +} + +fn config_hash(config: &SSHStampConfig) -> Result<[u8; 32], SunsetError> { + let mut h = sha2::Sha256::new(); + sshwire::hash_ser(&mut h, config)?; + Ok(h.finalize().into()) +} + +/// Loads a SSHConfig at startup. Good for persisting hostkeys. +pub async fn load_or_create(flash: &mut Fl<'_>) -> Result { + match load(flash).await { + Ok(c) => { + println!("Good existing config"); + return Ok(c); + } + Err(e) => println!("Existing config bad, making new. {e}"), + } + + create(flash).await +} + +pub async fn create(flash: &mut Fl<'_>) -> Result { + let c = SSHStampConfig::new()?; + save(flash, &c).await?; + dbg!("Created new config: ", &c); + + Ok(c) +} + +pub async fn load(fl: &mut Fl<'_>) -> Result { + fl.flash + .read(CONFIG_OFFSET as u32, &mut fl.buf) + .map_err(|_e| { + dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); + SunsetError::msg("flash error") + })?; + + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None) + .map_err(|_| SunsetError::msg("failed to decode flash config"))?; + + if flash_config.version != SSHStampConfig::CURRENT_VERSION { + dbg!("wrong config version on decode: {}", flash_config.version); + return Err(SunsetError::msg("wrong config version")); + } + + let calc_hash = config_hash(flash_config.config.borrow()).unwrap(); + + if calc_hash != flash_config.hash { + return Err(SunsetError::msg("bad config hash")); + } + + if let OwnOrBorrow::Own(c) = flash_config.config { + Ok(c) + } else { + // OK panic - OwnOrBorrow always decodes to Own variant + panic!() + } +} + +pub async fn save(fl: &mut Fl<'_>, config: &SSHStampConfig) -> Result<(), SunsetError> { + let sc = FlashConfig { + version: SSHStampConfig::CURRENT_VERSION, + config: OwnOrBorrow::Borrow(&config), + hash: config_hash(&config)?, + }; + + FlashConfig::find_config_partition(&mut fl.flash).unwrap(); + + // dbg!("Saving config: ", &config); + dbg!("Before write_ssh, with hash: ", &sc.hash.hex_dump()); + let buf = &fl.buf[..]; + dbg!("Saved flash (after write_ssh): {}", &buf.hex_dump()); + + dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); + + dbg!("Erasing flash"); + + assert!(CONFIG_AREA_SIZE > FlashConfig::BUF_SIZE); + + fl.flash + .erase( + CONFIG_OFFSET as u32, + (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32, + ) + .unwrap(); + + fl.flash.write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); + + println!("flash save done"); + Ok(()) +}