├── .cargo └── config ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md └── src ├── authenticator.rs ├── cli.rs ├── lib.rs ├── main.rs ├── platform.rs └── platform └── store.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [net] 2 | git-fetch-with-cli = true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/state.littlefs2 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aead" 7 | version = "0.4.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" 10 | dependencies = [ 11 | "generic-array", 12 | "heapless", 13 | ] 14 | 15 | [[package]] 16 | name = "aes" 17 | version = "0.7.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" 20 | dependencies = [ 21 | "cfg-if", 22 | "cipher", 23 | "cpufeatures", 24 | "opaque-debug", 25 | ] 26 | 27 | [[package]] 28 | name = "aho-corasick" 29 | version = "0.7.18" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 32 | dependencies = [ 33 | "memchr", 34 | ] 35 | 36 | [[package]] 37 | name = "anyhow" 38 | version = "1.0.48" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e" 41 | 42 | [[package]] 43 | name = "atomic-polyfill" 44 | version = "0.1.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "e686d748538a32325b28d6411dd8a939e7ad5128e5d0023cc4fd3573db456042" 47 | dependencies = [ 48 | "critical-section", 49 | "riscv-target", 50 | ] 51 | 52 | [[package]] 53 | name = "atty" 54 | version = "0.2.14" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 57 | dependencies = [ 58 | "hermit-abi", 59 | "libc", 60 | "winapi", 61 | ] 62 | 63 | [[package]] 64 | name = "bare-metal" 65 | version = "0.2.5" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 68 | dependencies = [ 69 | "rustc_version", 70 | ] 71 | 72 | [[package]] 73 | name = "bare-metal" 74 | version = "1.0.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 77 | 78 | [[package]] 79 | name = "bindgen" 80 | version = "0.56.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" 83 | dependencies = [ 84 | "bitflags", 85 | "cexpr", 86 | "clang-sys", 87 | "lazy_static", 88 | "lazycell", 89 | "peeking_take_while", 90 | "proc-macro2", 91 | "quote", 92 | "regex", 93 | "rustc-hash", 94 | "shlex", 95 | ] 96 | 97 | [[package]] 98 | name = "bindgen" 99 | version = "0.57.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d" 102 | dependencies = [ 103 | "bitflags", 104 | "cexpr", 105 | "clang-sys", 106 | "lazy_static", 107 | "lazycell", 108 | "peeking_take_while", 109 | "proc-macro2", 110 | "quote", 111 | "regex", 112 | "rustc-hash", 113 | "shlex", 114 | ] 115 | 116 | [[package]] 117 | name = "bit_field" 118 | version = "0.10.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 121 | 122 | [[package]] 123 | name = "bitfield" 124 | version = "0.13.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 127 | 128 | [[package]] 129 | name = "bitflags" 130 | version = "1.3.2" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 133 | 134 | [[package]] 135 | name = "bitvec" 136 | version = "0.20.4" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" 139 | dependencies = [ 140 | "funty", 141 | "radium", 142 | "tap", 143 | "wyz", 144 | ] 145 | 146 | [[package]] 147 | name = "block-buffer" 148 | version = "0.9.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 151 | dependencies = [ 152 | "generic-array", 153 | ] 154 | 155 | [[package]] 156 | name = "block-modes" 157 | version = "0.8.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" 160 | dependencies = [ 161 | "block-padding", 162 | "cipher", 163 | ] 164 | 165 | [[package]] 166 | name = "block-padding" 167 | version = "0.2.1" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 170 | 171 | [[package]] 172 | name = "byteorder" 173 | version = "1.4.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 176 | 177 | [[package]] 178 | name = "cbor-smol" 179 | version = "0.4.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "d516e3e353d5fc5ee156028f43224033430fd08ef05f8d5dba18a57a4ee5df49" 182 | dependencies = [ 183 | "delog", 184 | "heapless", 185 | "heapless-bytes", 186 | "serde", 187 | ] 188 | 189 | [[package]] 190 | name = "cc" 191 | version = "1.0.72" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 194 | 195 | [[package]] 196 | name = "cexpr" 197 | version = "0.4.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" 200 | dependencies = [ 201 | "nom", 202 | ] 203 | 204 | [[package]] 205 | name = "cfg-if" 206 | version = "1.0.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 209 | 210 | [[package]] 211 | name = "chacha20" 212 | version = "0.7.3" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "f08493fa7707effc63254c66c6ea908675912493cd67952eda23c09fae2610b1" 215 | dependencies = [ 216 | "cfg-if", 217 | "cipher", 218 | "cpufeatures", 219 | "rand_core", 220 | "zeroize", 221 | ] 222 | 223 | [[package]] 224 | name = "chacha20poly1305" 225 | version = "0.8.2" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "b6547abe025f4027edacd9edaa357aded014eecec42a5070d9b885c3c334aba2" 228 | dependencies = [ 229 | "aead", 230 | "chacha20", 231 | "cipher", 232 | "poly1305", 233 | "zeroize", 234 | ] 235 | 236 | [[package]] 237 | name = "cipher" 238 | version = "0.3.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" 241 | dependencies = [ 242 | "generic-array", 243 | ] 244 | 245 | [[package]] 246 | name = "clang-sys" 247 | version = "1.3.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" 250 | dependencies = [ 251 | "glob", 252 | "libc", 253 | ] 254 | 255 | [[package]] 256 | name = "clap" 257 | version = "2.33.3" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 260 | dependencies = [ 261 | "bitflags", 262 | "textwrap", 263 | "unicode-width", 264 | ] 265 | 266 | [[package]] 267 | name = "cortex-m" 268 | version = "0.7.3" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "2ac919ef424449ec8c08d515590ce15d9262c0ca5f0da5b0c901e971a3b783b3" 271 | dependencies = [ 272 | "bare-metal 0.2.5", 273 | "bitfield", 274 | "embedded-hal", 275 | "volatile-register", 276 | ] 277 | 278 | [[package]] 279 | name = "cosey" 280 | version = "0.3.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "bb743c2c58b69b970e02f5ba7f552f75fcfc8393768e3ae4316e055aabacfdaa" 283 | dependencies = [ 284 | "heapless-bytes", 285 | "serde", 286 | "serde_repr", 287 | ] 288 | 289 | [[package]] 290 | name = "cpufeatures" 291 | version = "0.2.1" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 294 | dependencies = [ 295 | "libc", 296 | ] 297 | 298 | [[package]] 299 | name = "critical-section" 300 | version = "0.2.5" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" 303 | dependencies = [ 304 | "bare-metal 1.0.0", 305 | "cfg-if", 306 | "cortex-m", 307 | "riscv", 308 | ] 309 | 310 | [[package]] 311 | name = "crypto-mac" 312 | version = "0.11.1" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" 315 | dependencies = [ 316 | "generic-array", 317 | "subtle", 318 | ] 319 | 320 | [[package]] 321 | name = "cstr_core" 322 | version = "0.2.4" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "917ba9efe9e1e736671d5a03f006afc4e7e3f32503e2077e0bcaf519c0c8c1d3" 325 | dependencies = [ 326 | "cty", 327 | "memchr", 328 | ] 329 | 330 | [[package]] 331 | name = "cty" 332 | version = "0.2.2" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 335 | 336 | [[package]] 337 | name = "data-encoding" 338 | version = "2.3.2" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" 341 | 342 | [[package]] 343 | name = "delog" 344 | version = "0.1.2" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "1abe705f4932cb84b67569a7e05cda7ffc9b8847980b2e1cf290f41045eea73b" 347 | dependencies = [ 348 | "log", 349 | ] 350 | 351 | [[package]] 352 | name = "der" 353 | version = "0.3.5" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "2eeb9d92785d1facb50567852ce75d0858630630e7eabea59cf7eb7474051087" 356 | dependencies = [ 357 | "der_derive", 358 | "typenum", 359 | ] 360 | 361 | [[package]] 362 | name = "der_derive" 363 | version = "0.3.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "ed80cdf655e9e748d5bcc5ea2c59fffb8750eb949d2161e72886c9bdf5b12c34" 366 | dependencies = [ 367 | "proc-macro2", 368 | "quote", 369 | "syn", 370 | "synstructure", 371 | ] 372 | 373 | [[package]] 374 | name = "des" 375 | version = "0.7.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" 378 | dependencies = [ 379 | "byteorder", 380 | "cipher", 381 | "opaque-debug", 382 | ] 383 | 384 | [[package]] 385 | name = "digest" 386 | version = "0.9.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 389 | dependencies = [ 390 | "generic-array", 391 | ] 392 | 393 | [[package]] 394 | name = "ecdsa" 395 | version = "0.11.1" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "34d33b390ab82f2e1481e331dbd0530895640179d2128ef9a79cc690b78d1eba" 398 | dependencies = [ 399 | "der", 400 | "elliptic-curve", 401 | "hmac", 402 | "signature", 403 | ] 404 | 405 | [[package]] 406 | name = "ed25519" 407 | version = "1.3.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" 410 | dependencies = [ 411 | "signature", 412 | ] 413 | 414 | [[package]] 415 | name = "elliptic-curve" 416 | version = "0.9.12" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "c13e9b0c3c4170dcc2a12783746c4205d98e18957f57854251eea3f9750fe005" 419 | dependencies = [ 420 | "bitvec", 421 | "ff", 422 | "generic-array", 423 | "group", 424 | "rand_core", 425 | "subtle", 426 | "zeroize", 427 | ] 428 | 429 | [[package]] 430 | name = "embedded-hal" 431 | version = "0.2.6" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" 434 | dependencies = [ 435 | "nb 0.1.3", 436 | "void", 437 | ] 438 | 439 | [[package]] 440 | name = "env_logger" 441 | version = "0.7.1" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 444 | dependencies = [ 445 | "atty", 446 | "humantime", 447 | "log", 448 | "regex", 449 | "termcolor", 450 | ] 451 | 452 | [[package]] 453 | name = "ff" 454 | version = "0.9.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "72a4d941a5b7c2a75222e2d44fcdf634a67133d9db31e177ae5ff6ecda852bfe" 457 | dependencies = [ 458 | "bitvec", 459 | "rand_core", 460 | "subtle", 461 | ] 462 | 463 | [[package]] 464 | name = "flexiber" 465 | version = "0.1.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "e3df5b1466eec7b03f5848d8388b99975a0ba1a26510db61ba87c2a6177938e5" 468 | dependencies = [ 469 | "delog", 470 | "flexiber_derive", 471 | "heapless", 472 | ] 473 | 474 | [[package]] 475 | name = "flexiber_derive" 476 | version = "0.1.0" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "500c147f43e74e711720769dd7f37bc90dc6e0798621bfe5e3acb8239fd2f826" 479 | dependencies = [ 480 | "proc-macro2", 481 | "quote", 482 | "syn", 483 | "synstructure", 484 | ] 485 | 486 | [[package]] 487 | name = "funty" 488 | version = "1.1.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" 491 | 492 | [[package]] 493 | name = "generic-array" 494 | version = "0.14.4" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 497 | dependencies = [ 498 | "typenum", 499 | "version_check", 500 | ] 501 | 502 | [[package]] 503 | name = "getrandom" 504 | version = "0.2.3" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 507 | dependencies = [ 508 | "cfg-if", 509 | "libc", 510 | "wasi", 511 | ] 512 | 513 | [[package]] 514 | name = "glob" 515 | version = "0.3.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 518 | 519 | [[package]] 520 | name = "group" 521 | version = "0.9.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "61b3c1e8b4f1ca07e6605ea1be903a5f6956aec5c8a67fd44d56076631675ed8" 524 | dependencies = [ 525 | "ff", 526 | "rand_core", 527 | "subtle", 528 | ] 529 | 530 | [[package]] 531 | name = "half" 532 | version = "1.8.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 535 | 536 | [[package]] 537 | name = "hash32" 538 | version = "0.2.1" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 541 | dependencies = [ 542 | "byteorder", 543 | ] 544 | 545 | [[package]] 546 | name = "heapless" 547 | version = "0.7.8" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "9c1ad878e07405df82b695089e63d278244344f80e764074d0bdfe99b89460f3" 550 | dependencies = [ 551 | "atomic-polyfill", 552 | "hash32", 553 | "serde", 554 | "spin", 555 | "stable_deref_trait", 556 | ] 557 | 558 | [[package]] 559 | name = "heapless-bytes" 560 | version = "0.3.0" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "7285eba272c6af3e9f15fb9e1c1b6e7d35aa70580ffe0d47af017e97dfb6f48b" 563 | dependencies = [ 564 | "heapless", 565 | "serde", 566 | "serde_cbor", 567 | "typenum", 568 | ] 569 | 570 | [[package]] 571 | name = "hermit-abi" 572 | version = "0.1.19" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 575 | dependencies = [ 576 | "libc", 577 | ] 578 | 579 | [[package]] 580 | name = "hex-literal" 581 | version = "0.3.4" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" 584 | 585 | [[package]] 586 | name = "hmac" 587 | version = "0.11.0" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" 590 | dependencies = [ 591 | "crypto-mac", 592 | "digest", 593 | ] 594 | 595 | [[package]] 596 | name = "humantime" 597 | version = "1.3.0" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 600 | dependencies = [ 601 | "quick-error", 602 | ] 603 | 604 | [[package]] 605 | name = "interchange" 606 | version = "0.2.1" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "65d9ab155e6b8e53ae742a06a850b2645e333d40d89ef2e28f190763742d768c" 609 | 610 | [[package]] 611 | name = "lazy_static" 612 | version = "1.4.0" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 615 | 616 | [[package]] 617 | name = "lazycell" 618 | version = "1.3.0" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 621 | 622 | [[package]] 623 | name = "libc" 624 | version = "0.2.108" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" 627 | 628 | [[package]] 629 | name = "littlefs2" 630 | version = "0.3.2" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "0dc089501e32d62b3e4d809a29b9e00d6211a197440602d69f304f1e4e82136b" 633 | dependencies = [ 634 | "bitflags", 635 | "cstr_core", 636 | "cty", 637 | "delog", 638 | "generic-array", 639 | "heapless", 640 | "littlefs2-sys", 641 | "serde", 642 | ] 643 | 644 | [[package]] 645 | name = "littlefs2-sys" 646 | version = "0.1.6" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "aac150caec8a056e1248754f738c2330e578469093a522cf1afe30bbe545b90b" 649 | dependencies = [ 650 | "bindgen 0.56.0", 651 | "cc", 652 | "cty", 653 | ] 654 | 655 | [[package]] 656 | name = "lock_api" 657 | version = "0.4.5" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 660 | dependencies = [ 661 | "scopeguard", 662 | ] 663 | 664 | [[package]] 665 | name = "log" 666 | version = "0.4.14" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 669 | dependencies = [ 670 | "cfg-if", 671 | ] 672 | 673 | [[package]] 674 | name = "memchr" 675 | version = "2.4.1" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 678 | 679 | [[package]] 680 | name = "nb" 681 | version = "0.1.3" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 684 | dependencies = [ 685 | "nb 1.0.0", 686 | ] 687 | 688 | [[package]] 689 | name = "nb" 690 | version = "1.0.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 693 | 694 | [[package]] 695 | name = "nom" 696 | version = "5.1.2" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 699 | dependencies = [ 700 | "memchr", 701 | "version_check", 702 | ] 703 | 704 | [[package]] 705 | name = "opaque-debug" 706 | version = "0.3.0" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 709 | 710 | [[package]] 711 | name = "p256" 712 | version = "0.8.1" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "2f05f5287453297c4c16af5e2b04df8fd2a3008d70f252729650bc6d7ace5844" 715 | dependencies = [ 716 | "ecdsa", 717 | "elliptic-curve", 718 | "sha2", 719 | ] 720 | 721 | [[package]] 722 | name = "p256-cortex-m4" 723 | version = "0.1.0-alpha.5" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "2c11579292e2d7a61626c5ef1c0bddb6a21d6b0c6f9d36ea83d2c63da5bbc3d1" 726 | dependencies = [ 727 | "der", 728 | "ecdsa", 729 | "elliptic-curve", 730 | "p256", 731 | "p256-cortex-m4-sys", 732 | "rand_core", 733 | "sha2", 734 | "zeroize", 735 | ] 736 | 737 | [[package]] 738 | name = "p256-cortex-m4-sys" 739 | version = "0.1.0-alpha.2" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "f9450cfb34f995019026c81c45031df2db7bc8c8523739f84d7113862f07b6e3" 742 | dependencies = [ 743 | "bindgen 0.57.0", 744 | "cc", 745 | "cty", 746 | ] 747 | 748 | [[package]] 749 | name = "peeking_take_while" 750 | version = "0.1.2" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 753 | 754 | [[package]] 755 | name = "poly1305" 756 | version = "0.7.2" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" 759 | dependencies = [ 760 | "cpufeatures", 761 | "opaque-debug", 762 | "universal-hash", 763 | ] 764 | 765 | [[package]] 766 | name = "postcard" 767 | version = "0.7.2" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "c8863e251332eb18520388099b8b0acc4810ed6e602e3b6f674e8a46ba20e15c" 770 | dependencies = [ 771 | "heapless", 772 | "postcard-cobs", 773 | "serde", 774 | ] 775 | 776 | [[package]] 777 | name = "postcard-cobs" 778 | version = "0.1.5-pre" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" 781 | 782 | [[package]] 783 | name = "pretty_env_logger" 784 | version = "0.4.0" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 787 | dependencies = [ 788 | "env_logger", 789 | "log", 790 | ] 791 | 792 | [[package]] 793 | name = "proc-macro2" 794 | version = "1.0.32" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 797 | dependencies = [ 798 | "unicode-xid", 799 | ] 800 | 801 | [[package]] 802 | name = "quick-error" 803 | version = "1.2.3" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 806 | 807 | [[package]] 808 | name = "quote" 809 | version = "1.0.10" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 812 | dependencies = [ 813 | "proc-macro2", 814 | ] 815 | 816 | [[package]] 817 | name = "radium" 818 | version = "0.6.2" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" 821 | 822 | [[package]] 823 | name = "rand_core" 824 | version = "0.6.3" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 827 | dependencies = [ 828 | "getrandom", 829 | ] 830 | 831 | [[package]] 832 | name = "regex" 833 | version = "1.5.4" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 836 | dependencies = [ 837 | "aho-corasick", 838 | "memchr", 839 | "regex-syntax", 840 | ] 841 | 842 | [[package]] 843 | name = "regex-syntax" 844 | version = "0.6.25" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 847 | 848 | [[package]] 849 | name = "riscv" 850 | version = "0.7.0" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 853 | dependencies = [ 854 | "bare-metal 1.0.0", 855 | "bit_field", 856 | "riscv-target", 857 | ] 858 | 859 | [[package]] 860 | name = "riscv-target" 861 | version = "0.1.2" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 864 | dependencies = [ 865 | "lazy_static", 866 | "regex", 867 | ] 868 | 869 | [[package]] 870 | name = "rustc-hash" 871 | version = "1.1.0" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 874 | 875 | [[package]] 876 | name = "rustc_version" 877 | version = "0.2.3" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 880 | dependencies = [ 881 | "semver", 882 | ] 883 | 884 | [[package]] 885 | name = "salty" 886 | version = "0.2.0" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "77cdd38ed8bfe51e53ee991aae0791b94349d0a05cfdecd283835a8a965d4c37" 889 | dependencies = [ 890 | "cosey", 891 | "ed25519", 892 | "subtle", 893 | "zeroize", 894 | ] 895 | 896 | [[package]] 897 | name = "scopeguard" 898 | version = "1.1.0" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 901 | 902 | [[package]] 903 | name = "semver" 904 | version = "0.9.0" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 907 | dependencies = [ 908 | "semver-parser", 909 | ] 910 | 911 | [[package]] 912 | name = "semver-parser" 913 | version = "0.7.0" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 916 | 917 | [[package]] 918 | name = "serde" 919 | version = "1.0.130" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 922 | dependencies = [ 923 | "serde_derive", 924 | ] 925 | 926 | [[package]] 927 | name = "serde-indexed" 928 | version = "0.1.0" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "431fb604dab775c7bdabdab23b491ec773de085afd92b5dac26b8f3db5965f42" 931 | dependencies = [ 932 | "proc-macro2", 933 | "quote", 934 | "syn", 935 | ] 936 | 937 | [[package]] 938 | name = "serde_cbor" 939 | version = "0.11.2" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" 942 | dependencies = [ 943 | "half", 944 | "serde", 945 | ] 946 | 947 | [[package]] 948 | name = "serde_derive" 949 | version = "1.0.130" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 952 | dependencies = [ 953 | "proc-macro2", 954 | "quote", 955 | "syn", 956 | ] 957 | 958 | [[package]] 959 | name = "serde_repr" 960 | version = "0.1.7" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" 963 | dependencies = [ 964 | "proc-macro2", 965 | "quote", 966 | "syn", 967 | ] 968 | 969 | [[package]] 970 | name = "sha-1" 971 | version = "0.9.8" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" 974 | dependencies = [ 975 | "block-buffer", 976 | "cfg-if", 977 | "cpufeatures", 978 | "digest", 979 | "opaque-debug", 980 | ] 981 | 982 | [[package]] 983 | name = "sha2" 984 | version = "0.9.8" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" 987 | dependencies = [ 988 | "block-buffer", 989 | "cfg-if", 990 | "cpufeatures", 991 | "digest", 992 | "opaque-debug", 993 | ] 994 | 995 | [[package]] 996 | name = "shlex" 997 | version = "0.1.1" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" 1000 | 1001 | [[package]] 1002 | name = "signature" 1003 | version = "1.3.2" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" 1006 | dependencies = [ 1007 | "digest", 1008 | "rand_core", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "spin" 1013 | version = "0.9.2" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" 1016 | dependencies = [ 1017 | "lock_api", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "stable_deref_trait" 1022 | version = "1.2.0" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1025 | 1026 | [[package]] 1027 | name = "subtle" 1028 | version = "2.4.1" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1031 | 1032 | [[package]] 1033 | name = "syn" 1034 | version = "1.0.81" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 1037 | dependencies = [ 1038 | "proc-macro2", 1039 | "quote", 1040 | "unicode-xid", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "synstructure" 1045 | version = "0.12.6" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 1048 | dependencies = [ 1049 | "proc-macro2", 1050 | "quote", 1051 | "syn", 1052 | "unicode-xid", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "tap" 1057 | version = "1.0.1" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 1060 | 1061 | [[package]] 1062 | name = "termcolor" 1063 | version = "1.1.2" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1066 | dependencies = [ 1067 | "winapi-util", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "textwrap" 1072 | version = "0.11.0" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1075 | dependencies = [ 1076 | "unicode-width", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "thiserror" 1081 | version = "1.0.30" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1084 | dependencies = [ 1085 | "thiserror-impl", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "thiserror-impl" 1090 | version = "1.0.30" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1093 | dependencies = [ 1094 | "proc-macro2", 1095 | "quote", 1096 | "syn", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "trussed" 1101 | version = "0.1.0" 1102 | source = "git+https://github.com/trussed-dev/trussed?branch=main#6823d9534701e27bd946907968b518d4347550e4" 1103 | dependencies = [ 1104 | "aes", 1105 | "bitflags", 1106 | "block-modes", 1107 | "cbor-smol", 1108 | "cfg-if", 1109 | "chacha20", 1110 | "chacha20poly1305", 1111 | "cosey", 1112 | "delog", 1113 | "des", 1114 | "embedded-hal", 1115 | "flexiber", 1116 | "generic-array", 1117 | "heapless", 1118 | "heapless-bytes", 1119 | "hex-literal", 1120 | "hmac", 1121 | "interchange", 1122 | "littlefs2", 1123 | "nb 1.0.0", 1124 | "p256-cortex-m4", 1125 | "postcard", 1126 | "rand_core", 1127 | "salty", 1128 | "serde", 1129 | "serde-indexed", 1130 | "sha-1", 1131 | "sha2", 1132 | "zeroize", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "trussed-totp-pc-tutorial" 1137 | version = "0.1.0" 1138 | dependencies = [ 1139 | "anyhow", 1140 | "chacha20", 1141 | "clap", 1142 | "data-encoding", 1143 | "delog", 1144 | "generic-array", 1145 | "littlefs2", 1146 | "log", 1147 | "postcard", 1148 | "pretty_env_logger", 1149 | "rand_core", 1150 | "serde", 1151 | "thiserror", 1152 | "trussed", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "typenum" 1157 | version = "1.14.0" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 1160 | 1161 | [[package]] 1162 | name = "unicode-width" 1163 | version = "0.1.9" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1166 | 1167 | [[package]] 1168 | name = "unicode-xid" 1169 | version = "0.2.2" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1172 | 1173 | [[package]] 1174 | name = "universal-hash" 1175 | version = "0.4.1" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" 1178 | dependencies = [ 1179 | "generic-array", 1180 | "subtle", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "vcell" 1185 | version = "0.1.3" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 1188 | 1189 | [[package]] 1190 | name = "version_check" 1191 | version = "0.9.3" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1194 | 1195 | [[package]] 1196 | name = "void" 1197 | version = "1.0.2" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1200 | 1201 | [[package]] 1202 | name = "volatile-register" 1203 | version = "0.2.1" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" 1206 | dependencies = [ 1207 | "vcell", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "wasi" 1212 | version = "0.10.2+wasi-snapshot-preview1" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1215 | 1216 | [[package]] 1217 | name = "winapi" 1218 | version = "0.3.9" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1221 | dependencies = [ 1222 | "winapi-i686-pc-windows-gnu", 1223 | "winapi-x86_64-pc-windows-gnu", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "winapi-i686-pc-windows-gnu" 1228 | version = "0.4.0" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1231 | 1232 | [[package]] 1233 | name = "winapi-util" 1234 | version = "0.1.5" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1237 | dependencies = [ 1238 | "winapi", 1239 | ] 1240 | 1241 | [[package]] 1242 | name = "winapi-x86_64-pc-windows-gnu" 1243 | version = "0.4.0" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1246 | 1247 | [[package]] 1248 | name = "wyz" 1249 | version = "0.2.0" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" 1252 | 1253 | [[package]] 1254 | name = "zeroize" 1255 | version = "1.3.0" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" 1258 | dependencies = [ 1259 | "zeroize_derive", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "zeroize_derive" 1264 | version = "1.2.2" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" 1267 | dependencies = [ 1268 | "proc-macro2", 1269 | "quote", 1270 | "syn", 1271 | "synstructure", 1272 | ] 1273 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trussed-totp-pc-tutorial" 3 | version = "0.1.0" 4 | authors = ["Nicolas Stalder "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "tutorial" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | anyhow = "1" 14 | chacha20 = { version = "0.7", features = ["rng"] } 15 | clap = { version = "2", default-features = false } 16 | data-encoding = "2" 17 | delog = "0.1" 18 | generic-array = "0.14" 19 | littlefs2 = "0.3" 20 | log = "0.4" 21 | postcard = "0.7" 22 | rand_core = { version = "0.6", features = ["getrandom"] } 23 | serde = { version = "1", default-features = false, features = ["derive"] } 24 | pretty_env_logger = "0.4" 25 | thiserror = "1" 26 | 27 | # need access to the repository for this 28 | trussed = { git = "https://github.com/trussed-dev/trussed", branch = "main" } 29 | # trussed = { path = "../trussed" } 30 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 SoloKeys 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINARY := trussed-totp-pc-tutorial 2 | LABEL := alice@trussed.dev 3 | SECRET := JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP 4 | 5 | register: 6 | $(BINARY) register $(LABEL) $(SECRET) 7 | 8 | register-dev: 9 | cargo run -- register $(LABEL) $(SECRET) 10 | 11 | authenticate: 12 | $(BINARY) authenticate $(LABEL) 13 | 14 | authenticate-dev: 15 | cargo run -- authenticate $(LABEL) 16 | 17 | install: 18 | cargo install --path . --locked 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trussed-totp-pc-tutorial 2 | 3 | This is a simple application implementing OATH TOTP, built on [Trussed][trussed]®. 4 | 5 | To try it out, run `make install`, and then for instance 6 | ``` 7 | trussed-totp-pc-tutorial register alice@trussed.dev JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP 8 | ``` 9 | This registers a credential, which is stored in `state.littlefs2`. 10 | 11 | To generate a one-time password, run 12 | ``` 13 | trussed-totp-pc-tutorial authenticate alice@trussed.dev 14 | ``` 15 | 16 | For more logging prefix commands with, e.g., `RUST_LOG=debug`. 17 | 18 | [trussed]: https://trussed.dev 19 | 20 | #### License 21 | 22 | 23 | Licensed under either of Apache License, Version 24 | 2.0 or MIT license at your option. 25 | 26 | 27 |
28 | 29 | 30 | Unless you explicitly state otherwise, any contribution intentionally submitted 31 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 32 | be dual licensed as above, without any additional terms or conditions. 33 | 34 | -------------------------------------------------------------------------------- /src/authenticator.rs: -------------------------------------------------------------------------------- 1 | //! The example TOTP authenticator of this tutorial. 2 | //! 3 | //! While not a requirement from Trussed®, we use a pattern of declaring 4 | //! command/request inputs (`Register` and `Authenticate`) as Rust structs. 5 | //! 6 | //! This pushes (or can help to push) the question of lower-level protocol 7 | //! encodings outside of the "app", which can then focus even more on 8 | //! implementing the exact logic required. 9 | 10 | use core::convert::TryInto; 11 | 12 | use delog::hex_str; 13 | use log::{debug, info}; 14 | use serde::{Deserialize, Serialize}; 15 | use trussed::{syscall, try_syscall, types::Message}; 16 | use trussed::{Bytes, types::{Mechanism, /*SignatureSerialization, StorageAttributes,*/ Location}}; 17 | 18 | use crate::Result; 19 | 20 | const MAX_CRED_LABEL_LENGTH: usize = 256; 21 | 22 | /// The core "app", implementing TOTP authentication, using Trussed® 23 | pub struct Authenticator 24 | { 25 | trussed: T, 26 | } 27 | 28 | #[derive(Clone, Debug, PartialEq)] 29 | /// One of the two commands this authenticator can process: credential registration 30 | pub struct Register { 31 | /// Label for the credential, e.g. `alice@trussed.dev` 32 | pub label: String, 33 | /// Choices could be made here on who is responsible for decoding the raw secret bytes 34 | pub base32_secret: String, 35 | /// Period in seconds after which the counter for the TOTP token is incremented 36 | pub period_seconds: u64, 37 | } 38 | 39 | #[derive(Clone, Debug, PartialEq)] 40 | /// One of the two commands this authenticator can process: authentication with a registered 41 | /// credential 42 | pub struct Authenticate { 43 | /// Label for the credential, e.g. `alice@trussed.dev` 44 | pub label: String, 45 | /// Timestamp (seconds since UNIX epoch) 46 | // pub timestamp: std::time::Instant, 47 | pub timestamp: u64, 48 | } 49 | 50 | #[derive(Clone, Debug, PartialEq)] 51 | /// The public API of this TOTP authenticator 52 | #[allow(missing_docs)] 53 | pub enum Command { 54 | Register(Register), 55 | Authenticate(Authenticate), 56 | } 57 | 58 | #[derive(Clone, Debug, PartialEq)] 59 | /// Contains a one-time password 60 | pub struct Otp(pub u64); 61 | 62 | /// OTP codes are typically presented as left-zero-padded strings 63 | impl core::fmt::Display for Otp { 64 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 65 | write!(f, "{:06}", self.0) 66 | } 67 | } 68 | 69 | #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] 70 | /// The metadata associated with a TOTP secret, enabling later use 71 | /// in the `Authenticate` command. 72 | /// 73 | /// The `serde::Serialize` and `serde::Deserialize` implementations allow 74 | /// credentials to easily be stored in binary format. 75 | pub struct Credential { 76 | label: trussed::Bytes, 77 | period_seconds: u64, 78 | key_handle: trussed::types::KeyId, 79 | } 80 | 81 | impl Authenticator 82 | where 83 | T: trussed::Client + trussed::client::mechanisms::Totp, 84 | { 85 | /// Constructor, consumes a Trussed client 86 | pub fn new(trussed: T) -> Self { 87 | Self { trussed } 88 | } 89 | 90 | /// Injects the TOTP secret in Trussed's key storage, stores a `Credential` 91 | /// with the metadata for the secret. 92 | pub fn register(&mut self, parameters: &Register) -> Result<()> { 93 | 94 | let Register { label, base32_secret, period_seconds } = parameters; 95 | debug!("register {:?}", parameters); 96 | 97 | // 1. Decode TOTP secret 98 | let raw_key_bytes = data_encoding::BASE32.decode(&base32_secret.as_bytes())?; 99 | let raw_key: [u8; 20] = (&raw_key_bytes[..]).try_into()?; 100 | debug!("raw key: {}", hex_str!(&raw_key, 4)); 101 | 102 | // 2. Store secret in Trussed 103 | let key_handle = syscall!( 104 | self.trussed 105 | .unsafe_inject_shared_key(&raw_key, Location::Internal) 106 | ).key; 107 | info!("new key handle: {:?}", key_handle); 108 | 109 | // 3. Generate credential 110 | let credential = Credential { 111 | label: Bytes::from_slice(label.as_bytes()).map_err(EmptyError::from)?, 112 | period_seconds: *period_seconds, 113 | key_handle, 114 | }; 115 | let mut buf = [0u8; 512]; 116 | let serialized_credential = postcard::to_slice(&credential, &mut buf) 117 | .map_err(|_| anyhow::anyhow!("postcard serialization error"))?; 118 | 119 | // 4. Store credential 120 | let filename = self.filename_for_label(&label); 121 | debug!("saving to filename {}", filename.as_ref()); 122 | 123 | syscall!(self.trussed.write_file( 124 | Location::Internal, 125 | filename, 126 | Bytes::from_slice(&*serialized_credential).unwrap(), 127 | None 128 | )); 129 | 130 | // done \o/ 131 | Ok(()) 132 | } 133 | 134 | /// Looks up a previously registered credential (else fails), 135 | /// create a TOTP using the supplied timestamp. 136 | pub fn authenticate(&mut self, parameters: &Authenticate) -> Result { 137 | let Authenticate { label, timestamp } = parameters; 138 | debug!("authenticate {:?}", parameters); 139 | 140 | // 1. Load credential 141 | let filename = self.filename_for_label(&label); 142 | let serialized_credential = try_syscall!(self.trussed.read_file( 143 | Location::Internal, 144 | filename, 145 | )) 146 | .map_err(|_| anyhow::anyhow!("Could not find a credential labelled {}", label))? 147 | .data; 148 | 149 | let credential: Credential = postcard::from_bytes(serialized_credential.as_ref()) 150 | .map_err(|_| anyhow::anyhow!("postcard deserialization error"))?; 151 | debug!("found credential: {:?}", &credential); 152 | 153 | // 2. Calculate OTP 154 | let counter = *timestamp / credential.period_seconds; 155 | 156 | // // TODO: take this out of Trussed again, and implement "by hand" for posterity 157 | // let counter_bytes: [u8; 8] = counter.to_be_bytes(); 158 | // let hmac = syscall!(self.trussed.sign( 159 | // Mechanism::Totp, 160 | // credential.handle, 161 | // &counter_bytes, 162 | // SignatureSerialization::Raw, 163 | // )).signature; 164 | // debug!("calculated HMAC: {}", hex_str!(&hmac, 4)); 165 | 166 | let otp = syscall!(self.trussed.sign_totp( 167 | credential.key_handle, 168 | counter, 169 | )).signature; 170 | 171 | try_syscall!(self.trussed.confirm_user_present(5_000)) 172 | .map_err(|_| anyhow::anyhow!("Could not obtain confirmation of user presence!"))?; 173 | 174 | let otp = u64::from_le_bytes(otp[..8].try_into().unwrap()); 175 | debug!("calculated OTP: {}", otp); 176 | 177 | // done \o_ 178 | Ok(Otp(otp)) 179 | } 180 | 181 | /// Helper method, using Trussed, to determine a filename for the Credential 182 | fn filename_for_label(&mut self, label: &str) -> trussed::types::PathBuf { 183 | let filename = syscall!(self.trussed.hash(Mechanism::Sha256, Message::from_slice(label.as_bytes()).unwrap())).hash; 184 | let mut hex_filename = [0u8; 16]; 185 | use std::io::Write as _; 186 | // first 8 bytes of SHA256 hash of label, as hexadecimal digits 187 | hex_filename.as_mut().write_fmt(format_args!("{}", delog::hexstr!(&filename[..8]))).unwrap(); 188 | 189 | trussed::types::PathBuf::from(hex_filename.as_ref()) 190 | } 191 | } 192 | 193 | #[derive(Debug, thiserror::Error)] 194 | /// In embedded, we don't have `std::error::Error`, and in many situations, 195 | /// the type `()` is used as error type. To make this compatible with our use 196 | /// of `std` Errors here, we need a wrapper type (the error trait is not implemented for `()`). 197 | pub enum EmptyError { 198 | #[error("no error")] 199 | /// The empty singleton 200 | Empty, 201 | } 202 | 203 | impl core::convert::From<()> for EmptyError { 204 | fn from(_: ()) -> Self { 205 | Self::Empty 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the CLI, an "interface" for our "runner". 2 | use core::convert::TryFrom; 3 | 4 | use anyhow::{Error, Result}; 5 | use clap::{ 6 | App, 7 | Arg, 8 | crate_authors, crate_version, 9 | SubCommand, 10 | }; 11 | 12 | use crate::authenticator::{Authenticate, Command, Register}; 13 | 14 | /// entry point to the CLI 15 | pub fn init_cli() -> (clap::ArgMatches<'static>, String) { 16 | let clap_app = clap_app(); 17 | let matches = clap_app.get_matches(); 18 | // no panic - clap enforces the value's existence 19 | let state_file: String = matches.value_of("STATE-FILE").unwrap().into(); 20 | (matches, state_file.into()) 21 | } 22 | 23 | const ABOUT: &str = " 24 | An example app, using Trussed®, running on PC, implementing TOTP. 25 | 26 | Project homepage: . 27 | "; 28 | 29 | /// defines the commands, flags, arguments and options of the CLI 30 | pub fn clap_app() -> clap::App<'static, 'static> { 31 | 32 | let app = App::new("trussed-otp-pc-tutorial") 33 | .author(crate_authors!()) 34 | .version(crate_version!()) 35 | .about(ABOUT) 36 | // .help_message("Prints help information. Use --help for more details.") 37 | .setting(clap::AppSettings::SubcommandRequiredElseHelp) 38 | 39 | .arg(Arg::with_name("STATE-FILE") 40 | .short("s") 41 | .long("state-file") 42 | .default_value("state.littlefs2") 43 | .help("file containing persistent state") 44 | .required(false) 45 | .global(true) 46 | ) 47 | 48 | // cf. https://github.com/google/google-authenticator/wiki/Key-Uri-Format 49 | // eg. otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example 50 | 51 | .subcommand(SubCommand::with_name("register") 52 | .about("register a TOTP secret") 53 | .arg(Arg::with_name("label") 54 | .help("label to use for the TOTP secret, e.g. alice@trussed.dev") 55 | .value_name("LABEL") 56 | .required(true) 57 | ) 58 | .arg(Arg::with_name("secret") 59 | .help("the actual TOTP seed, e.g. JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP") 60 | .value_name("SECRET") 61 | .required(true) 62 | ) 63 | ) 64 | 65 | .subcommand(SubCommand::with_name("authenticate") 66 | .about("generate a TOTP from a previously registered secret") 67 | .arg(Arg::with_name("TIMESTAMP") 68 | .short("t") 69 | .long("timestamp") 70 | .help("timestamp to use to generate the OTP, as seconds since the UNIX epoch") 71 | .value_name("TIMESTAMP") 72 | .required(false) 73 | ) 74 | .arg(Arg::with_name("label") 75 | .help("Label of the TOTP secret to use, e.g. alice@trussed.dev") 76 | .value_name("LABEL") 77 | .required(true) 78 | ) 79 | ) 80 | ; 81 | 82 | app 83 | } 84 | 85 | impl TryFrom<&'_ clap::ArgMatches<'static>> for Command { 86 | type Error = Error; 87 | fn try_from(args: &clap::ArgMatches<'static>) -> Result { 88 | if let Some(command) = args.subcommand_matches("register") { 89 | return Ok(Command::Register(Register { 90 | label: command.value_of("label").unwrap().into(), 91 | base32_secret: command.value_of("secret").unwrap().into(), 92 | period_seconds: 30, 93 | })); 94 | } 95 | 96 | if let Some(command) = args.subcommand_matches("authenticate") { 97 | let timestamp = match command.value_of("timestamp") { 98 | Some(s) => s.parse()?, 99 | None => { 100 | let since_epoch = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap(); 101 | since_epoch.as_secs() 102 | } 103 | }; 104 | return Ok(Command::Authenticate(Authenticate { 105 | label: command.value_of("label").unwrap().into(), 106 | timestamp, 107 | })); 108 | } 109 | Err(anyhow::anyhow!("Unexpected case")) 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | //! # Trussed®-based TOTP authenticator, for PC. 3 | //! 4 | //! This is a demo implementation of a TOTP authenticator, built on [Trussed®][trussed]. 5 | //! 6 | //! In general, Trussed requires a platform-specific implementation of its `Platform` trait, 7 | //! encapsulating: 8 | //! - Reliable **true** random number generator 9 | //! - `littlefs`-based persistent and volatile storage backends 10 | //! - the `UserInterface`, allowing prompts and checks of user presence, among other things 11 | //! 12 | //! The Trussed service can then be constructed, and hands out a configurable number of Trussed 13 | //! client interfaces, which are passed into application constructors – having access to a Trussed 14 | //! client is essentially what makes an app a "Trussed app", and hence very portable, if the app 15 | //! focuses only on implementation of its logic and uses its client for all: 16 | //! - cryptography 17 | //! - storage (keys and files) 18 | //! - user interface 19 | //! 20 | //! Outside of Trussed's domain is the implementation of a "runner", which includes 21 | //! - bring-up of the platform (e.g., in embedded, initialization of all peripherals) 22 | //! - setup and wiring of all components (internally, [`interchange`][interchange] is used) 23 | //! - implementation of external interfaces (e.g. USB, NFC, CLI, HTTP) 24 | //! - implementation of dispatch between (possibly multiple) interfaces and (possibly multiple) 25 | //! apps, including serialization/deserialization of interface-level protocols (we recommend use of 26 | //! [`serde`][serde] and, again, [`interchange`][interchange]). 27 | //! - scheduling of all the components involved (e.g., in embedded, we recommend [`RTIC`][rtic], an 28 | //! efficient, minimal scheduler which uses the hardware's interrupt controller directly. On 29 | //! PCs, there are of course many competing options). 30 | //! 31 | //! The reason for splitting out all this additional infrastructure is a general cryptographic 32 | //! principle of doing one thing and doing that one thing well (although... Trussed does do quite a 33 | //! lot of things), but also flexibility, as different platforms may have very different 34 | //! capabilities, use async/await instead of RTIC, etc. etc. 35 | //! 36 | //! 37 | //! [trussed]: https://trussed.dev 38 | //! [interchange]: https://docs.rs/interchange/ 39 | //! [serde]: https://serde.rs 40 | //! [rtic]: https://rtic.rs 41 | 42 | /// In real life we would use `no_std`-compatible errors, and define `thiserror` wrappers. 43 | /// Here, we are somewhat untyped and just use `anyhow`. 44 | pub use anyhow::Result; 45 | 46 | pub mod authenticator; 47 | pub mod cli; 48 | pub mod platform; 49 | 50 | #[cfg(feature = "include-main-in-lib-for-docs")] 51 | pub mod main; 52 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryFrom; 2 | 3 | use anyhow::Result; 4 | use log::info; 5 | 6 | // #[cfg(feature = "include-main-in-lib-for-docs")] 7 | // use crate::{authenticator, cli, platform}; 8 | // #[cfg(not(feature = "include-main-in-lib-for-docs"))] 9 | use tutorial::{authenticator, cli, platform}; 10 | 11 | 12 | /// Simplified "runner" to demonstrate the TOTP authenticator app. 13 | /// 14 | /// In general: There is a runner (often a firmware), which is responsible 15 | /// for piping together and scheduling the main components of a Trussed runner: 16 | /// 17 | /// - interfaces (typically USB, possibly NFC) 18 | /// - dispatch (between the interfaces and the apps) 19 | /// - multiple Trussed applications 20 | /// - multiple Trussed clients, connected to the Trussed service 21 | /// 22 | /// In this tutorial, the interface is a CLI, which directly dispatches into the single app. 23 | /// 24 | /// This allows us to more clearly demonstrate: 25 | /// - the authenticator app, which is only concerned with the logic needed to 26 | /// process a `Command`, using its Trussed client for crypto, storage, and UI. 27 | /// - the piping necessary to pack everything up into a runner 28 | /// 29 | pub fn main() -> Result<()> { 30 | 31 | pretty_env_logger::init(); 32 | info!("Welcome to the tutorial."); 33 | 34 | let (args, state_file) = cli::init_cli(); 35 | 36 | // setup platform (in our case, PC) 37 | let trussed_platform = platform::init_platform(state_file); 38 | 39 | // setup Trussed 40 | let mut trussed_service = trussed::service::Service::new(trussed_platform); 41 | let client_id = "totp"; 42 | // In real life, `trussed_service.try_new_client` has an additional parameter that is a `Syscall` 43 | // implementation; giving the client a way to signal the ambient runtime to call the service. 44 | // Here, we use the service's implementation of `Syscall`, where it simply calls itself :) 45 | let trussed_client = trussed_service.try_as_new_client(client_id).unwrap(); 46 | 47 | // setup authenticator 48 | let mut authenticator = authenticator::Authenticator::new(trussed_client); 49 | 50 | 51 | // The "runner"'s actual "scheduling" part starts here 52 | info!("Let's go!"); 53 | 54 | // the "args" come in over the CLI "interface", and are "deserialized" for processing 55 | // using `Command`'s implementation of `TryFrom`, the standard Trait for fallible type conversion 56 | let command = authenticator::Command::try_from(&args)?; 57 | 58 | // the command is "dispatched" into the application 59 | match command { 60 | authenticator::Command::Register(register) => { 61 | authenticator.register(®ister)?; 62 | } 63 | authenticator::Command::Authenticate(authenticate) => { 64 | let otp = authenticator.authenticate(&authenticate)?; 65 | 66 | // the application response is "dispatched" back over the CLI 67 | println!("{}", &otp); 68 | } 69 | } 70 | 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /src/platform.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | //! Implementation of `trussed::Platform` trait for our platform, PC 3 | 4 | use log::info; 5 | 6 | use trussed::platform::{consent, reboot, ui}; 7 | 8 | pub mod store; 9 | 10 | trussed::platform!(Platform, 11 | R: chacha20::ChaCha8Rng, 12 | S: store::Store, 13 | UI: UserInterface, 14 | ); 15 | 16 | /// sets up the platform components and then itself 17 | pub fn init_platform(state_path: impl AsRef) -> Platform { 18 | use trussed::service::SeedableRng; 19 | let rng = chacha20::ChaCha8Rng::from_rng(rand_core::OsRng).unwrap(); 20 | let store = store::init_store(state_path); 21 | let ui = UserInterface::new(); 22 | 23 | let platform = Platform::new(rng, store, ui); 24 | 25 | platform 26 | } 27 | 28 | /// Implementation of `trussed::platform::UserInterface` trait 29 | pub struct UserInterface { 30 | start_time: std::time::Instant, 31 | } 32 | 33 | impl UserInterface { 34 | pub fn new() -> Self { 35 | Self { 36 | start_time: std::time::Instant::now(), 37 | } 38 | } 39 | } 40 | 41 | impl trussed::platform::UserInterface for UserInterface 42 | { 43 | /// Prompt user to type a word for confirmation 44 | fn check_user_presence(&mut self) -> consent::Level { 45 | use std::io::Read as _; 46 | // This is not nice - we should "peek" and return Level::None 47 | // if there is no key pressed yet (unbuffered read from stdin). 48 | // Couldn't get this to work (without pulling in ncurses or similar). 49 | std::io::stdin().bytes().next(); 50 | consent::Level::Normal 51 | } 52 | 53 | fn set_status(&mut self, status: ui::Status) { 54 | info!("Set status: {:?}", status); 55 | 56 | if status == ui::Status::WaitingForUserPresence { 57 | use std::io::{Write as _}; 58 | let mut stdout = std::io::stdout(); 59 | write!(stdout, "Press ENTER to confirm (Ctrl-C to abort): ").ok(); 60 | stdout.flush().unwrap(); 61 | } 62 | } 63 | 64 | fn refresh(&mut self) {} 65 | 66 | fn uptime(&mut self) -> core::time::Duration { 67 | self.start_time.elapsed() 68 | } 69 | 70 | fn reboot(&mut self, to: reboot::To) -> ! { 71 | info!("Restart! ({:?})", to); 72 | std::process::exit(25); 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/platform/store.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | //! Trussed stores are built on underlying `littlefs` implementations. 3 | //! 4 | //! Here, we use a single binary file-backed littlefs implementation for 5 | //! persistent storage, and RAM array-backed implementations for the volatile storage. 6 | use std::{fs::File, io::{Seek as _, SeekFrom}}; 7 | 8 | pub use generic_array::{GenericArray, typenum::{consts, U16, U128, U256, U512, U1022}}; 9 | use littlefs2::const_ram_storage; 10 | use log::info; 11 | use trussed::types::{LfsResult, LfsStorage}; 12 | 13 | const_ram_storage!(VolatileStorage, 1024); 14 | // currently, `trussed` needs a dummy parameter here 15 | const_ram_storage!(ExternalStorage, 1024); 16 | 17 | trussed::store!(Store, 18 | Internal: FileFlash, 19 | External: ExternalStorage, 20 | Volatile: VolatileStorage 21 | ); 22 | 23 | pub fn init_store(state_path: impl AsRef) -> Store { 24 | let filesystem = FileFlash::new(state_path); 25 | Store::attach_else_format(filesystem, ExternalStorage::new(), VolatileStorage::new()) 26 | } 27 | 28 | pub struct FileFlash { 29 | path: std::path::PathBuf, 30 | } 31 | 32 | impl FileFlash { 33 | const SIZE: u64 = 128*1024; 34 | 35 | pub fn new(state_path: impl AsRef) -> Self { 36 | 37 | let path: std::path::PathBuf = state_path.as_ref().into(); 38 | 39 | if let Ok(file) = File::open(&path) { 40 | assert_eq!(file.metadata().unwrap().len(), Self::SIZE); 41 | } else { 42 | // TODO: error handling 43 | let file = File::create(&path).unwrap(); 44 | file.set_len(Self::SIZE).unwrap(); 45 | info!("Created new state file"); 46 | } 47 | Self { path } 48 | } 49 | } 50 | 51 | impl littlefs2::driver::Storage for FileFlash { 52 | const READ_SIZE: usize = 16; 53 | const WRITE_SIZE: usize = 16; 54 | const BLOCK_SIZE: usize = 512; 55 | 56 | const BLOCK_COUNT: usize = 128; 57 | const BLOCK_CYCLES: isize = -1; 58 | 59 | type CACHE_SIZE = U512; 60 | type LOOKAHEADWORDS_SIZE = U16; 61 | /// TODO: We can't actually be changed currently 62 | //type FILENAME_MAX_PLUS_ONE = U256; 63 | //type PATH_MAX_PLUS_ONE = U256; 64 | //const FILEBYTES_MAX: usize = littlefs2::ll::LFS_FILE_MAX as _; 65 | /// TODO: We can't actually be changed currently 66 | //type ATTRBYTES_MAX = U1022; 67 | 68 | 69 | fn read(&self, offset: usize, buffer: &mut [u8]) -> LfsResult { 70 | use std::io::Read; 71 | 72 | // debug!("reading {} bytes from {} in {:?}...", buffer.len(), offset, self.path); 73 | let mut file = File::open(&self.path).unwrap(); 74 | file.seek(SeekFrom::Start(offset as _)).unwrap(); 75 | let bytes_read = file.read(buffer).unwrap(); 76 | assert_eq!(bytes_read, buffer.len()); 77 | // debug!("..ok"); 78 | Ok(bytes_read as _) 79 | } 80 | 81 | fn write(&mut self, offset: usize, data: &[u8]) -> LfsResult { 82 | use std::io::Write; 83 | 84 | // debug!("writing {} bytes from {} in {:?}...", data.len(), offset, self.path); 85 | // debug!("{:?}", data); 86 | let mut file = std::fs::OpenOptions::new().write(true).open(&self.path).unwrap(); 87 | file.seek(SeekFrom::Start(offset as _)).unwrap(); 88 | let bytes_written = file.write(data).unwrap(); 89 | assert_eq!(bytes_written, data.len()); 90 | file.flush().unwrap(); 91 | // debug!("..ok"); 92 | Ok(bytes_written) 93 | } 94 | 95 | fn erase(&mut self, offset: usize, len: usize) -> LfsResult { 96 | use std::io::Write; 97 | 98 | // debug!("erasing {} bytes from {} in {:?}...", len, offset, self.path); 99 | let mut file = std::fs::OpenOptions::new().write(true).open(&self.path).unwrap(); 100 | file.seek(SeekFrom::Start(offset as _)).unwrap(); 101 | let zero_block = [0xFFu8; Self::BLOCK_SIZE]; 102 | for _ in 0..(len/Self::BLOCK_SIZE) { 103 | let bytes_written = file.write(&zero_block).unwrap(); 104 | assert_eq!(bytes_written, Self::BLOCK_SIZE); 105 | } 106 | file.flush().unwrap(); 107 | // debug!("..ok"); 108 | Ok(len) 109 | } 110 | 111 | } 112 | 113 | --------------------------------------------------------------------------------