diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-01 00:00:00 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-01 00:00:00 +0000 |
| commit | 96660a90e4cd296a2922d7a547de4cd9d0b1928b (patch) | |
| tree | e5216e22ee1a3e1653d8d1ecd856f4f03615d6a1 | |
| parent | 6423baebd92e45c9be85157c443dff42e65d8d14 (diff) | |
feat(login) password login using encrypted nsec
Enables the user to only handle the nsec upon first use of the tool
by encrypting it with a password and storing it on disk in an
application cache.
The approach to encryption draws heavily from that used by the gossip
nostr client.
- unencrypted nsec is zeroed from memory
- a salt is used to defend against rainbow tables
- computationally expensive key stretching defends against
brute-force attacks of passwords with low entropy.
There is UX trade-off between decryption speed and key-stretching
computation. This UX challenge is exacerbated in a cli tool as
decryption must take place more regularly. Thought was put into the
selected n_log and a heavily reduced value is provided for long
passwords where security benefits are smaller.
A more granular reducing in computation was also considered by
rejected to avoided to revealing just how weak a password is as most
weak passwords are reused.
| -rw-r--r-- | Cargo.lock | 1896 | ||||
| -rw-r--r-- | Cargo.toml | 8 | ||||
| -rw-r--r-- | flake.nix | 18 | ||||
| -rw-r--r-- | planning.md | 78 | ||||
| -rw-r--r-- | src/cli_interactor.rs | 31 | ||||
| -rw-r--r-- | src/config.rs | 111 | ||||
| -rw-r--r-- | src/key_handling/encryption.rs | 247 | ||||
| -rw-r--r-- | src/key_handling/mod.rs | 1 | ||||
| -rw-r--r-- | src/key_handling/users.rs | 262 | ||||
| -rw-r--r-- | src/login.rs | 83 | ||||
| -rw-r--r-- | src/main.rs | 5 | ||||
| -rw-r--r-- | src/sub_commands/login.rs | 3 | ||||
| -rw-r--r-- | test_utils/Cargo.toml | 2 | ||||
| -rw-r--r-- | test_utils/src/lib.rs | 116 | ||||
| -rw-r--r-- | tests/login.rs | 334 |
15 files changed, 3046 insertions, 149 deletions
| @@ -3,6 +3,54 @@ | |||
| 3 | version = 3 | 3 | version = 3 |
| 4 | 4 | ||
| 5 | [[package]] | 5 | [[package]] |
| 6 | name = "addr2line" | ||
| 7 | version = "0.21.0" | ||
| 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" | ||
| 10 | dependencies = [ | ||
| 11 | "gimli", | ||
| 12 | ] | ||
| 13 | |||
| 14 | [[package]] | ||
| 15 | name = "adler" | ||
| 16 | version = "1.0.2" | ||
| 17 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||
| 19 | |||
| 20 | [[package]] | ||
| 21 | name = "aead" | ||
| 22 | version = "0.5.2" | ||
| 23 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 24 | checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" | ||
| 25 | dependencies = [ | ||
| 26 | "crypto-common", | ||
| 27 | "generic-array", | ||
| 28 | ] | ||
| 29 | |||
| 30 | [[package]] | ||
| 31 | name = "aes" | ||
| 32 | version = "0.7.5" | ||
| 33 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 34 | checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" | ||
| 35 | dependencies = [ | ||
| 36 | "cfg-if", | ||
| 37 | "cipher 0.3.0", | ||
| 38 | "cpufeatures", | ||
| 39 | "opaque-debug", | ||
| 40 | ] | ||
| 41 | |||
| 42 | [[package]] | ||
| 43 | name = "aes" | ||
| 44 | version = "0.8.3" | ||
| 45 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 46 | checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" | ||
| 47 | dependencies = [ | ||
| 48 | "cfg-if", | ||
| 49 | "cipher 0.4.4", | ||
| 50 | "cpufeatures", | ||
| 51 | ] | ||
| 52 | |||
| 53 | [[package]] | ||
| 6 | name = "aho-corasick" | 54 | name = "aho-corasick" |
| 7 | version = "1.0.5" | 55 | version = "1.0.5" |
| 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | 56 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -81,12 +129,219 @@ dependencies = [ | |||
| 81 | ] | 129 | ] |
| 82 | 130 | ||
| 83 | [[package]] | 131 | [[package]] |
| 132 | name = "async-broadcast" | ||
| 133 | version = "0.5.1" | ||
| 134 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 135 | checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" | ||
| 136 | dependencies = [ | ||
| 137 | "event-listener", | ||
| 138 | "futures-core", | ||
| 139 | ] | ||
| 140 | |||
| 141 | [[package]] | ||
| 142 | name = "async-channel" | ||
| 143 | version = "1.9.0" | ||
| 144 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 145 | checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" | ||
| 146 | dependencies = [ | ||
| 147 | "concurrent-queue", | ||
| 148 | "event-listener", | ||
| 149 | "futures-core", | ||
| 150 | ] | ||
| 151 | |||
| 152 | [[package]] | ||
| 153 | name = "async-executor" | ||
| 154 | version = "1.5.1" | ||
| 155 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 156 | checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" | ||
| 157 | dependencies = [ | ||
| 158 | "async-lock", | ||
| 159 | "async-task", | ||
| 160 | "concurrent-queue", | ||
| 161 | "fastrand 1.9.0", | ||
| 162 | "futures-lite", | ||
| 163 | "slab", | ||
| 164 | ] | ||
| 165 | |||
| 166 | [[package]] | ||
| 167 | name = "async-fs" | ||
| 168 | version = "1.6.0" | ||
| 169 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 170 | checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" | ||
| 171 | dependencies = [ | ||
| 172 | "async-lock", | ||
| 173 | "autocfg", | ||
| 174 | "blocking", | ||
| 175 | "futures-lite", | ||
| 176 | ] | ||
| 177 | |||
| 178 | [[package]] | ||
| 179 | name = "async-io" | ||
| 180 | version = "1.13.0" | ||
| 181 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 182 | checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" | ||
| 183 | dependencies = [ | ||
| 184 | "async-lock", | ||
| 185 | "autocfg", | ||
| 186 | "cfg-if", | ||
| 187 | "concurrent-queue", | ||
| 188 | "futures-lite", | ||
| 189 | "log", | ||
| 190 | "parking", | ||
| 191 | "polling", | ||
| 192 | "rustix 0.37.23", | ||
| 193 | "slab", | ||
| 194 | "socket2 0.4.9", | ||
| 195 | "waker-fn", | ||
| 196 | ] | ||
| 197 | |||
| 198 | [[package]] | ||
| 199 | name = "async-lock" | ||
| 200 | version = "2.8.0" | ||
| 201 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 202 | checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" | ||
| 203 | dependencies = [ | ||
| 204 | "event-listener", | ||
| 205 | ] | ||
| 206 | |||
| 207 | [[package]] | ||
| 208 | name = "async-process" | ||
| 209 | version = "1.7.0" | ||
| 210 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 211 | checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" | ||
| 212 | dependencies = [ | ||
| 213 | "async-io", | ||
| 214 | "async-lock", | ||
| 215 | "autocfg", | ||
| 216 | "blocking", | ||
| 217 | "cfg-if", | ||
| 218 | "event-listener", | ||
| 219 | "futures-lite", | ||
| 220 | "rustix 0.37.23", | ||
| 221 | "signal-hook", | ||
| 222 | "windows-sys 0.48.0", | ||
| 223 | ] | ||
| 224 | |||
| 225 | [[package]] | ||
| 226 | name = "async-recursion" | ||
| 227 | version = "1.0.4" | ||
| 228 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 229 | checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" | ||
| 230 | dependencies = [ | ||
| 231 | "proc-macro2", | ||
| 232 | "quote", | ||
| 233 | "syn 2.0.32", | ||
| 234 | ] | ||
| 235 | |||
| 236 | [[package]] | ||
| 237 | name = "async-task" | ||
| 238 | version = "4.4.0" | ||
| 239 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 240 | checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" | ||
| 241 | |||
| 242 | [[package]] | ||
| 243 | name = "async-trait" | ||
| 244 | version = "0.1.73" | ||
| 245 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 246 | checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" | ||
| 247 | dependencies = [ | ||
| 248 | "proc-macro2", | ||
| 249 | "quote", | ||
| 250 | "syn 2.0.32", | ||
| 251 | ] | ||
| 252 | |||
| 253 | [[package]] | ||
| 254 | name = "atomic-waker" | ||
| 255 | version = "1.1.1" | ||
| 256 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 257 | checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" | ||
| 258 | |||
| 259 | [[package]] | ||
| 84 | name = "autocfg" | 260 | name = "autocfg" |
| 85 | version = "1.1.0" | 261 | version = "1.1.0" |
| 86 | source = "registry+https://github.com/rust-lang/crates.io-index" | 262 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 87 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | 263 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" |
| 88 | 264 | ||
| 89 | [[package]] | 265 | [[package]] |
| 266 | name = "backtrace" | ||
| 267 | version = "0.3.69" | ||
| 268 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 269 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" | ||
| 270 | dependencies = [ | ||
| 271 | "addr2line", | ||
| 272 | "cc", | ||
| 273 | "cfg-if", | ||
| 274 | "libc", | ||
| 275 | "miniz_oxide", | ||
| 276 | "object", | ||
| 277 | "rustc-demangle", | ||
| 278 | ] | ||
| 279 | |||
| 280 | [[package]] | ||
| 281 | name = "base64" | ||
| 282 | version = "0.21.3" | ||
| 283 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 284 | checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" | ||
| 285 | |||
| 286 | [[package]] | ||
| 287 | name = "base64ct" | ||
| 288 | version = "1.6.0" | ||
| 289 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 290 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" | ||
| 291 | |||
| 292 | [[package]] | ||
| 293 | name = "bech32" | ||
| 294 | version = "0.9.1" | ||
| 295 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 296 | checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" | ||
| 297 | |||
| 298 | [[package]] | ||
| 299 | name = "bip39" | ||
| 300 | version = "2.0.0" | ||
| 301 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 302 | checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" | ||
| 303 | dependencies = [ | ||
| 304 | "bitcoin_hashes 0.11.0", | ||
| 305 | "serde", | ||
| 306 | "unicode-normalization", | ||
| 307 | ] | ||
| 308 | |||
| 309 | [[package]] | ||
| 310 | name = "bitcoin" | ||
| 311 | version = "0.30.1" | ||
| 312 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 313 | checksum = "4e99ff7289b20a7385f66a0feda78af2fc119d28fb56aea8886a9cd0a4abdd75" | ||
| 314 | dependencies = [ | ||
| 315 | "bech32", | ||
| 316 | "bitcoin-private", | ||
| 317 | "bitcoin_hashes 0.12.0", | ||
| 318 | "hex_lit", | ||
| 319 | "secp256k1", | ||
| 320 | ] | ||
| 321 | |||
| 322 | [[package]] | ||
| 323 | name = "bitcoin-private" | ||
| 324 | version = "0.1.0" | ||
| 325 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 326 | checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" | ||
| 327 | |||
| 328 | [[package]] | ||
| 329 | name = "bitcoin_hashes" | ||
| 330 | version = "0.11.0" | ||
| 331 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 332 | checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" | ||
| 333 | |||
| 334 | [[package]] | ||
| 335 | name = "bitcoin_hashes" | ||
| 336 | version = "0.12.0" | ||
| 337 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 338 | checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" | ||
| 339 | dependencies = [ | ||
| 340 | "bitcoin-private", | ||
| 341 | "serde", | ||
| 342 | ] | ||
| 343 | |||
| 344 | [[package]] | ||
| 90 | name = "bitflags" | 345 | name = "bitflags" |
| 91 | version = "1.3.2" | 346 | version = "1.3.2" |
| 92 | source = "registry+https://github.com/rust-lang/crates.io-index" | 347 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -99,6 +354,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 99 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" | 354 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" |
| 100 | 355 | ||
| 101 | [[package]] | 356 | [[package]] |
| 357 | name = "block-buffer" | ||
| 358 | version = "0.10.4" | ||
| 359 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 360 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | ||
| 361 | dependencies = [ | ||
| 362 | "generic-array", | ||
| 363 | ] | ||
| 364 | |||
| 365 | [[package]] | ||
| 366 | name = "block-modes" | ||
| 367 | version = "0.8.1" | ||
| 368 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 369 | checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" | ||
| 370 | dependencies = [ | ||
| 371 | "block-padding 0.2.1", | ||
| 372 | "cipher 0.3.0", | ||
| 373 | ] | ||
| 374 | |||
| 375 | [[package]] | ||
| 376 | name = "block-padding" | ||
| 377 | version = "0.2.1" | ||
| 378 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 379 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" | ||
| 380 | |||
| 381 | [[package]] | ||
| 382 | name = "block-padding" | ||
| 383 | version = "0.3.3" | ||
| 384 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 385 | checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" | ||
| 386 | dependencies = [ | ||
| 387 | "generic-array", | ||
| 388 | ] | ||
| 389 | |||
| 390 | [[package]] | ||
| 391 | name = "blocking" | ||
| 392 | version = "1.3.1" | ||
| 393 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 394 | checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" | ||
| 395 | dependencies = [ | ||
| 396 | "async-channel", | ||
| 397 | "async-lock", | ||
| 398 | "async-task", | ||
| 399 | "atomic-waker", | ||
| 400 | "fastrand 1.9.0", | ||
| 401 | "futures-lite", | ||
| 402 | "log", | ||
| 403 | ] | ||
| 404 | |||
| 405 | [[package]] | ||
| 102 | name = "bstr" | 406 | name = "bstr" |
| 103 | version = "1.6.2" | 407 | version = "1.6.2" |
| 104 | source = "registry+https://github.com/rust-lang/crates.io-index" | 408 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -110,6 +414,33 @@ dependencies = [ | |||
| 110 | ] | 414 | ] |
| 111 | 415 | ||
| 112 | [[package]] | 416 | [[package]] |
| 417 | name = "bumpalo" | ||
| 418 | version = "3.13.0" | ||
| 419 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 420 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" | ||
| 421 | |||
| 422 | [[package]] | ||
| 423 | name = "byteorder" | ||
| 424 | version = "1.4.3" | ||
| 425 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 426 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" | ||
| 427 | |||
| 428 | [[package]] | ||
| 429 | name = "bytes" | ||
| 430 | version = "1.4.0" | ||
| 431 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 432 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" | ||
| 433 | |||
| 434 | [[package]] | ||
| 435 | name = "cbc" | ||
| 436 | version = "0.1.2" | ||
| 437 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 438 | checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" | ||
| 439 | dependencies = [ | ||
| 440 | "cipher 0.4.4", | ||
| 441 | ] | ||
| 442 | |||
| 443 | [[package]] | ||
| 113 | name = "cc" | 444 | name = "cc" |
| 114 | version = "1.0.83" | 445 | version = "1.0.83" |
| 115 | source = "registry+https://github.com/rust-lang/crates.io-index" | 446 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -125,6 +456,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 125 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | 456 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
| 126 | 457 | ||
| 127 | [[package]] | 458 | [[package]] |
| 459 | name = "chacha20" | ||
| 460 | version = "0.9.1" | ||
| 461 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 462 | checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" | ||
| 463 | dependencies = [ | ||
| 464 | "cfg-if", | ||
| 465 | "cipher 0.4.4", | ||
| 466 | "cpufeatures", | ||
| 467 | ] | ||
| 468 | |||
| 469 | [[package]] | ||
| 470 | name = "chacha20poly1305" | ||
| 471 | version = "0.10.1" | ||
| 472 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 473 | checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" | ||
| 474 | dependencies = [ | ||
| 475 | "aead", | ||
| 476 | "chacha20", | ||
| 477 | "cipher 0.4.4", | ||
| 478 | "poly1305", | ||
| 479 | "zeroize", | ||
| 480 | ] | ||
| 481 | |||
| 482 | [[package]] | ||
| 483 | name = "cipher" | ||
| 484 | version = "0.3.0" | ||
| 485 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 486 | checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" | ||
| 487 | dependencies = [ | ||
| 488 | "generic-array", | ||
| 489 | ] | ||
| 490 | |||
| 491 | [[package]] | ||
| 492 | name = "cipher" | ||
| 493 | version = "0.4.4" | ||
| 494 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 495 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" | ||
| 496 | dependencies = [ | ||
| 497 | "crypto-common", | ||
| 498 | "inout", | ||
| 499 | "zeroize", | ||
| 500 | ] | ||
| 501 | |||
| 502 | [[package]] | ||
| 128 | name = "clap" | 503 | name = "clap" |
| 129 | version = "4.4.2" | 504 | version = "4.4.2" |
| 130 | source = "registry+https://github.com/rust-lang/crates.io-index" | 505 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -177,6 +552,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 177 | checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" | 552 | checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" |
| 178 | 553 | ||
| 179 | [[package]] | 554 | [[package]] |
| 555 | name = "concurrent-queue" | ||
| 556 | version = "2.2.0" | ||
| 557 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 558 | checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" | ||
| 559 | dependencies = [ | ||
| 560 | "crossbeam-utils", | ||
| 561 | ] | ||
| 562 | |||
| 563 | [[package]] | ||
| 180 | name = "console" | 564 | name = "console" |
| 181 | version = "0.15.7" | 565 | version = "0.15.7" |
| 182 | source = "registry+https://github.com/rust-lang/crates.io-index" | 566 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -190,19 +574,75 @@ dependencies = [ | |||
| 190 | ] | 574 | ] |
| 191 | 575 | ||
| 192 | [[package]] | 576 | [[package]] |
| 577 | name = "core-foundation" | ||
| 578 | version = "0.9.3" | ||
| 579 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 580 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" | ||
| 581 | dependencies = [ | ||
| 582 | "core-foundation-sys", | ||
| 583 | "libc", | ||
| 584 | ] | ||
| 585 | |||
| 586 | [[package]] | ||
| 587 | name = "core-foundation-sys" | ||
| 588 | version = "0.8.4" | ||
| 589 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 590 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" | ||
| 591 | |||
| 592 | [[package]] | ||
| 593 | name = "cpufeatures" | ||
| 594 | version = "0.2.9" | ||
| 595 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 596 | checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" | ||
| 597 | dependencies = [ | ||
| 598 | "libc", | ||
| 599 | ] | ||
| 600 | |||
| 601 | [[package]] | ||
| 602 | name = "crossbeam-utils" | ||
| 603 | version = "0.8.16" | ||
| 604 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 605 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" | ||
| 606 | dependencies = [ | ||
| 607 | "cfg-if", | ||
| 608 | ] | ||
| 609 | |||
| 610 | [[package]] | ||
| 611 | name = "crypto-common" | ||
| 612 | version = "0.1.6" | ||
| 613 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 614 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" | ||
| 615 | dependencies = [ | ||
| 616 | "generic-array", | ||
| 617 | "rand_core", | ||
| 618 | "typenum", | ||
| 619 | ] | ||
| 620 | |||
| 621 | [[package]] | ||
| 193 | name = "dashmap" | 622 | name = "dashmap" |
| 194 | version = "5.5.3" | 623 | version = "5.5.3" |
| 195 | source = "registry+https://github.com/rust-lang/crates.io-index" | 624 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 196 | checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" | 625 | checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" |
| 197 | dependencies = [ | 626 | dependencies = [ |
| 198 | "cfg-if", | 627 | "cfg-if", |
| 199 | "hashbrown", | 628 | "hashbrown 0.14.0", |
| 200 | "lock_api", | 629 | "lock_api", |
| 201 | "once_cell", | 630 | "once_cell", |
| 202 | "parking_lot_core", | 631 | "parking_lot_core", |
| 203 | ] | 632 | ] |
| 204 | 633 | ||
| 205 | [[package]] | 634 | [[package]] |
| 635 | name = "derivative" | ||
| 636 | version = "2.2.0" | ||
| 637 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 638 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" | ||
| 639 | dependencies = [ | ||
| 640 | "proc-macro2", | ||
| 641 | "quote", | ||
| 642 | "syn 1.0.109", | ||
| 643 | ] | ||
| 644 | |||
| 645 | [[package]] | ||
| 206 | name = "dialoguer" | 646 | name = "dialoguer" |
| 207 | version = "0.10.4" | 647 | version = "0.10.4" |
| 208 | source = "registry+https://github.com/rust-lang/crates.io-index" | 648 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -221,6 +661,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 221 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" | 661 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" |
| 222 | 662 | ||
| 223 | [[package]] | 663 | [[package]] |
| 664 | name = "digest" | ||
| 665 | version = "0.10.7" | ||
| 666 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 667 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | ||
| 668 | dependencies = [ | ||
| 669 | "block-buffer", | ||
| 670 | "crypto-common", | ||
| 671 | "subtle", | ||
| 672 | ] | ||
| 673 | |||
| 674 | [[package]] | ||
| 224 | name = "directories" | 675 | name = "directories" |
| 225 | version = "5.0.1" | 676 | version = "5.0.1" |
| 226 | source = "registry+https://github.com/rust-lang/crates.io-index" | 677 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -276,6 +727,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 276 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" | 727 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" |
| 277 | 728 | ||
| 278 | [[package]] | 729 | [[package]] |
| 730 | name = "encoding_rs" | ||
| 731 | version = "0.8.33" | ||
| 732 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 733 | checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" | ||
| 734 | dependencies = [ | ||
| 735 | "cfg-if", | ||
| 736 | ] | ||
| 737 | |||
| 738 | [[package]] | ||
| 739 | name = "enumflags2" | ||
| 740 | version = "0.7.7" | ||
| 741 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 742 | checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" | ||
| 743 | dependencies = [ | ||
| 744 | "enumflags2_derive", | ||
| 745 | "serde", | ||
| 746 | ] | ||
| 747 | |||
| 748 | [[package]] | ||
| 749 | name = "enumflags2_derive" | ||
| 750 | version = "0.7.7" | ||
| 751 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 752 | checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" | ||
| 753 | dependencies = [ | ||
| 754 | "proc-macro2", | ||
| 755 | "quote", | ||
| 756 | "syn 2.0.32", | ||
| 757 | ] | ||
| 758 | |||
| 759 | [[package]] | ||
| 760 | name = "equivalent" | ||
| 761 | version = "1.0.1" | ||
| 762 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 763 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||
| 764 | |||
| 765 | [[package]] | ||
| 279 | name = "errno" | 766 | name = "errno" |
| 280 | version = "0.3.3" | 767 | version = "0.3.3" |
| 281 | source = "registry+https://github.com/rust-lang/crates.io-index" | 768 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -297,6 +784,21 @@ dependencies = [ | |||
| 297 | ] | 784 | ] |
| 298 | 785 | ||
| 299 | [[package]] | 786 | [[package]] |
| 787 | name = "event-listener" | ||
| 788 | version = "2.5.3" | ||
| 789 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 790 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" | ||
| 791 | |||
| 792 | [[package]] | ||
| 793 | name = "fastrand" | ||
| 794 | version = "1.9.0" | ||
| 795 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 796 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" | ||
| 797 | dependencies = [ | ||
| 798 | "instant", | ||
| 799 | ] | ||
| 800 | |||
| 801 | [[package]] | ||
| 300 | name = "fastrand" | 802 | name = "fastrand" |
| 301 | version = "2.0.0" | 803 | version = "2.0.0" |
| 302 | source = "registry+https://github.com/rust-lang/crates.io-index" | 804 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -312,6 +814,21 @@ dependencies = [ | |||
| 312 | ] | 814 | ] |
| 313 | 815 | ||
| 314 | [[package]] | 816 | [[package]] |
| 817 | name = "fnv" | ||
| 818 | version = "1.0.7" | ||
| 819 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 820 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||
| 821 | |||
| 822 | [[package]] | ||
| 823 | name = "form_urlencoded" | ||
| 824 | version = "1.2.0" | ||
| 825 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 826 | checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" | ||
| 827 | dependencies = [ | ||
| 828 | "percent-encoding", | ||
| 829 | ] | ||
| 830 | |||
| 831 | [[package]] | ||
| 315 | name = "fragile" | 832 | name = "fragile" |
| 316 | version = "2.0.0" | 833 | version = "2.0.0" |
| 317 | source = "registry+https://github.com/rust-lang/crates.io-index" | 834 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -366,6 +883,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 366 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" | 883 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" |
| 367 | 884 | ||
| 368 | [[package]] | 885 | [[package]] |
| 886 | name = "futures-lite" | ||
| 887 | version = "1.13.0" | ||
| 888 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 889 | checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" | ||
| 890 | dependencies = [ | ||
| 891 | "fastrand 1.9.0", | ||
| 892 | "futures-core", | ||
| 893 | "futures-io", | ||
| 894 | "memchr", | ||
| 895 | "parking", | ||
| 896 | "pin-project-lite", | ||
| 897 | "waker-fn", | ||
| 898 | ] | ||
| 899 | |||
| 900 | [[package]] | ||
| 901 | name = "futures-macro" | ||
| 902 | version = "0.3.28" | ||
| 903 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 904 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" | ||
| 905 | dependencies = [ | ||
| 906 | "proc-macro2", | ||
| 907 | "quote", | ||
| 908 | "syn 2.0.32", | ||
| 909 | ] | ||
| 910 | |||
| 911 | [[package]] | ||
| 369 | name = "futures-sink" | 912 | name = "futures-sink" |
| 370 | version = "0.3.28" | 913 | version = "0.3.28" |
| 371 | source = "registry+https://github.com/rust-lang/crates.io-index" | 914 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -386,6 +929,7 @@ dependencies = [ | |||
| 386 | "futures-channel", | 929 | "futures-channel", |
| 387 | "futures-core", | 930 | "futures-core", |
| 388 | "futures-io", | 931 | "futures-io", |
| 932 | "futures-macro", | ||
| 389 | "futures-sink", | 933 | "futures-sink", |
| 390 | "futures-task", | 934 | "futures-task", |
| 391 | "memchr", | 935 | "memchr", |
| @@ -395,18 +939,61 @@ dependencies = [ | |||
| 395 | ] | 939 | ] |
| 396 | 940 | ||
| 397 | [[package]] | 941 | [[package]] |
| 942 | name = "generic-array" | ||
| 943 | version = "0.14.7" | ||
| 944 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 945 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" | ||
| 946 | dependencies = [ | ||
| 947 | "typenum", | ||
| 948 | "version_check", | ||
| 949 | ] | ||
| 950 | |||
| 951 | [[package]] | ||
| 398 | name = "getrandom" | 952 | name = "getrandom" |
| 399 | version = "0.2.10" | 953 | version = "0.2.10" |
| 400 | source = "registry+https://github.com/rust-lang/crates.io-index" | 954 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 401 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" | 955 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" |
| 402 | dependencies = [ | 956 | dependencies = [ |
| 403 | "cfg-if", | 957 | "cfg-if", |
| 958 | "js-sys", | ||
| 404 | "libc", | 959 | "libc", |
| 405 | "wasi", | 960 | "wasi", |
| 961 | "wasm-bindgen", | ||
| 962 | ] | ||
| 963 | |||
| 964 | [[package]] | ||
| 965 | name = "gimli" | ||
| 966 | version = "0.28.0" | ||
| 967 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 968 | checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" | ||
| 969 | |||
| 970 | [[package]] | ||
| 971 | name = "h2" | ||
| 972 | version = "0.3.21" | ||
| 973 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 974 | checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" | ||
| 975 | dependencies = [ | ||
| 976 | "bytes", | ||
| 977 | "fnv", | ||
| 978 | "futures-core", | ||
| 979 | "futures-sink", | ||
| 980 | "futures-util", | ||
| 981 | "http", | ||
| 982 | "indexmap 1.9.3", | ||
| 983 | "slab", | ||
| 984 | "tokio", | ||
| 985 | "tokio-util", | ||
| 986 | "tracing", | ||
| 406 | ] | 987 | ] |
| 407 | 988 | ||
| 408 | [[package]] | 989 | [[package]] |
| 409 | name = "hashbrown" | 990 | name = "hashbrown" |
| 991 | version = "0.12.3" | ||
| 992 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 993 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | ||
| 994 | |||
| 995 | [[package]] | ||
| 996 | name = "hashbrown" | ||
| 410 | version = "0.14.0" | 997 | version = "0.14.0" |
| 411 | source = "registry+https://github.com/rust-lang/crates.io-index" | 998 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 412 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" | 999 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" |
| @@ -418,6 +1005,183 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 418 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" | 1005 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" |
| 419 | 1006 | ||
| 420 | [[package]] | 1007 | [[package]] |
| 1008 | name = "hermit-abi" | ||
| 1009 | version = "0.3.2" | ||
| 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1011 | checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" | ||
| 1012 | |||
| 1013 | [[package]] | ||
| 1014 | name = "hex" | ||
| 1015 | version = "0.4.3" | ||
| 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1017 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||
| 1018 | |||
| 1019 | [[package]] | ||
| 1020 | name = "hex_lit" | ||
| 1021 | version = "0.1.1" | ||
| 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1023 | checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" | ||
| 1024 | |||
| 1025 | [[package]] | ||
| 1026 | name = "hkdf" | ||
| 1027 | version = "0.12.3" | ||
| 1028 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1029 | checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" | ||
| 1030 | dependencies = [ | ||
| 1031 | "hmac", | ||
| 1032 | ] | ||
| 1033 | |||
| 1034 | [[package]] | ||
| 1035 | name = "hmac" | ||
| 1036 | version = "0.12.1" | ||
| 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1038 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" | ||
| 1039 | dependencies = [ | ||
| 1040 | "digest", | ||
| 1041 | ] | ||
| 1042 | |||
| 1043 | [[package]] | ||
| 1044 | name = "http" | ||
| 1045 | version = "0.2.9" | ||
| 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1047 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" | ||
| 1048 | dependencies = [ | ||
| 1049 | "bytes", | ||
| 1050 | "fnv", | ||
| 1051 | "itoa", | ||
| 1052 | ] | ||
| 1053 | |||
| 1054 | [[package]] | ||
| 1055 | name = "http-body" | ||
| 1056 | version = "0.4.5" | ||
| 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1058 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" | ||
| 1059 | dependencies = [ | ||
| 1060 | "bytes", | ||
| 1061 | "http", | ||
| 1062 | "pin-project-lite", | ||
| 1063 | ] | ||
| 1064 | |||
| 1065 | [[package]] | ||
| 1066 | name = "httparse" | ||
| 1067 | version = "1.8.0" | ||
| 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1069 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" | ||
| 1070 | |||
| 1071 | [[package]] | ||
| 1072 | name = "httpdate" | ||
| 1073 | version = "1.0.3" | ||
| 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1075 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | ||
| 1076 | |||
| 1077 | [[package]] | ||
| 1078 | name = "hyper" | ||
| 1079 | version = "0.14.27" | ||
| 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1081 | checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" | ||
| 1082 | dependencies = [ | ||
| 1083 | "bytes", | ||
| 1084 | "futures-channel", | ||
| 1085 | "futures-core", | ||
| 1086 | "futures-util", | ||
| 1087 | "h2", | ||
| 1088 | "http", | ||
| 1089 | "http-body", | ||
| 1090 | "httparse", | ||
| 1091 | "httpdate", | ||
| 1092 | "itoa", | ||
| 1093 | "pin-project-lite", | ||
| 1094 | "socket2 0.4.9", | ||
| 1095 | "tokio", | ||
| 1096 | "tower-service", | ||
| 1097 | "tracing", | ||
| 1098 | "want", | ||
| 1099 | ] | ||
| 1100 | |||
| 1101 | [[package]] | ||
| 1102 | name = "hyper-rustls" | ||
| 1103 | version = "0.24.1" | ||
| 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1105 | checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" | ||
| 1106 | dependencies = [ | ||
| 1107 | "futures-util", | ||
| 1108 | "http", | ||
| 1109 | "hyper", | ||
| 1110 | "rustls", | ||
| 1111 | "tokio", | ||
| 1112 | "tokio-rustls", | ||
| 1113 | ] | ||
| 1114 | |||
| 1115 | [[package]] | ||
| 1116 | name = "idna" | ||
| 1117 | version = "0.4.0" | ||
| 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1119 | checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" | ||
| 1120 | dependencies = [ | ||
| 1121 | "unicode-bidi", | ||
| 1122 | "unicode-normalization", | ||
| 1123 | ] | ||
| 1124 | |||
| 1125 | [[package]] | ||
| 1126 | name = "indexmap" | ||
| 1127 | version = "1.9.3" | ||
| 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1129 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" | ||
| 1130 | dependencies = [ | ||
| 1131 | "autocfg", | ||
| 1132 | "hashbrown 0.12.3", | ||
| 1133 | ] | ||
| 1134 | |||
| 1135 | [[package]] | ||
| 1136 | name = "indexmap" | ||
| 1137 | version = "2.0.0" | ||
| 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1139 | checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" | ||
| 1140 | dependencies = [ | ||
| 1141 | "equivalent", | ||
| 1142 | "hashbrown 0.14.0", | ||
| 1143 | ] | ||
| 1144 | |||
| 1145 | [[package]] | ||
| 1146 | name = "inout" | ||
| 1147 | version = "0.1.3" | ||
| 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1149 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" | ||
| 1150 | dependencies = [ | ||
| 1151 | "block-padding 0.3.3", | ||
| 1152 | "generic-array", | ||
| 1153 | ] | ||
| 1154 | |||
| 1155 | [[package]] | ||
| 1156 | name = "instant" | ||
| 1157 | version = "0.1.12" | ||
| 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1159 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" | ||
| 1160 | dependencies = [ | ||
| 1161 | "cfg-if", | ||
| 1162 | "js-sys", | ||
| 1163 | "wasm-bindgen", | ||
| 1164 | "web-sys", | ||
| 1165 | ] | ||
| 1166 | |||
| 1167 | [[package]] | ||
| 1168 | name = "io-lifetimes" | ||
| 1169 | version = "1.0.11" | ||
| 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1171 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" | ||
| 1172 | dependencies = [ | ||
| 1173 | "hermit-abi", | ||
| 1174 | "libc", | ||
| 1175 | "windows-sys 0.48.0", | ||
| 1176 | ] | ||
| 1177 | |||
| 1178 | [[package]] | ||
| 1179 | name = "ipnet" | ||
| 1180 | version = "2.8.0" | ||
| 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1182 | checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" | ||
| 1183 | |||
| 1184 | [[package]] | ||
| 421 | name = "itertools" | 1185 | name = "itertools" |
| 422 | version = "0.10.5" | 1186 | version = "0.10.5" |
| 423 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -433,6 +1197,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 433 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" | 1197 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" |
| 434 | 1198 | ||
| 435 | [[package]] | 1199 | [[package]] |
| 1200 | name = "js-sys" | ||
| 1201 | version = "0.3.64" | ||
| 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1203 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" | ||
| 1204 | dependencies = [ | ||
| 1205 | "wasm-bindgen", | ||
| 1206 | ] | ||
| 1207 | |||
| 1208 | [[package]] | ||
| 1209 | name = "keyring" | ||
| 1210 | version = "2.0.5" | ||
| 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1212 | checksum = "9549a129bd08149e0a71b2d1ce2729780d47127991bfd0a78cc1df697ec72492" | ||
| 1213 | dependencies = [ | ||
| 1214 | "byteorder", | ||
| 1215 | "lazy_static", | ||
| 1216 | "linux-keyutils", | ||
| 1217 | "secret-service", | ||
| 1218 | "security-framework", | ||
| 1219 | "winapi", | ||
| 1220 | ] | ||
| 1221 | |||
| 1222 | [[package]] | ||
| 436 | name = "lazy_static" | 1223 | name = "lazy_static" |
| 437 | version = "1.4.0" | 1224 | version = "1.4.0" |
| 438 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -445,6 +1232,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 445 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" | 1232 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" |
| 446 | 1233 | ||
| 447 | [[package]] | 1234 | [[package]] |
| 1235 | name = "linux-keyutils" | ||
| 1236 | version = "0.2.3" | ||
| 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1238 | checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" | ||
| 1239 | dependencies = [ | ||
| 1240 | "bitflags 1.3.2", | ||
| 1241 | "libc", | ||
| 1242 | ] | ||
| 1243 | |||
| 1244 | [[package]] | ||
| 1245 | name = "linux-raw-sys" | ||
| 1246 | version = "0.3.8" | ||
| 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1248 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" | ||
| 1249 | |||
| 1250 | [[package]] | ||
| 448 | name = "linux-raw-sys" | 1251 | name = "linux-raw-sys" |
| 449 | version = "0.4.7" | 1252 | version = "0.4.7" |
| 450 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -474,6 +1277,15 @@ checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" | |||
| 474 | 1277 | ||
| 475 | [[package]] | 1278 | [[package]] |
| 476 | name = "memoffset" | 1279 | name = "memoffset" |
| 1280 | version = "0.6.5" | ||
| 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1282 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" | ||
| 1283 | dependencies = [ | ||
| 1284 | "autocfg", | ||
| 1285 | ] | ||
| 1286 | |||
| 1287 | [[package]] | ||
| 1288 | name = "memoffset" | ||
| 477 | version = "0.7.1" | 1289 | version = "0.7.1" |
| 478 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 479 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" | 1291 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" |
| @@ -482,6 +1294,32 @@ dependencies = [ | |||
| 482 | ] | 1294 | ] |
| 483 | 1295 | ||
| 484 | [[package]] | 1296 | [[package]] |
| 1297 | name = "mime" | ||
| 1298 | version = "0.3.17" | ||
| 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1300 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" | ||
| 1301 | |||
| 1302 | [[package]] | ||
| 1303 | name = "miniz_oxide" | ||
| 1304 | version = "0.7.1" | ||
| 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1306 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" | ||
| 1307 | dependencies = [ | ||
| 1308 | "adler", | ||
| 1309 | ] | ||
| 1310 | |||
| 1311 | [[package]] | ||
| 1312 | name = "mio" | ||
| 1313 | version = "0.8.8" | ||
| 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1315 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" | ||
| 1316 | dependencies = [ | ||
| 1317 | "libc", | ||
| 1318 | "wasi", | ||
| 1319 | "windows-sys 0.48.0", | ||
| 1320 | ] | ||
| 1321 | |||
| 1322 | [[package]] | ||
| 485 | name = "mockall" | 1323 | name = "mockall" |
| 486 | version = "0.11.4" | 1324 | version = "0.11.4" |
| 487 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -514,15 +1352,37 @@ version = "0.0.1" | |||
| 514 | dependencies = [ | 1352 | dependencies = [ |
| 515 | "anyhow", | 1353 | "anyhow", |
| 516 | "assert_cmd", | 1354 | "assert_cmd", |
| 1355 | "chacha20poly1305", | ||
| 517 | "clap", | 1356 | "clap", |
| 518 | "dialoguer", | 1357 | "dialoguer", |
| 519 | "directories", | 1358 | "directories", |
| 520 | "duplicate", | 1359 | "duplicate", |
| 1360 | "keyring", | ||
| 521 | "mockall", | 1361 | "mockall", |
| 1362 | "nostr", | ||
| 1363 | "once_cell", | ||
| 1364 | "passwords", | ||
| 1365 | "rexpect 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| 1366 | "scrypt", | ||
| 522 | "serde", | 1367 | "serde", |
| 523 | "serde_json", | 1368 | "serde_json", |
| 524 | "serial_test", | 1369 | "serial_test", |
| 525 | "test_utils", | 1370 | "test_utils", |
| 1371 | "zeroize", | ||
| 1372 | ] | ||
| 1373 | |||
| 1374 | [[package]] | ||
| 1375 | name = "nix" | ||
| 1376 | version = "0.25.1" | ||
| 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1378 | checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" | ||
| 1379 | dependencies = [ | ||
| 1380 | "autocfg", | ||
| 1381 | "bitflags 1.3.2", | ||
| 1382 | "cfg-if", | ||
| 1383 | "libc", | ||
| 1384 | "memoffset 0.6.5", | ||
| 1385 | "pin-utils", | ||
| 526 | ] | 1386 | ] |
| 527 | 1387 | ||
| 528 | [[package]] | 1388 | [[package]] |
| @@ -534,7 +1394,7 @@ dependencies = [ | |||
| 534 | "bitflags 1.3.2", | 1394 | "bitflags 1.3.2", |
| 535 | "cfg-if", | 1395 | "cfg-if", |
| 536 | "libc", | 1396 | "libc", |
| 537 | "memoffset", | 1397 | "memoffset 0.7.1", |
| 538 | "pin-utils", | 1398 | "pin-utils", |
| 539 | ] | 1399 | ] |
| 540 | 1400 | ||
| @@ -545,6 +1405,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 545 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" | 1405 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" |
| 546 | 1406 | ||
| 547 | [[package]] | 1407 | [[package]] |
| 1408 | name = "nostr" | ||
| 1409 | version = "0.23.0" | ||
| 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1411 | checksum = "525a8f75106f4eeb1fedaacadc61547548fe4715c3edde7d03eed2900b467952" | ||
| 1412 | dependencies = [ | ||
| 1413 | "aes 0.8.3", | ||
| 1414 | "base64", | ||
| 1415 | "bech32", | ||
| 1416 | "bip39", | ||
| 1417 | "bitcoin", | ||
| 1418 | "bitcoin_hashes 0.12.0", | ||
| 1419 | "cbc", | ||
| 1420 | "getrandom", | ||
| 1421 | "instant", | ||
| 1422 | "reqwest", | ||
| 1423 | "secp256k1", | ||
| 1424 | "serde", | ||
| 1425 | "serde_json", | ||
| 1426 | "tracing", | ||
| 1427 | "url", | ||
| 1428 | ] | ||
| 1429 | |||
| 1430 | [[package]] | ||
| 1431 | name = "num" | ||
| 1432 | version = "0.4.1" | ||
| 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1434 | checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" | ||
| 1435 | dependencies = [ | ||
| 1436 | "num-bigint", | ||
| 1437 | "num-complex", | ||
| 1438 | "num-integer", | ||
| 1439 | "num-iter", | ||
| 1440 | "num-rational", | ||
| 1441 | "num-traits", | ||
| 1442 | ] | ||
| 1443 | |||
| 1444 | [[package]] | ||
| 1445 | name = "num-bigint" | ||
| 1446 | version = "0.4.4" | ||
| 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1448 | checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" | ||
| 1449 | dependencies = [ | ||
| 1450 | "autocfg", | ||
| 1451 | "num-integer", | ||
| 1452 | "num-traits", | ||
| 1453 | ] | ||
| 1454 | |||
| 1455 | [[package]] | ||
| 1456 | name = "num-complex" | ||
| 1457 | version = "0.4.4" | ||
| 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1459 | checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" | ||
| 1460 | dependencies = [ | ||
| 1461 | "num-traits", | ||
| 1462 | ] | ||
| 1463 | |||
| 1464 | [[package]] | ||
| 1465 | name = "num-integer" | ||
| 1466 | version = "0.1.45" | ||
| 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1468 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" | ||
| 1469 | dependencies = [ | ||
| 1470 | "autocfg", | ||
| 1471 | "num-traits", | ||
| 1472 | ] | ||
| 1473 | |||
| 1474 | [[package]] | ||
| 1475 | name = "num-iter" | ||
| 1476 | version = "0.1.43" | ||
| 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1478 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" | ||
| 1479 | dependencies = [ | ||
| 1480 | "autocfg", | ||
| 1481 | "num-integer", | ||
| 1482 | "num-traits", | ||
| 1483 | ] | ||
| 1484 | |||
| 1485 | [[package]] | ||
| 1486 | name = "num-rational" | ||
| 1487 | version = "0.4.1" | ||
| 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1489 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" | ||
| 1490 | dependencies = [ | ||
| 1491 | "autocfg", | ||
| 1492 | "num-bigint", | ||
| 1493 | "num-integer", | ||
| 1494 | "num-traits", | ||
| 1495 | ] | ||
| 1496 | |||
| 1497 | [[package]] | ||
| 548 | name = "num-traits" | 1498 | name = "num-traits" |
| 549 | version = "0.2.16" | 1499 | version = "0.2.16" |
| 550 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -554,18 +1504,49 @@ dependencies = [ | |||
| 554 | ] | 1504 | ] |
| 555 | 1505 | ||
| 556 | [[package]] | 1506 | [[package]] |
| 1507 | name = "object" | ||
| 1508 | version = "0.32.0" | ||
| 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1510 | checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" | ||
| 1511 | dependencies = [ | ||
| 1512 | "memchr", | ||
| 1513 | ] | ||
| 1514 | |||
| 1515 | [[package]] | ||
| 557 | name = "once_cell" | 1516 | name = "once_cell" |
| 558 | version = "1.18.0" | 1517 | version = "1.18.0" |
| 559 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 560 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" | 1519 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" |
| 561 | 1520 | ||
| 562 | [[package]] | 1521 | [[package]] |
| 1522 | name = "opaque-debug" | ||
| 1523 | version = "0.3.0" | ||
| 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1525 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" | ||
| 1526 | |||
| 1527 | [[package]] | ||
| 563 | name = "option-ext" | 1528 | name = "option-ext" |
| 564 | version = "0.2.0" | 1529 | version = "0.2.0" |
| 565 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 566 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" | 1531 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" |
| 567 | 1532 | ||
| 568 | [[package]] | 1533 | [[package]] |
| 1534 | name = "ordered-stream" | ||
| 1535 | version = "0.2.0" | ||
| 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1537 | checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" | ||
| 1538 | dependencies = [ | ||
| 1539 | "futures-core", | ||
| 1540 | "pin-project-lite", | ||
| 1541 | ] | ||
| 1542 | |||
| 1543 | [[package]] | ||
| 1544 | name = "parking" | ||
| 1545 | version = "2.1.0" | ||
| 1546 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1547 | checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" | ||
| 1548 | |||
| 1549 | [[package]] | ||
| 569 | name = "parking_lot" | 1550 | name = "parking_lot" |
| 570 | version = "0.12.1" | 1551 | version = "0.12.1" |
| 571 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -589,6 +1570,42 @@ dependencies = [ | |||
| 589 | ] | 1570 | ] |
| 590 | 1571 | ||
| 591 | [[package]] | 1572 | [[package]] |
| 1573 | name = "password-hash" | ||
| 1574 | version = "0.5.0" | ||
| 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1576 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" | ||
| 1577 | dependencies = [ | ||
| 1578 | "base64ct", | ||
| 1579 | "rand_core", | ||
| 1580 | "subtle", | ||
| 1581 | ] | ||
| 1582 | |||
| 1583 | [[package]] | ||
| 1584 | name = "passwords" | ||
| 1585 | version = "3.1.13" | ||
| 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1587 | checksum = "9ca743e019c2c679e1d92b329214cb4ffc4312200ca5ae60227aad1425c0aac8" | ||
| 1588 | dependencies = [ | ||
| 1589 | "random-pick", | ||
| 1590 | ] | ||
| 1591 | |||
| 1592 | [[package]] | ||
| 1593 | name = "pbkdf2" | ||
| 1594 | version = "0.12.2" | ||
| 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1596 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" | ||
| 1597 | dependencies = [ | ||
| 1598 | "digest", | ||
| 1599 | "hmac", | ||
| 1600 | ] | ||
| 1601 | |||
| 1602 | [[package]] | ||
| 1603 | name = "percent-encoding" | ||
| 1604 | version = "2.3.0" | ||
| 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1606 | checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" | ||
| 1607 | |||
| 1608 | [[package]] | ||
| 592 | name = "pin-project-lite" | 1609 | name = "pin-project-lite" |
| 593 | version = "0.2.13" | 1610 | version = "0.2.13" |
| 594 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -601,6 +1618,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 601 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" | 1618 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" |
| 602 | 1619 | ||
| 603 | [[package]] | 1620 | [[package]] |
| 1621 | name = "polling" | ||
| 1622 | version = "2.8.0" | ||
| 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1624 | checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" | ||
| 1625 | dependencies = [ | ||
| 1626 | "autocfg", | ||
| 1627 | "bitflags 1.3.2", | ||
| 1628 | "cfg-if", | ||
| 1629 | "concurrent-queue", | ||
| 1630 | "libc", | ||
| 1631 | "log", | ||
| 1632 | "pin-project-lite", | ||
| 1633 | "windows-sys 0.48.0", | ||
| 1634 | ] | ||
| 1635 | |||
| 1636 | [[package]] | ||
| 1637 | name = "poly1305" | ||
| 1638 | version = "0.8.0" | ||
| 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1640 | checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" | ||
| 1641 | dependencies = [ | ||
| 1642 | "cpufeatures", | ||
| 1643 | "opaque-debug", | ||
| 1644 | "universal-hash", | ||
| 1645 | ] | ||
| 1646 | |||
| 1647 | [[package]] | ||
| 1648 | name = "ppv-lite86" | ||
| 1649 | version = "0.2.17" | ||
| 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1651 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | ||
| 1652 | |||
| 1653 | [[package]] | ||
| 604 | name = "predicates" | 1654 | name = "predicates" |
| 605 | version = "2.1.5" | 1655 | version = "2.1.5" |
| 606 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -643,6 +1693,16 @@ dependencies = [ | |||
| 643 | ] | 1693 | ] |
| 644 | 1694 | ||
| 645 | [[package]] | 1695 | [[package]] |
| 1696 | name = "proc-macro-crate" | ||
| 1697 | version = "1.3.1" | ||
| 1698 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1699 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" | ||
| 1700 | dependencies = [ | ||
| 1701 | "once_cell", | ||
| 1702 | "toml_edit", | ||
| 1703 | ] | ||
| 1704 | |||
| 1705 | [[package]] | ||
| 646 | name = "proc-macro-error" | 1706 | name = "proc-macro-error" |
| 647 | version = "1.0.4" | 1707 | version = "1.0.4" |
| 648 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -667,6 +1727,12 @@ dependencies = [ | |||
| 667 | ] | 1727 | ] |
| 668 | 1728 | ||
| 669 | [[package]] | 1729 | [[package]] |
| 1730 | name = "proc-macro-hack" | ||
| 1731 | version = "0.5.20+deprecated" | ||
| 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1733 | checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" | ||
| 1734 | |||
| 1735 | [[package]] | ||
| 670 | name = "proc-macro2" | 1736 | name = "proc-macro2" |
| 671 | version = "1.0.66" | 1737 | version = "1.0.66" |
| 672 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -685,6 +1751,67 @@ dependencies = [ | |||
| 685 | ] | 1751 | ] |
| 686 | 1752 | ||
| 687 | [[package]] | 1753 | [[package]] |
| 1754 | name = "rand" | ||
| 1755 | version = "0.8.5" | ||
| 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1757 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" | ||
| 1758 | dependencies = [ | ||
| 1759 | "libc", | ||
| 1760 | "rand_chacha", | ||
| 1761 | "rand_core", | ||
| 1762 | ] | ||
| 1763 | |||
| 1764 | [[package]] | ||
| 1765 | name = "rand_chacha" | ||
| 1766 | version = "0.3.1" | ||
| 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1768 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | ||
| 1769 | dependencies = [ | ||
| 1770 | "ppv-lite86", | ||
| 1771 | "rand_core", | ||
| 1772 | ] | ||
| 1773 | |||
| 1774 | [[package]] | ||
| 1775 | name = "rand_core" | ||
| 1776 | version = "0.6.4" | ||
| 1777 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1778 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" | ||
| 1779 | dependencies = [ | ||
| 1780 | "getrandom", | ||
| 1781 | ] | ||
| 1782 | |||
| 1783 | [[package]] | ||
| 1784 | name = "random-number" | ||
| 1785 | version = "0.1.8" | ||
| 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1787 | checksum = "3a3da5cbb4c27c5150c03a54a7e4745437cd90f9e329ae657c0b889a144bb7be" | ||
| 1788 | dependencies = [ | ||
| 1789 | "proc-macro-hack", | ||
| 1790 | "rand", | ||
| 1791 | "random-number-macro-impl", | ||
| 1792 | ] | ||
| 1793 | |||
| 1794 | [[package]] | ||
| 1795 | name = "random-number-macro-impl" | ||
| 1796 | version = "0.1.7" | ||
| 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1798 | checksum = "8b86292cf41ccfc96c5de7165c1c53d5b4ac540c5bab9d1857acbe9eba5f1a0b" | ||
| 1799 | dependencies = [ | ||
| 1800 | "proc-macro-hack", | ||
| 1801 | "quote", | ||
| 1802 | "syn 2.0.32", | ||
| 1803 | ] | ||
| 1804 | |||
| 1805 | [[package]] | ||
| 1806 | name = "random-pick" | ||
| 1807 | version = "1.2.16" | ||
| 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1809 | checksum = "c179499072da789afe44127d5f4aa6012de2c2f96ef759990196b37387a2a0f8" | ||
| 1810 | dependencies = [ | ||
| 1811 | "random-number", | ||
| 1812 | ] | ||
| 1813 | |||
| 1814 | [[package]] | ||
| 688 | name = "redox_syscall" | 1815 | name = "redox_syscall" |
| 689 | version = "0.2.16" | 1816 | version = "0.2.16" |
| 690 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1817 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -743,18 +1870,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 743 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" | 1870 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" |
| 744 | 1871 | ||
| 745 | [[package]] | 1872 | [[package]] |
| 1873 | name = "reqwest" | ||
| 1874 | version = "0.11.20" | ||
| 1875 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1876 | checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" | ||
| 1877 | dependencies = [ | ||
| 1878 | "base64", | ||
| 1879 | "bytes", | ||
| 1880 | "encoding_rs", | ||
| 1881 | "futures-core", | ||
| 1882 | "futures-util", | ||
| 1883 | "h2", | ||
| 1884 | "http", | ||
| 1885 | "http-body", | ||
| 1886 | "hyper", | ||
| 1887 | "hyper-rustls", | ||
| 1888 | "ipnet", | ||
| 1889 | "js-sys", | ||
| 1890 | "log", | ||
| 1891 | "mime", | ||
| 1892 | "once_cell", | ||
| 1893 | "percent-encoding", | ||
| 1894 | "pin-project-lite", | ||
| 1895 | "rustls", | ||
| 1896 | "rustls-pemfile", | ||
| 1897 | "serde", | ||
| 1898 | "serde_json", | ||
| 1899 | "serde_urlencoded", | ||
| 1900 | "tokio", | ||
| 1901 | "tokio-rustls", | ||
| 1902 | "tokio-socks", | ||
| 1903 | "tower-service", | ||
| 1904 | "url", | ||
| 1905 | "wasm-bindgen", | ||
| 1906 | "wasm-bindgen-futures", | ||
| 1907 | "web-sys", | ||
| 1908 | "webpki-roots", | ||
| 1909 | "winreg", | ||
| 1910 | ] | ||
| 1911 | |||
| 1912 | [[package]] | ||
| 1913 | name = "rexpect" | ||
| 1914 | version = "0.5.0" | ||
| 1915 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1916 | checksum = "01ff60778f96fb5a48adbe421d21bf6578ed58c0872d712e7e08593c195adff8" | ||
| 1917 | dependencies = [ | ||
| 1918 | "comma", | ||
| 1919 | "nix 0.25.1", | ||
| 1920 | "regex", | ||
| 1921 | "tempfile", | ||
| 1922 | "thiserror", | ||
| 1923 | ] | ||
| 1924 | |||
| 1925 | [[package]] | ||
| 746 | name = "rexpect" | 1926 | name = "rexpect" |
| 747 | version = "0.5.0" | 1927 | version = "0.5.0" |
| 748 | source = "git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes#f099dc4750e38a1c31d2ab06d7d3e0352679d85a" | 1928 | source = "git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes#f099dc4750e38a1c31d2ab06d7d3e0352679d85a" |
| 749 | dependencies = [ | 1929 | dependencies = [ |
| 750 | "comma", | 1930 | "comma", |
| 751 | "nix", | 1931 | "nix 0.26.4", |
| 752 | "regex", | 1932 | "regex", |
| 753 | "tempfile", | 1933 | "tempfile", |
| 754 | "thiserror", | 1934 | "thiserror", |
| 755 | ] | 1935 | ] |
| 756 | 1936 | ||
| 757 | [[package]] | 1937 | [[package]] |
| 1938 | name = "ring" | ||
| 1939 | version = "0.16.20" | ||
| 1940 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1941 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" | ||
| 1942 | dependencies = [ | ||
| 1943 | "cc", | ||
| 1944 | "libc", | ||
| 1945 | "once_cell", | ||
| 1946 | "spin", | ||
| 1947 | "untrusted", | ||
| 1948 | "web-sys", | ||
| 1949 | "winapi", | ||
| 1950 | ] | ||
| 1951 | |||
| 1952 | [[package]] | ||
| 1953 | name = "rustc-demangle" | ||
| 1954 | version = "0.1.23" | ||
| 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1956 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" | ||
| 1957 | |||
| 1958 | [[package]] | ||
| 1959 | name = "rustix" | ||
| 1960 | version = "0.37.23" | ||
| 1961 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1962 | checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" | ||
| 1963 | dependencies = [ | ||
| 1964 | "bitflags 1.3.2", | ||
| 1965 | "errno", | ||
| 1966 | "io-lifetimes", | ||
| 1967 | "libc", | ||
| 1968 | "linux-raw-sys 0.3.8", | ||
| 1969 | "windows-sys 0.48.0", | ||
| 1970 | ] | ||
| 1971 | |||
| 1972 | [[package]] | ||
| 758 | name = "rustix" | 1973 | name = "rustix" |
| 759 | version = "0.38.13" | 1974 | version = "0.38.13" |
| 760 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -763,23 +1978,148 @@ dependencies = [ | |||
| 763 | "bitflags 2.4.0", | 1978 | "bitflags 2.4.0", |
| 764 | "errno", | 1979 | "errno", |
| 765 | "libc", | 1980 | "libc", |
| 766 | "linux-raw-sys", | 1981 | "linux-raw-sys 0.4.7", |
| 767 | "windows-sys 0.48.0", | 1982 | "windows-sys 0.48.0", |
| 768 | ] | 1983 | ] |
| 769 | 1984 | ||
| 770 | [[package]] | 1985 | [[package]] |
| 1986 | name = "rustls" | ||
| 1987 | version = "0.21.7" | ||
| 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1989 | checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" | ||
| 1990 | dependencies = [ | ||
| 1991 | "log", | ||
| 1992 | "ring", | ||
| 1993 | "rustls-webpki", | ||
| 1994 | "sct", | ||
| 1995 | ] | ||
| 1996 | |||
| 1997 | [[package]] | ||
| 1998 | name = "rustls-pemfile" | ||
| 1999 | version = "1.0.3" | ||
| 2000 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2001 | checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" | ||
| 2002 | dependencies = [ | ||
| 2003 | "base64", | ||
| 2004 | ] | ||
| 2005 | |||
| 2006 | [[package]] | ||
| 2007 | name = "rustls-webpki" | ||
| 2008 | version = "0.101.4" | ||
| 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2010 | checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" | ||
| 2011 | dependencies = [ | ||
| 2012 | "ring", | ||
| 2013 | "untrusted", | ||
| 2014 | ] | ||
| 2015 | |||
| 2016 | [[package]] | ||
| 771 | name = "ryu" | 2017 | name = "ryu" |
| 772 | version = "1.0.15" | 2018 | version = "1.0.15" |
| 773 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2019 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 774 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" | 2020 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" |
| 775 | 2021 | ||
| 776 | [[package]] | 2022 | [[package]] |
| 2023 | name = "salsa20" | ||
| 2024 | version = "0.10.2" | ||
| 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2026 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" | ||
| 2027 | dependencies = [ | ||
| 2028 | "cipher 0.4.4", | ||
| 2029 | ] | ||
| 2030 | |||
| 2031 | [[package]] | ||
| 777 | name = "scopeguard" | 2032 | name = "scopeguard" |
| 778 | version = "1.2.0" | 2033 | version = "1.2.0" |
| 779 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 780 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | 2035 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" |
| 781 | 2036 | ||
| 782 | [[package]] | 2037 | [[package]] |
| 2038 | name = "scrypt" | ||
| 2039 | version = "0.11.0" | ||
| 2040 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2041 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" | ||
| 2042 | dependencies = [ | ||
| 2043 | "password-hash", | ||
| 2044 | "pbkdf2", | ||
| 2045 | "salsa20", | ||
| 2046 | "sha2", | ||
| 2047 | ] | ||
| 2048 | |||
| 2049 | [[package]] | ||
| 2050 | name = "sct" | ||
| 2051 | version = "0.7.0" | ||
| 2052 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2053 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" | ||
| 2054 | dependencies = [ | ||
| 2055 | "ring", | ||
| 2056 | "untrusted", | ||
| 2057 | ] | ||
| 2058 | |||
| 2059 | [[package]] | ||
| 2060 | name = "secp256k1" | ||
| 2061 | version = "0.27.0" | ||
| 2062 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2063 | checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" | ||
| 2064 | dependencies = [ | ||
| 2065 | "bitcoin_hashes 0.12.0", | ||
| 2066 | "rand", | ||
| 2067 | "secp256k1-sys", | ||
| 2068 | "serde", | ||
| 2069 | ] | ||
| 2070 | |||
| 2071 | [[package]] | ||
| 2072 | name = "secp256k1-sys" | ||
| 2073 | version = "0.8.1" | ||
| 2074 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2075 | checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" | ||
| 2076 | dependencies = [ | ||
| 2077 | "cc", | ||
| 2078 | ] | ||
| 2079 | |||
| 2080 | [[package]] | ||
| 2081 | name = "secret-service" | ||
| 2082 | version = "3.0.1" | ||
| 2083 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2084 | checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" | ||
| 2085 | dependencies = [ | ||
| 2086 | "aes 0.7.5", | ||
| 2087 | "block-modes", | ||
| 2088 | "futures-util", | ||
| 2089 | "generic-array", | ||
| 2090 | "hkdf", | ||
| 2091 | "num", | ||
| 2092 | "once_cell", | ||
| 2093 | "rand", | ||
| 2094 | "serde", | ||
| 2095 | "sha2", | ||
| 2096 | "zbus", | ||
| 2097 | ] | ||
| 2098 | |||
| 2099 | [[package]] | ||
| 2100 | name = "security-framework" | ||
| 2101 | version = "2.9.2" | ||
| 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2103 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" | ||
| 2104 | dependencies = [ | ||
| 2105 | "bitflags 1.3.2", | ||
| 2106 | "core-foundation", | ||
| 2107 | "core-foundation-sys", | ||
| 2108 | "libc", | ||
| 2109 | "security-framework-sys", | ||
| 2110 | ] | ||
| 2111 | |||
| 2112 | [[package]] | ||
| 2113 | name = "security-framework-sys" | ||
| 2114 | version = "2.9.1" | ||
| 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2116 | checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" | ||
| 2117 | dependencies = [ | ||
| 2118 | "core-foundation-sys", | ||
| 2119 | "libc", | ||
| 2120 | ] | ||
| 2121 | |||
| 2122 | [[package]] | ||
| 783 | name = "serde" | 2123 | name = "serde" |
| 784 | version = "1.0.188" | 2124 | version = "1.0.188" |
| 785 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2125 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -811,6 +2151,29 @@ dependencies = [ | |||
| 811 | ] | 2151 | ] |
| 812 | 2152 | ||
| 813 | [[package]] | 2153 | [[package]] |
| 2154 | name = "serde_repr" | ||
| 2155 | version = "0.1.16" | ||
| 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2157 | checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" | ||
| 2158 | dependencies = [ | ||
| 2159 | "proc-macro2", | ||
| 2160 | "quote", | ||
| 2161 | "syn 2.0.32", | ||
| 2162 | ] | ||
| 2163 | |||
| 2164 | [[package]] | ||
| 2165 | name = "serde_urlencoded" | ||
| 2166 | version = "0.7.1" | ||
| 2167 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2168 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" | ||
| 2169 | dependencies = [ | ||
| 2170 | "form_urlencoded", | ||
| 2171 | "itoa", | ||
| 2172 | "ryu", | ||
| 2173 | "serde", | ||
| 2174 | ] | ||
| 2175 | |||
| 2176 | [[package]] | ||
| 814 | name = "serial_test" | 2177 | name = "serial_test" |
| 815 | version = "2.0.0" | 2178 | version = "2.0.0" |
| 816 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2179 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -836,12 +2199,53 @@ dependencies = [ | |||
| 836 | ] | 2199 | ] |
| 837 | 2200 | ||
| 838 | [[package]] | 2201 | [[package]] |
| 2202 | name = "sha1" | ||
| 2203 | version = "0.10.5" | ||
| 2204 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2205 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" | ||
| 2206 | dependencies = [ | ||
| 2207 | "cfg-if", | ||
| 2208 | "cpufeatures", | ||
| 2209 | "digest", | ||
| 2210 | ] | ||
| 2211 | |||
| 2212 | [[package]] | ||
| 2213 | name = "sha2" | ||
| 2214 | version = "0.10.7" | ||
| 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2216 | checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" | ||
| 2217 | dependencies = [ | ||
| 2218 | "cfg-if", | ||
| 2219 | "cpufeatures", | ||
| 2220 | "digest", | ||
| 2221 | ] | ||
| 2222 | |||
| 2223 | [[package]] | ||
| 839 | name = "shell-words" | 2224 | name = "shell-words" |
| 840 | version = "1.1.0" | 2225 | version = "1.1.0" |
| 841 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2226 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 842 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" | 2227 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" |
| 843 | 2228 | ||
| 844 | [[package]] | 2229 | [[package]] |
| 2230 | name = "signal-hook" | ||
| 2231 | version = "0.3.17" | ||
| 2232 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2233 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" | ||
| 2234 | dependencies = [ | ||
| 2235 | "libc", | ||
| 2236 | "signal-hook-registry", | ||
| 2237 | ] | ||
| 2238 | |||
| 2239 | [[package]] | ||
| 2240 | name = "signal-hook-registry" | ||
| 2241 | version = "1.4.1" | ||
| 2242 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2243 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" | ||
| 2244 | dependencies = [ | ||
| 2245 | "libc", | ||
| 2246 | ] | ||
| 2247 | |||
| 2248 | [[package]] | ||
| 845 | name = "slab" | 2249 | name = "slab" |
| 846 | version = "0.4.9" | 2250 | version = "0.4.9" |
| 847 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2251 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -857,6 +2261,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 857 | checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" | 2261 | checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" |
| 858 | 2262 | ||
| 859 | [[package]] | 2263 | [[package]] |
| 2264 | name = "socket2" | ||
| 2265 | version = "0.4.9" | ||
| 2266 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2267 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" | ||
| 2268 | dependencies = [ | ||
| 2269 | "libc", | ||
| 2270 | "winapi", | ||
| 2271 | ] | ||
| 2272 | |||
| 2273 | [[package]] | ||
| 2274 | name = "socket2" | ||
| 2275 | version = "0.5.3" | ||
| 2276 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2277 | checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" | ||
| 2278 | dependencies = [ | ||
| 2279 | "libc", | ||
| 2280 | "windows-sys 0.48.0", | ||
| 2281 | ] | ||
| 2282 | |||
| 2283 | [[package]] | ||
| 2284 | name = "spin" | ||
| 2285 | version = "0.5.2" | ||
| 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2287 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||
| 2288 | |||
| 2289 | [[package]] | ||
| 2290 | name = "static_assertions" | ||
| 2291 | version = "1.1.0" | ||
| 2292 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2293 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||
| 2294 | |||
| 2295 | [[package]] | ||
| 860 | name = "strip-ansi-escapes" | 2296 | name = "strip-ansi-escapes" |
| 861 | version = "0.2.0" | 2297 | version = "0.2.0" |
| 862 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2298 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -872,6 +2308,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 872 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | 2308 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" |
| 873 | 2309 | ||
| 874 | [[package]] | 2310 | [[package]] |
| 2311 | name = "subtle" | ||
| 2312 | version = "2.5.0" | ||
| 2313 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2314 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" | ||
| 2315 | |||
| 2316 | [[package]] | ||
| 875 | name = "syn" | 2317 | name = "syn" |
| 876 | version = "1.0.109" | 2318 | version = "1.0.109" |
| 877 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2319 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -900,9 +2342,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 900 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" | 2342 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" |
| 901 | dependencies = [ | 2343 | dependencies = [ |
| 902 | "cfg-if", | 2344 | "cfg-if", |
| 903 | "fastrand", | 2345 | "fastrand 2.0.0", |
| 904 | "redox_syscall 0.3.5", | 2346 | "redox_syscall 0.3.5", |
| 905 | "rustix", | 2347 | "rustix 0.38.13", |
| 906 | "windows-sys 0.48.0", | 2348 | "windows-sys 0.48.0", |
| 907 | ] | 2349 | ] |
| 908 | 2350 | ||
| @@ -920,7 +2362,9 @@ dependencies = [ | |||
| 920 | "assert_cmd", | 2362 | "assert_cmd", |
| 921 | "dialoguer", | 2363 | "dialoguer", |
| 922 | "directories", | 2364 | "directories", |
| 923 | "rexpect", | 2365 | "nostr", |
| 2366 | "once_cell", | ||
| 2367 | "rexpect 0.5.0 (git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes)", | ||
| 924 | "strip-ansi-escapes", | 2368 | "strip-ansi-escapes", |
| 925 | ] | 2369 | ] |
| 926 | 2370 | ||
| @@ -945,18 +2389,204 @@ dependencies = [ | |||
| 945 | ] | 2389 | ] |
| 946 | 2390 | ||
| 947 | [[package]] | 2391 | [[package]] |
| 2392 | name = "tinyvec" | ||
| 2393 | version = "1.6.0" | ||
| 2394 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2395 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" | ||
| 2396 | dependencies = [ | ||
| 2397 | "tinyvec_macros", | ||
| 2398 | ] | ||
| 2399 | |||
| 2400 | [[package]] | ||
| 2401 | name = "tinyvec_macros" | ||
| 2402 | version = "0.1.1" | ||
| 2403 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2404 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" | ||
| 2405 | |||
| 2406 | [[package]] | ||
| 2407 | name = "tokio" | ||
| 2408 | version = "1.32.0" | ||
| 2409 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2410 | checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" | ||
| 2411 | dependencies = [ | ||
| 2412 | "backtrace", | ||
| 2413 | "bytes", | ||
| 2414 | "libc", | ||
| 2415 | "mio", | ||
| 2416 | "pin-project-lite", | ||
| 2417 | "socket2 0.5.3", | ||
| 2418 | "windows-sys 0.48.0", | ||
| 2419 | ] | ||
| 2420 | |||
| 2421 | [[package]] | ||
| 2422 | name = "tokio-rustls" | ||
| 2423 | version = "0.24.1" | ||
| 2424 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2425 | checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" | ||
| 2426 | dependencies = [ | ||
| 2427 | "rustls", | ||
| 2428 | "tokio", | ||
| 2429 | ] | ||
| 2430 | |||
| 2431 | [[package]] | ||
| 2432 | name = "tokio-socks" | ||
| 2433 | version = "0.5.1" | ||
| 2434 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2435 | checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" | ||
| 2436 | dependencies = [ | ||
| 2437 | "either", | ||
| 2438 | "futures-util", | ||
| 2439 | "thiserror", | ||
| 2440 | "tokio", | ||
| 2441 | ] | ||
| 2442 | |||
| 2443 | [[package]] | ||
| 2444 | name = "tokio-util" | ||
| 2445 | version = "0.7.8" | ||
| 2446 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2447 | checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" | ||
| 2448 | dependencies = [ | ||
| 2449 | "bytes", | ||
| 2450 | "futures-core", | ||
| 2451 | "futures-sink", | ||
| 2452 | "pin-project-lite", | ||
| 2453 | "tokio", | ||
| 2454 | "tracing", | ||
| 2455 | ] | ||
| 2456 | |||
| 2457 | [[package]] | ||
| 2458 | name = "toml_datetime" | ||
| 2459 | version = "0.6.3" | ||
| 2460 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2461 | checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" | ||
| 2462 | |||
| 2463 | [[package]] | ||
| 2464 | name = "toml_edit" | ||
| 2465 | version = "0.19.14" | ||
| 2466 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2467 | checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" | ||
| 2468 | dependencies = [ | ||
| 2469 | "indexmap 2.0.0", | ||
| 2470 | "toml_datetime", | ||
| 2471 | "winnow", | ||
| 2472 | ] | ||
| 2473 | |||
| 2474 | [[package]] | ||
| 2475 | name = "tower-service" | ||
| 2476 | version = "0.3.2" | ||
| 2477 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2478 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" | ||
| 2479 | |||
| 2480 | [[package]] | ||
| 2481 | name = "tracing" | ||
| 2482 | version = "0.1.37" | ||
| 2483 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2484 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" | ||
| 2485 | dependencies = [ | ||
| 2486 | "cfg-if", | ||
| 2487 | "pin-project-lite", | ||
| 2488 | "tracing-attributes", | ||
| 2489 | "tracing-core", | ||
| 2490 | ] | ||
| 2491 | |||
| 2492 | [[package]] | ||
| 2493 | name = "tracing-attributes" | ||
| 2494 | version = "0.1.26" | ||
| 2495 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2496 | checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" | ||
| 2497 | dependencies = [ | ||
| 2498 | "proc-macro2", | ||
| 2499 | "quote", | ||
| 2500 | "syn 2.0.32", | ||
| 2501 | ] | ||
| 2502 | |||
| 2503 | [[package]] | ||
| 2504 | name = "tracing-core" | ||
| 2505 | version = "0.1.31" | ||
| 2506 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2507 | checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" | ||
| 2508 | dependencies = [ | ||
| 2509 | "once_cell", | ||
| 2510 | ] | ||
| 2511 | |||
| 2512 | [[package]] | ||
| 2513 | name = "try-lock" | ||
| 2514 | version = "0.2.4" | ||
| 2515 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2516 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" | ||
| 2517 | |||
| 2518 | [[package]] | ||
| 2519 | name = "typenum" | ||
| 2520 | version = "1.16.0" | ||
| 2521 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2522 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" | ||
| 2523 | |||
| 2524 | [[package]] | ||
| 2525 | name = "uds_windows" | ||
| 2526 | version = "1.0.2" | ||
| 2527 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2528 | checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" | ||
| 2529 | dependencies = [ | ||
| 2530 | "tempfile", | ||
| 2531 | "winapi", | ||
| 2532 | ] | ||
| 2533 | |||
| 2534 | [[package]] | ||
| 2535 | name = "unicode-bidi" | ||
| 2536 | version = "0.3.13" | ||
| 2537 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2538 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" | ||
| 2539 | |||
| 2540 | [[package]] | ||
| 948 | name = "unicode-ident" | 2541 | name = "unicode-ident" |
| 949 | version = "1.0.11" | 2542 | version = "1.0.11" |
| 950 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2543 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 951 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" | 2544 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" |
| 952 | 2545 | ||
| 953 | [[package]] | 2546 | [[package]] |
| 2547 | name = "unicode-normalization" | ||
| 2548 | version = "0.1.22" | ||
| 2549 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2550 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" | ||
| 2551 | dependencies = [ | ||
| 2552 | "tinyvec", | ||
| 2553 | ] | ||
| 2554 | |||
| 2555 | [[package]] | ||
| 954 | name = "unicode-width" | 2556 | name = "unicode-width" |
| 955 | version = "0.1.10" | 2557 | version = "0.1.10" |
| 956 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2558 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 957 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" | 2559 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" |
| 958 | 2560 | ||
| 959 | [[package]] | 2561 | [[package]] |
| 2562 | name = "universal-hash" | ||
| 2563 | version = "0.5.1" | ||
| 2564 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2565 | checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" | ||
| 2566 | dependencies = [ | ||
| 2567 | "crypto-common", | ||
| 2568 | "subtle", | ||
| 2569 | ] | ||
| 2570 | |||
| 2571 | [[package]] | ||
| 2572 | name = "untrusted" | ||
| 2573 | version = "0.7.1" | ||
| 2574 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2575 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" | ||
| 2576 | |||
| 2577 | [[package]] | ||
| 2578 | name = "url" | ||
| 2579 | version = "2.4.1" | ||
| 2580 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2581 | checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" | ||
| 2582 | dependencies = [ | ||
| 2583 | "form_urlencoded", | ||
| 2584 | "idna", | ||
| 2585 | "percent-encoding", | ||
| 2586 | "serde", | ||
| 2587 | ] | ||
| 2588 | |||
| 2589 | [[package]] | ||
| 960 | name = "utf8parse" | 2590 | name = "utf8parse" |
| 961 | version = "0.2.1" | 2591 | version = "0.2.1" |
| 962 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2592 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -998,12 +2628,131 @@ dependencies = [ | |||
| 998 | ] | 2628 | ] |
| 999 | 2629 | ||
| 1000 | [[package]] | 2630 | [[package]] |
| 2631 | name = "waker-fn" | ||
| 2632 | version = "1.1.0" | ||
| 2633 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2634 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" | ||
| 2635 | |||
| 2636 | [[package]] | ||
| 2637 | name = "want" | ||
| 2638 | version = "0.3.1" | ||
| 2639 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2640 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" | ||
| 2641 | dependencies = [ | ||
| 2642 | "try-lock", | ||
| 2643 | ] | ||
| 2644 | |||
| 2645 | [[package]] | ||
| 1001 | name = "wasi" | 2646 | name = "wasi" |
| 1002 | version = "0.11.0+wasi-snapshot-preview1" | 2647 | version = "0.11.0+wasi-snapshot-preview1" |
| 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2648 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 1004 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | 2649 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" |
| 1005 | 2650 | ||
| 1006 | [[package]] | 2651 | [[package]] |
| 2652 | name = "wasm-bindgen" | ||
| 2653 | version = "0.2.87" | ||
| 2654 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2655 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" | ||
| 2656 | dependencies = [ | ||
| 2657 | "cfg-if", | ||
| 2658 | "wasm-bindgen-macro", | ||
| 2659 | ] | ||
| 2660 | |||
| 2661 | [[package]] | ||
| 2662 | name = "wasm-bindgen-backend" | ||
| 2663 | version = "0.2.87" | ||
| 2664 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2665 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" | ||
| 2666 | dependencies = [ | ||
| 2667 | "bumpalo", | ||
| 2668 | "log", | ||
| 2669 | "once_cell", | ||
| 2670 | "proc-macro2", | ||
| 2671 | "quote", | ||
| 2672 | "syn 2.0.32", | ||
| 2673 | "wasm-bindgen-shared", | ||
| 2674 | ] | ||
| 2675 | |||
| 2676 | [[package]] | ||
| 2677 | name = "wasm-bindgen-futures" | ||
| 2678 | version = "0.4.37" | ||
| 2679 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2680 | checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" | ||
| 2681 | dependencies = [ | ||
| 2682 | "cfg-if", | ||
| 2683 | "js-sys", | ||
| 2684 | "wasm-bindgen", | ||
| 2685 | "web-sys", | ||
| 2686 | ] | ||
| 2687 | |||
| 2688 | [[package]] | ||
| 2689 | name = "wasm-bindgen-macro" | ||
| 2690 | version = "0.2.87" | ||
| 2691 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2692 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" | ||
| 2693 | dependencies = [ | ||
| 2694 | "quote", | ||
| 2695 | "wasm-bindgen-macro-support", | ||
| 2696 | ] | ||
| 2697 | |||
| 2698 | [[package]] | ||
| 2699 | name = "wasm-bindgen-macro-support" | ||
| 2700 | version = "0.2.87" | ||
| 2701 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2702 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" | ||
| 2703 | dependencies = [ | ||
| 2704 | "proc-macro2", | ||
| 2705 | "quote", | ||
| 2706 | "syn 2.0.32", | ||
| 2707 | "wasm-bindgen-backend", | ||
| 2708 | "wasm-bindgen-shared", | ||
| 2709 | ] | ||
| 2710 | |||
| 2711 | [[package]] | ||
| 2712 | name = "wasm-bindgen-shared" | ||
| 2713 | version = "0.2.87" | ||
| 2714 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2715 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" | ||
| 2716 | |||
| 2717 | [[package]] | ||
| 2718 | name = "web-sys" | ||
| 2719 | version = "0.3.64" | ||
| 2720 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2721 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" | ||
| 2722 | dependencies = [ | ||
| 2723 | "js-sys", | ||
| 2724 | "wasm-bindgen", | ||
| 2725 | ] | ||
| 2726 | |||
| 2727 | [[package]] | ||
| 2728 | name = "webpki-roots" | ||
| 2729 | version = "0.25.2" | ||
| 2730 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2731 | checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" | ||
| 2732 | |||
| 2733 | [[package]] | ||
| 2734 | name = "winapi" | ||
| 2735 | version = "0.3.9" | ||
| 2736 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2737 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
| 2738 | dependencies = [ | ||
| 2739 | "winapi-i686-pc-windows-gnu", | ||
| 2740 | "winapi-x86_64-pc-windows-gnu", | ||
| 2741 | ] | ||
| 2742 | |||
| 2743 | [[package]] | ||
| 2744 | name = "winapi-i686-pc-windows-gnu" | ||
| 2745 | version = "0.4.0" | ||
| 2746 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2747 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
| 2748 | |||
| 2749 | [[package]] | ||
| 2750 | name = "winapi-x86_64-pc-windows-gnu" | ||
| 2751 | version = "0.4.0" | ||
| 2752 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2753 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
| 2754 | |||
| 2755 | [[package]] | ||
| 1007 | name = "windows-sys" | 2756 | name = "windows-sys" |
| 1008 | version = "0.45.0" | 2757 | version = "0.45.0" |
| 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2758 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -1136,7 +2885,140 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 1136 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | 2885 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" |
| 1137 | 2886 | ||
| 1138 | [[package]] | 2887 | [[package]] |
| 2888 | name = "winnow" | ||
| 2889 | version = "0.5.15" | ||
| 2890 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2891 | checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" | ||
| 2892 | dependencies = [ | ||
| 2893 | "memchr", | ||
| 2894 | ] | ||
| 2895 | |||
| 2896 | [[package]] | ||
| 2897 | name = "winreg" | ||
| 2898 | version = "0.50.0" | ||
| 2899 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2900 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" | ||
| 2901 | dependencies = [ | ||
| 2902 | "cfg-if", | ||
| 2903 | "windows-sys 0.48.0", | ||
| 2904 | ] | ||
| 2905 | |||
| 2906 | [[package]] | ||
| 2907 | name = "xdg-home" | ||
| 2908 | version = "1.0.0" | ||
| 2909 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2910 | checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" | ||
| 2911 | dependencies = [ | ||
| 2912 | "nix 0.26.4", | ||
| 2913 | "winapi", | ||
| 2914 | ] | ||
| 2915 | |||
| 2916 | [[package]] | ||
| 2917 | name = "zbus" | ||
| 2918 | version = "3.14.1" | ||
| 2919 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2920 | checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" | ||
| 2921 | dependencies = [ | ||
| 2922 | "async-broadcast", | ||
| 2923 | "async-executor", | ||
| 2924 | "async-fs", | ||
| 2925 | "async-io", | ||
| 2926 | "async-lock", | ||
| 2927 | "async-process", | ||
| 2928 | "async-recursion", | ||
| 2929 | "async-task", | ||
| 2930 | "async-trait", | ||
| 2931 | "blocking", | ||
| 2932 | "byteorder", | ||
| 2933 | "derivative", | ||
| 2934 | "enumflags2", | ||
| 2935 | "event-listener", | ||
| 2936 | "futures-core", | ||
| 2937 | "futures-sink", | ||
| 2938 | "futures-util", | ||
| 2939 | "hex", | ||
| 2940 | "nix 0.26.4", | ||
| 2941 | "once_cell", | ||
| 2942 | "ordered-stream", | ||
| 2943 | "rand", | ||
| 2944 | "serde", | ||
| 2945 | "serde_repr", | ||
| 2946 | "sha1", | ||
| 2947 | "static_assertions", | ||
| 2948 | "tracing", | ||
| 2949 | "uds_windows", | ||
| 2950 | "winapi", | ||
| 2951 | "xdg-home", | ||
| 2952 | "zbus_macros", | ||
| 2953 | "zbus_names", | ||
| 2954 | "zvariant", | ||
| 2955 | ] | ||
| 2956 | |||
| 2957 | [[package]] | ||
| 2958 | name = "zbus_macros" | ||
| 2959 | version = "3.14.1" | ||
| 2960 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2961 | checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" | ||
| 2962 | dependencies = [ | ||
| 2963 | "proc-macro-crate", | ||
| 2964 | "proc-macro2", | ||
| 2965 | "quote", | ||
| 2966 | "regex", | ||
| 2967 | "syn 1.0.109", | ||
| 2968 | "zvariant_utils", | ||
| 2969 | ] | ||
| 2970 | |||
| 2971 | [[package]] | ||
| 2972 | name = "zbus_names" | ||
| 2973 | version = "2.6.0" | ||
| 2974 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2975 | checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" | ||
| 2976 | dependencies = [ | ||
| 2977 | "serde", | ||
| 2978 | "static_assertions", | ||
| 2979 | "zvariant", | ||
| 2980 | ] | ||
| 2981 | |||
| 2982 | [[package]] | ||
| 1139 | name = "zeroize" | 2983 | name = "zeroize" |
| 1140 | version = "1.6.0" | 2984 | version = "1.6.0" |
| 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2985 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 1142 | checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" | 2986 | checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" |
| 2987 | |||
| 2988 | [[package]] | ||
| 2989 | name = "zvariant" | ||
| 2990 | version = "3.15.0" | ||
| 2991 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2992 | checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" | ||
| 2993 | dependencies = [ | ||
| 2994 | "byteorder", | ||
| 2995 | "enumflags2", | ||
| 2996 | "libc", | ||
| 2997 | "serde", | ||
| 2998 | "static_assertions", | ||
| 2999 | "zvariant_derive", | ||
| 3000 | ] | ||
| 3001 | |||
| 3002 | [[package]] | ||
| 3003 | name = "zvariant_derive" | ||
| 3004 | version = "3.15.0" | ||
| 3005 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3006 | checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" | ||
| 3007 | dependencies = [ | ||
| 3008 | "proc-macro-crate", | ||
| 3009 | "proc-macro2", | ||
| 3010 | "quote", | ||
| 3011 | "syn 1.0.109", | ||
| 3012 | "zvariant_utils", | ||
| 3013 | ] | ||
| 3014 | |||
| 3015 | [[package]] | ||
| 3016 | name = "zvariant_utils" | ||
| 3017 | version = "1.0.1" | ||
| 3018 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3019 | checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" | ||
| 3020 | dependencies = [ | ||
| 3021 | "proc-macro2", | ||
| 3022 | "quote", | ||
| 3023 | "syn 1.0.109", | ||
| 3024 | ] | ||
| @@ -13,16 +13,24 @@ categories = ["command-line-utilities","git"] | |||
| 13 | 13 | ||
| 14 | [dependencies] | 14 | [dependencies] |
| 15 | anyhow = "1.0.75" | 15 | anyhow = "1.0.75" |
| 16 | chacha20poly1305 = "0.10.1" | ||
| 16 | clap = { version = "4.3.19", features = ["derive"] } | 17 | clap = { version = "4.3.19", features = ["derive"] } |
| 17 | dialoguer = "0.10.4" | 18 | dialoguer = "0.10.4" |
| 18 | directories = "5.0.1" | 19 | directories = "5.0.1" |
| 20 | keyring = "2.0.5" | ||
| 21 | nostr = "0.23.0" | ||
| 22 | passwords = "3.1.13" | ||
| 23 | scrypt = "0.11.0" | ||
| 19 | serde = { version = "1.0.181", features = ["derive"] } | 24 | serde = { version = "1.0.181", features = ["derive"] } |
| 20 | serde_json = "1.0.105" | 25 | serde_json = "1.0.105" |
| 26 | zeroize = "1.6.0" | ||
| 21 | 27 | ||
| 22 | [dev-dependencies] | 28 | [dev-dependencies] |
| 23 | assert_cmd = "2.0.12" | 29 | assert_cmd = "2.0.12" |
| 24 | duplicate = "1.0.0" | 30 | duplicate = "1.0.0" |
| 25 | mockall = "0.11.4" | 31 | mockall = "0.11.4" |
| 32 | once_cell = "1.18.0" | ||
| 33 | rexpect = "0.5.0" | ||
| 26 | serial_test = "2.0.0" | 34 | serial_test = "2.0.0" |
| 27 | test_utils = { path = "test_utils" } | 35 | test_utils = { path = "test_utils" } |
| 28 | 36 | ||
| @@ -18,19 +18,13 @@ | |||
| 18 | devShells.default = mkShell { | 18 | devShells.default = mkShell { |
| 19 | 19 | ||
| 20 | nativeBuildInputs = [ | 20 | nativeBuildInputs = [ |
| 21 | # stable to be introduced when the following issue is resolved | 21 | # override rustfmt with nightly toolchain version to support unstable features |
| 22 | # ideally this wouldn't be pinned to a specific nightly version but | ||
| 23 | # selectLatestNightlyWith isn't support with mixed toolchains | ||
| 22 | # https://github.com/oxalica/rust-overlay/issues/136 | 24 | # https://github.com/oxalica/rust-overlay/issues/136 |
| 23 | # rust-bin.stable.latest.default | 25 | (lib.hiPrio rust-bin.nightly."2023-09-01".rustfmt) |
| 24 | # nightly for rustfmt | 26 | rust-bin.stable.latest.default |
| 25 | ( | 27 | |
| 26 | rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { | ||
| 27 | extensions = [ | ||
| 28 | "rust-src" | ||
| 29 | "rustfmt" | ||
| 30 | "clippy" | ||
| 31 | ]; | ||
| 32 | }) | ||
| 33 | ) | ||
| 34 | ]; | 28 | ]; |
| 35 | 29 | ||
| 36 | buildInputs = [ | 30 | buildInputs = [ |
diff --git a/planning.md b/planning.md new file mode 100644 index 0000000..d17d176 --- /dev/null +++ b/planning.md | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* ! | ||
| 2 | |||
| 3 | # Authentication and Key Management Requirements | ||
| 4 | |||
| 5 | ## User Experience | ||
| 6 | |||
| 7 | For a smooth UX: | ||
| 8 | 1. a private key should only need to be imported once | ||
| 9 | 2. authentication to sign events should persist at least across multiple calls | ||
| 10 | to the cli tool within a single terminal session. | ||
| 11 | |||
| 12 | ## Security | ||
| 13 | |||
| 14 | 1. key material must be encrypted with a salted passphrase when stored on disk. | ||
| 15 | 2. the passphase should only be accessable | ||
| 16 | a) by this specific cli tool; or alternatively | ||
| 17 | b) only within the terminal session | ||
| 18 | |||
| 19 | |||
| 20 | # Implementation | ||
| 21 | |||
| 22 | Every private key entired into the tool is encrypted with a salted user | ||
| 23 | provided passphrase and stored on disk in the tool's configuration file | ||
| 24 | alongside display_name and public key for identification. | ||
| 25 | |||
| 26 | The private key of the current logged-in user is encrypted with a salted | ||
| 27 | randomly generated token and stored on disk in the configuration file alongside | ||
| 28 | the public key for identification. The token is stored in the OS's keyring | ||
| 29 | using a rust crate called 'keyring'. On Linux this expires after a few days | ||
| 30 | whilst on Windows and MacOS it never expires. | ||
| 31 | |||
| 32 | Should the token be cycled? cycling the token would prevent an attacker who had | ||
| 33 | access to only the token or the encrypted key from returning after the token | ||
| 34 | had been cycled. This isn't worth it. An attacker is much more likely to have | ||
| 35 | access to both simultainiously. | ||
| 36 | |||
| 37 | logout should delete the key encrypted with the token and the token. It should | ||
| 38 | give the option to clear encrypted key material for the current user or all | ||
| 39 | users. | ||
| 40 | |||
| 41 | */ | ||
| 42 | |||
| 43 | init | ||
| 44 | |||
| 45 | initialize repoisiotr | ||
| 46 | |||
| 47 | |||
| 48 | replaceable event | ||
| 49 | |||
| 50 | commit id | ||
| 51 | |||
| 52 | search by initial commit / initial 5 commits | ||
| 53 | name | ||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | initialising a reposistory | ||
| 58 | |||
| 59 | |||
| 60 | git nostr init | ||
| 61 | > intialise repo | ||
| 62 | |||
| 63 | |||
| 64 | git nostr init - request patches / PRs, issues, | ||
| 65 | features to support | ||
| 66 | -- branch | ||
| 67 | -- patches / PRs | ||
| 68 | -- issues | ||
| 69 | |||
| 70 | -- override git push to also push to nostr. | ||
| 71 | |||
| 72 | settings | ||
| 73 | --git-repos - one or more git repositories where the latest commits can be pulled from | ||
| 74 | --name | ||
| 75 | --description | ||
| 76 | |||
| 77 | |||
| 78 | git push nostr main | ||
diff --git a/src/cli_interactor.rs b/src/cli_interactor.rs index 2f28aee..d7de087 100644 --- a/src/cli_interactor.rs +++ b/src/cli_interactor.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use anyhow::{bail, Result}; | 1 | use anyhow::Result; |
| 2 | use dialoguer::{theme::ColorfulTheme, Input}; | 2 | use dialoguer::{theme::ColorfulTheme, Input, Password}; |
| 3 | #[cfg(test)] | 3 | #[cfg(test)] |
| 4 | use mockall::*; | 4 | use mockall::*; |
| 5 | 5 | ||
| @@ -11,6 +11,7 @@ pub struct Interactor { | |||
| 11 | #[cfg_attr(test, automock)] | 11 | #[cfg_attr(test, automock)] |
| 12 | pub trait InteractorPrompt { | 12 | pub trait InteractorPrompt { |
| 13 | fn input(&self, parms: PromptInputParms) -> Result<String>; | 13 | fn input(&self, parms: PromptInputParms) -> Result<String>; |
| 14 | fn password(&self, parms: PromptPasswordParms) -> Result<String>; | ||
| 14 | } | 15 | } |
| 15 | impl InteractorPrompt for Interactor { | 16 | impl InteractorPrompt for Interactor { |
| 16 | fn input(&self, parms: PromptInputParms) -> Result<String> { | 17 | fn input(&self, parms: PromptInputParms) -> Result<String> { |
| @@ -19,6 +20,15 @@ impl InteractorPrompt for Interactor { | |||
| 19 | .interact_text()?; | 20 | .interact_text()?; |
| 20 | Ok(input) | 21 | Ok(input) |
| 21 | } | 22 | } |
| 23 | fn password(&self, parms: PromptPasswordParms) -> Result<String> { | ||
| 24 | let mut p = Password::with_theme(&self.theme); | ||
| 25 | p.with_prompt(parms.prompt); | ||
| 26 | if parms.confirm { | ||
| 27 | p.with_confirmation("confirm password", "passwords didnt match..."); | ||
| 28 | } | ||
| 29 | let pass: String = p.interact()?; | ||
| 30 | Ok(pass) | ||
| 31 | } | ||
| 22 | } | 32 | } |
| 23 | 33 | ||
| 24 | #[derive(Default)] | 34 | #[derive(Default)] |
| @@ -32,3 +42,20 @@ impl PromptInputParms { | |||
| 32 | self | 42 | self |
| 33 | } | 43 | } |
| 34 | } | 44 | } |
| 45 | |||
| 46 | #[derive(Default)] | ||
| 47 | pub struct PromptPasswordParms { | ||
| 48 | pub prompt: String, | ||
| 49 | pub confirm: bool, | ||
| 50 | } | ||
| 51 | |||
| 52 | impl PromptPasswordParms { | ||
| 53 | pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self { | ||
| 54 | self.prompt = prompt.into(); | ||
| 55 | self | ||
| 56 | } | ||
| 57 | pub const fn with_confirm(mut self) -> Self { | ||
| 58 | self.confirm = true; | ||
| 59 | self | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/src/config.rs b/src/config.rs index b26dea0..f410934 100644 --- a/src/config.rs +++ b/src/config.rs | |||
| @@ -4,6 +4,7 @@ use anyhow::{anyhow, Context, Result}; | |||
| 4 | use directories::ProjectDirs; | 4 | use directories::ProjectDirs; |
| 5 | #[cfg(test)] | 5 | #[cfg(test)] |
| 6 | use mockall::*; | 6 | use mockall::*; |
| 7 | use nostr::secp256k1::XOnlyPublicKey; | ||
| 7 | use serde::{self, Deserialize, Serialize}; | 8 | use serde::{self, Deserialize, Serialize}; |
| 8 | 9 | ||
| 9 | #[derive(Default)] | 10 | #[derive(Default)] |
| @@ -59,7 +60,7 @@ impl ConfigManagement for ConfigManager { | |||
| 59 | } | 60 | } |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | #[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)] | 63 | #[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)] |
| 63 | #[allow(clippy::module_name_repetitions)] | 64 | #[allow(clippy::module_name_repetitions)] |
| 64 | pub struct MyConfig { | 65 | pub struct MyConfig { |
| 65 | pub version: u8, | 66 | pub version: u8, |
| @@ -68,44 +69,64 @@ pub struct MyConfig { | |||
| 68 | 69 | ||
| 69 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] | 70 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] |
| 70 | pub struct UserRef { | 71 | pub struct UserRef { |
| 71 | pub nsec: String, | 72 | pub public_key: XOnlyPublicKey, |
| 73 | pub encrypted_key: String, | ||
| 72 | } | 74 | } |
| 73 | 75 | ||
| 74 | #[cfg(test)] | 76 | #[cfg(test)] |
| 75 | mod tests { | 77 | mod tests { |
| 76 | use anyhow::Result; | 78 | use anyhow::Result; |
| 77 | use serial_test::serial; | 79 | use serial_test::serial; |
| 78 | use test_utils::*; | ||
| 79 | 80 | ||
| 80 | use super::*; | 81 | use super::*; |
| 81 | 82 | ||
| 83 | fn backup_existing_config() -> Result<()> { | ||
| 84 | let config_path = get_dirs()?.config_dir().join("config.json"); | ||
| 85 | let backup_config_path = get_dirs()?.config_dir().join("config-backup.json"); | ||
| 86 | if config_path.exists() { | ||
| 87 | std::fs::rename(config_path, backup_config_path)?; | ||
| 88 | } | ||
| 89 | Ok(()) | ||
| 90 | } | ||
| 91 | |||
| 92 | fn restore_config_backup() -> Result<()> { | ||
| 93 | let config_path = get_dirs()?.config_dir().join("config.json"); | ||
| 94 | let backup_config_path = get_dirs()?.config_dir().join("config-backup.json"); | ||
| 95 | if config_path.exists() { | ||
| 96 | std::fs::remove_file(&config_path)?; | ||
| 97 | } | ||
| 98 | if backup_config_path.exists() { | ||
| 99 | std::fs::rename(backup_config_path, config_path)?; | ||
| 100 | } | ||
| 101 | Ok(()) | ||
| 102 | } | ||
| 103 | |||
| 82 | mod load { | 104 | mod load { |
| 83 | use super::*; | 105 | use super::*; |
| 84 | 106 | ||
| 85 | #[test] | 107 | #[test] |
| 86 | #[serial] | 108 | #[serial] |
| 87 | fn when_config_file_doesnt_exist_defaults_are_returned() -> Result<()> { | 109 | fn when_config_file_doesnt_exist_defaults_are_returned() -> Result<()> { |
| 88 | with_fresh_config(|| { | 110 | backup_existing_config()?; |
| 89 | assert_eq!(ConfigManager.load()?, MyConfig::default()); | 111 | let c = ConfigManager; |
| 90 | 112 | assert_eq!(c.load()?, MyConfig::default()); | |
| 91 | Ok(()) | 113 | restore_config_backup()?; |
| 92 | }) | 114 | Ok(()) |
| 93 | } | 115 | } |
| 94 | 116 | ||
| 95 | #[test] | 117 | #[test] |
| 96 | #[serial] | 118 | #[serial] |
| 97 | fn when_config_file_exists_it_is_returned() -> Result<()> { | 119 | fn when_config_file_exists_it_is_returned() -> Result<()> { |
| 98 | with_fresh_config(|| { | 120 | backup_existing_config()?; |
| 99 | let c = ConfigManager; | 121 | let c = ConfigManager; |
| 100 | let new_config = MyConfig { | 122 | let new_config = MyConfig { |
| 101 | version: 255, | 123 | version: 255, |
| 102 | ..MyConfig::default() | 124 | ..MyConfig::default() |
| 103 | }; | 125 | }; |
| 104 | c.save(&new_config)?; | 126 | c.save(&new_config)?; |
| 105 | assert_eq!(c.load()?, new_config); | 127 | assert_eq!(c.load()?, new_config); |
| 106 | 128 | restore_config_backup()?; | |
| 107 | Ok(()) | 129 | Ok(()) |
| 108 | }) | ||
| 109 | } | 130 | } |
| 110 | } | 131 | } |
| 111 | 132 | ||
| @@ -115,38 +136,36 @@ mod tests { | |||
| 115 | #[test] | 136 | #[test] |
| 116 | #[serial] | 137 | #[serial] |
| 117 | fn when_config_file_doesnt_config_is_saved() -> Result<()> { | 138 | fn when_config_file_doesnt_config_is_saved() -> Result<()> { |
| 118 | with_fresh_config(|| { | 139 | backup_existing_config()?; |
| 119 | let c = ConfigManager; | 140 | let c = ConfigManager; |
| 120 | let new_config = MyConfig { | 141 | let new_config = MyConfig { |
| 121 | version: 255, | 142 | version: 255, |
| 122 | ..MyConfig::default() | 143 | ..MyConfig::default() |
| 123 | }; | 144 | }; |
| 124 | c.save(&new_config)?; | 145 | c.save(&new_config)?; |
| 125 | assert_eq!(c.load()?, new_config); | 146 | assert_eq!(c.load().unwrap(), new_config); |
| 126 | 147 | restore_config_backup()?; | |
| 127 | Ok(()) | 148 | Ok(()) |
| 128 | }) | ||
| 129 | } | 149 | } |
| 130 | 150 | ||
| 131 | #[test] | 151 | #[test] |
| 132 | #[serial] | 152 | #[serial] |
| 133 | fn when_config_file_exists_new_config_is_saved() -> Result<()> { | 153 | fn when_config_file_exists_new_config_is_saved() -> Result<()> { |
| 134 | with_fresh_config(|| { | 154 | backup_existing_config()?; |
| 135 | let c = ConfigManager; | 155 | let c = ConfigManager; |
| 136 | let config = MyConfig { | 156 | let config = MyConfig { |
| 137 | version: 255, | 157 | version: 255, |
| 138 | ..MyConfig::default() | 158 | ..MyConfig::default() |
| 139 | }; | 159 | }; |
| 140 | c.save(&config)?; | 160 | c.save(&config)?; |
| 141 | let new_config = MyConfig { | 161 | let new_config = MyConfig { |
| 142 | version: 254, | 162 | version: 254, |
| 143 | ..MyConfig::default() | 163 | ..MyConfig::default() |
| 144 | }; | 164 | }; |
| 145 | c.save(&new_config)?; | 165 | c.save(&new_config)?; |
| 146 | assert_eq!(c.load()?, new_config); | 166 | assert_eq!(c.load().unwrap(), new_config); |
| 147 | 167 | restore_config_backup()?; | |
| 148 | Ok(()) | 168 | Ok(()) |
| 149 | }) | ||
| 150 | } | 169 | } |
| 151 | } | 170 | } |
| 152 | } | 171 | } |
diff --git a/src/key_handling/encryption.rs b/src/key_handling/encryption.rs new file mode 100644 index 0000000..0ef7f69 --- /dev/null +++ b/src/key_handling/encryption.rs | |||
| @@ -0,0 +1,247 @@ | |||
| 1 | use anyhow::{anyhow, bail, ensure, Context, Result}; | ||
| 2 | use chacha20poly1305::{ | ||
| 3 | aead::{rand_core::RngCore, Aead, AeadCore, KeyInit, OsRng, Payload}, | ||
| 4 | XChaCha20Poly1305, | ||
| 5 | }; | ||
| 6 | #[cfg(test)] | ||
| 7 | use mockall::*; | ||
| 8 | use nostr::{prelude::*, Keys}; | ||
| 9 | use rand::{distributions::Alphanumeric, thread_rng, Rng}; | ||
| 10 | use zeroize::Zeroize; | ||
| 11 | |||
| 12 | #[derive(Default)] | ||
| 13 | pub struct Encryptor; | ||
| 14 | |||
| 15 | #[cfg_attr(test, automock)] | ||
| 16 | pub trait EncryptDecrypt { | ||
| 17 | /// requires less CPU time if the password is long | ||
| 18 | fn encrypt_key(&self, keys: &Keys, password: &str) -> Result<String>; | ||
| 19 | fn decrypt_key(&self, encrypted_key: &str, password: &str) -> Result<Keys>; | ||
| 20 | /// generates a long random string | ||
| 21 | fn random_token(&self) -> String; | ||
| 22 | } | ||
| 23 | |||
| 24 | /// approach and code adapted from nostr gossip client | ||
| 25 | impl EncryptDecrypt for Encryptor { | ||
| 26 | fn encrypt_key(&self, keys: &Keys, password: &str) -> Result<String> { | ||
| 27 | // Generate a random 16-byte salt | ||
| 28 | let salt = { | ||
| 29 | let mut salt: [u8; 16] = [0; 16]; | ||
| 30 | OsRng.fill_bytes(&mut salt); | ||
| 31 | salt | ||
| 32 | }; | ||
| 33 | |||
| 34 | let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); | ||
| 35 | |||
| 36 | let log2_rounds: u8 = if password.len() > 20 { | ||
| 37 | // we have enough of entropy - no need to spend CPU time adding much more | ||
| 38 | 1 | ||
| 39 | } else { | ||
| 40 | // default (scrypt::Params::RECOMMENDED_LOG_N) is 17 but 30s is too long to wait | ||
| 41 | 15 | ||
| 42 | }; | ||
| 43 | |||
| 44 | let associated_data: Vec<u8> = vec![1]; | ||
| 45 | |||
| 46 | let ciphertext = { | ||
| 47 | let cipher = { | ||
| 48 | let symmetric_key = password_to_key(password, &salt, log2_rounds) | ||
| 49 | .context("failed create encryption key from password")?; | ||
| 50 | XChaCha20Poly1305::new((&symmetric_key).into()) | ||
| 51 | }; | ||
| 52 | cipher | ||
| 53 | .encrypt( | ||
| 54 | &nonce, | ||
| 55 | Payload { | ||
| 56 | msg: keys | ||
| 57 | .secret_key() | ||
| 58 | .context( | ||
| 59 | "supplied key should reveal secret key. Is this a public key only?", | ||
| 60 | )? | ||
| 61 | .display_secret() | ||
| 62 | .to_string() | ||
| 63 | .as_bytes(), | ||
| 64 | aad: &associated_data, | ||
| 65 | }, | ||
| 66 | ) | ||
| 67 | .map_err(|_| anyhow!("ChaChaPoly1305 failed to encrypt nsec with password"))? | ||
| 68 | }; | ||
| 69 | // Combine salt, IV and ciphertext | ||
| 70 | let mut concatenation: Vec<u8> = Vec::new(); | ||
| 71 | concatenation.push(0x1); // 1 byte version number | ||
| 72 | concatenation.push(log2_rounds); // 1 byte for scrypt N (rounds) | ||
| 73 | concatenation.extend(salt); // 16 bytes of salt | ||
| 74 | concatenation.extend(nonce); // 24 bytes of nonce | ||
| 75 | concatenation.extend(associated_data); // 1 byte of key security | ||
| 76 | concatenation.extend(ciphertext); // 48 bytes of ciphertext expected | ||
| 77 | // Total length is 91 = 1 + 1 + 16 + 24 + 1 + 48 | ||
| 78 | |||
| 79 | bech32::encode( | ||
| 80 | "ncryptsec", | ||
| 81 | concatenation.to_base32(), | ||
| 82 | bech32::Variant::Bech32, | ||
| 83 | ) | ||
| 84 | .context("encrypted nsec failed to encode") | ||
| 85 | } | ||
| 86 | |||
| 87 | fn decrypt_key(&self, encrypted_key: &str, password: &str) -> Result<nostr::Keys> { | ||
| 88 | let data = | ||
| 89 | bech32::decode(encrypted_key).context("failed to decode encrypted key as bech32")?; | ||
| 90 | if data.0 != "ncryptsec" { | ||
| 91 | bail!("encrypted key is in the wrong format - it doesnt start with ncryptsec"); | ||
| 92 | } | ||
| 93 | let concatenation = Vec::<u8>::from_base32(&data.1) | ||
| 94 | .context("failed to convert bech32::decode output to Vec<u8>")?; | ||
| 95 | |||
| 96 | // Break into parts | ||
| 97 | let version: u8 = concatenation[0]; | ||
| 98 | ensure!(version == 0x1, "encryption version is incorrect"); | ||
| 99 | let log2_rounds: u8 = concatenation[1]; | ||
| 100 | let salt: [u8; 16] = concatenation[2..2 + 16].try_into()?; | ||
| 101 | let nonce = &concatenation[2 + 16..2 + 16 + 24]; | ||
| 102 | let associated_data = &concatenation[(2 + 16 + 24)..=(2 + 16 + 24)]; | ||
| 103 | let ciphertext = &concatenation[2 + 16 + 24 + 1..]; | ||
| 104 | |||
| 105 | let cipher = { | ||
| 106 | let symmetric_key = password_to_key(password, &salt, log2_rounds)?; | ||
| 107 | XChaCha20Poly1305::new((&symmetric_key).into()) | ||
| 108 | }; | ||
| 109 | |||
| 110 | let payload = Payload { | ||
| 111 | msg: ciphertext, | ||
| 112 | aad: associated_data, | ||
| 113 | }; | ||
| 114 | |||
| 115 | let mut inner_secret = cipher | ||
| 116 | .decrypt(nonce.into(), payload) | ||
| 117 | .map_err(|_| anyhow!("failed to decrypt"))?; | ||
| 118 | |||
| 119 | if associated_data.is_empty() { | ||
| 120 | bail!("invalid encrypted key"); | ||
| 121 | } | ||
| 122 | |||
| 123 | let key = Keys::from_sk_str( | ||
| 124 | std::str::from_utf8(&inner_secret).context("inner secret is not [u8]")?, | ||
| 125 | ) | ||
| 126 | .context("incorrect password. Key decrypted with password did not produce a valid nsec.")?; | ||
| 127 | |||
| 128 | inner_secret.zeroize(); | ||
| 129 | |||
| 130 | Ok(key) | ||
| 131 | } | ||
| 132 | |||
| 133 | fn random_token(&self) -> String { | ||
| 134 | thread_rng() | ||
| 135 | .sample_iter(&Alphanumeric) | ||
| 136 | .take(32) | ||
| 137 | .map(char::from) | ||
| 138 | .collect() | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | /// uses scrypt to stretch password into key | ||
| 143 | fn password_to_key(password: &str, salt: &[u8; 16], log_n: u8) -> Result<[u8; 32]> { | ||
| 144 | let params = scrypt::Params::new(log_n, 8, 1, 32) | ||
| 145 | .context("scrypt failed to generate params to stretch password")?; | ||
| 146 | let mut key: [u8; 32] = [0; 32]; | ||
| 147 | if log_n > 14 { | ||
| 148 | println!("this may take a few seconds..."); | ||
| 149 | } | ||
| 150 | |||
| 151 | scrypt::scrypt(password.as_bytes(), salt, ¶ms, &mut key) | ||
| 152 | .context("scrypt failed to stretch password")?; | ||
| 153 | Ok(key) | ||
| 154 | } | ||
| 155 | |||
| 156 | #[cfg(test)] | ||
| 157 | mod tests { | ||
| 158 | use test_utils::*; | ||
| 159 | |||
| 160 | use super::*; | ||
| 161 | |||
| 162 | #[test] | ||
| 163 | fn encrypt_key_produces_string_prefixed_with() -> Result<()> { | ||
| 164 | let s = Encryptor.encrypt_key(&nostr::Keys::generate(), TEST_PASSWORD)?; | ||
| 165 | assert!(s.starts_with("ncryptsec")); | ||
| 166 | Ok(()) | ||
| 167 | } | ||
| 168 | |||
| 169 | #[test] | ||
| 170 | // ensures password encryption hasn't changed | ||
| 171 | fn decrypts_with_strong_password_from_reference_string() -> Result<()> { | ||
| 172 | let encryptor = Encryptor; | ||
| 173 | let decrypted_key = encryptor.decrypt_key(TEST_KEY_1_ENCRYPTED, TEST_PASSWORD)?; | ||
| 174 | |||
| 175 | assert_eq!( | ||
| 176 | format!( | ||
| 177 | "{}", | ||
| 178 | TEST_KEY_1_KEYS.secret_key().unwrap().to_bech32().unwrap() | ||
| 179 | ), | ||
| 180 | format!( | ||
| 181 | "{}", | ||
| 182 | decrypted_key.secret_key().unwrap().to_bech32().unwrap() | ||
| 183 | ), | ||
| 184 | ); | ||
| 185 | Ok(()) | ||
| 186 | } | ||
| 187 | |||
| 188 | #[test] | ||
| 189 | // ensures password encryption hasn't changed | ||
| 190 | fn decrypts_with_weak_password_from_reference_string() -> Result<()> { | ||
| 191 | let encryptor = Encryptor; | ||
| 192 | let decrypted_key = encryptor.decrypt_key(TEST_KEY_1_ENCRYPTED_WEAK, TEST_WEAK_PASSWORD)?; | ||
| 193 | |||
| 194 | assert_eq!( | ||
| 195 | format!( | ||
| 196 | "{}", | ||
| 197 | TEST_KEY_1_KEYS.secret_key().unwrap().to_bech32().unwrap() | ||
| 198 | ), | ||
| 199 | format!( | ||
| 200 | "{}", | ||
| 201 | decrypted_key.secret_key().unwrap().to_bech32().unwrap() | ||
| 202 | ), | ||
| 203 | ); | ||
| 204 | Ok(()) | ||
| 205 | } | ||
| 206 | |||
| 207 | #[test] | ||
| 208 | fn decrypts_key_encrypted_using_encrypt_key() -> Result<()> { | ||
| 209 | let encryptor = Encryptor; | ||
| 210 | let key = nostr::Keys::generate(); | ||
| 211 | let s = encryptor.encrypt_key(&key, TEST_PASSWORD)?; | ||
| 212 | let newkey = encryptor.decrypt_key(s.as_str(), TEST_PASSWORD)?; | ||
| 213 | |||
| 214 | assert_eq!( | ||
| 215 | format!("{}", key.secret_key().unwrap().to_bech32().unwrap()), | ||
| 216 | format!("{}", newkey.secret_key().unwrap().to_bech32().unwrap()), | ||
| 217 | ); | ||
| 218 | Ok(()) | ||
| 219 | } | ||
| 220 | |||
| 221 | #[test] | ||
| 222 | fn decrypt_key_successfully_decrypts_key_encrypted_using_encrypt_key() -> Result<()> { | ||
| 223 | let encryptor = Encryptor; | ||
| 224 | let key = nostr::Keys::generate(); | ||
| 225 | let s = encryptor.encrypt_key(&key, TEST_PASSWORD)?; | ||
| 226 | let newkey = encryptor.decrypt_key(s.as_str(), TEST_PASSWORD)?; | ||
| 227 | |||
| 228 | assert_eq!( | ||
| 229 | format!("{}", key.secret_key().unwrap().to_bech32().unwrap()), | ||
| 230 | format!("{}", newkey.secret_key().unwrap().to_bech32().unwrap()), | ||
| 231 | ); | ||
| 232 | Ok(()) | ||
| 233 | } | ||
| 234 | |||
| 235 | #[test] | ||
| 236 | fn password_to_key_returns_ok_with_standard_password() { | ||
| 237 | let salt = { | ||
| 238 | let mut salt: [u8; 16] = [0; 16]; | ||
| 239 | OsRng.fill_bytes(&mut salt); | ||
| 240 | salt | ||
| 241 | }; | ||
| 242 | |||
| 243 | let log2_rounds: u8 = 1; | ||
| 244 | |||
| 245 | assert!(password_to_key(TEST_PASSWORD, &salt, log2_rounds).is_ok()); | ||
| 246 | } | ||
| 247 | } | ||
diff --git a/src/key_handling/mod.rs b/src/key_handling/mod.rs index 913bd46..bcb10df 100644 --- a/src/key_handling/mod.rs +++ b/src/key_handling/mod.rs | |||
| @@ -1 +1,2 @@ | |||
| 1 | pub mod encryption; | ||
| 1 | pub mod users; | 2 | pub mod users; |
diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs index bd1748a..1d2cc34 100644 --- a/src/key_handling/users.rs +++ b/src/key_handling/users.rs | |||
| @@ -1,47 +1,90 @@ | |||
| 1 | use anyhow::{Context, Result}; | 1 | use anyhow::{Context, Result}; |
| 2 | use nostr::prelude::*; | ||
| 3 | use zeroize::Zeroize; | ||
| 2 | 4 | ||
| 5 | use super::encryption::{EncryptDecrypt, Encryptor}; | ||
| 3 | use crate::{ | 6 | use crate::{ |
| 4 | cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, | 7 | cli_interactor::{Interactor, InteractorPrompt, PromptInputParms, PromptPasswordParms}, |
| 5 | config::{ConfigManagement, ConfigManager, MyConfig, UserRef}, | 8 | config::{self, ConfigManagement, ConfigManager}, |
| 6 | }; | 9 | }; |
| 7 | 10 | ||
| 8 | #[derive(Default)] | 11 | #[derive(Default)] |
| 9 | pub struct UserManager { | 12 | pub struct UserManager { |
| 10 | config_manager: ConfigManager, | 13 | config_manager: ConfigManager, |
| 11 | interactor: Interactor, | 14 | interactor: Interactor, |
| 15 | encryptor: Encryptor, | ||
| 12 | } | 16 | } |
| 13 | 17 | ||
| 14 | pub trait UserManagement { | 18 | pub trait UserManagement { |
| 15 | fn add(&self, nsec: &Option<String>) -> Result<()>; | 19 | fn add(&self, nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys>; |
| 16 | } | 20 | } |
| 17 | 21 | ||
| 18 | #[cfg(test)] | 22 | #[cfg(test)] |
| 19 | use duplicate::duplicate_item; | 23 | use duplicate::duplicate_item; |
| 20 | #[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))] | 24 | #[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))] |
| 21 | impl UserManagement for UserManager { | 25 | impl UserManagement for UserManager { |
| 22 | fn add(&self, nsec: &Option<String>) -> Result<()> { | 26 | fn add(&self, nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys> { |
| 23 | let nsec = match nsec.clone() { | 27 | let mut prompt = "login with nsec (or hex private key)"; |
| 24 | Some(nsec) => nsec, | 28 | let keys = loop { |
| 29 | let pk = match nsec.clone() { | ||
| 30 | Some(nsec) => nsec, | ||
| 31 | None => self | ||
| 32 | .interactor | ||
| 33 | .input(PromptInputParms::default().with_prompt(prompt)) | ||
| 34 | .context("failed to get nsec input from interactor")?, | ||
| 35 | }; | ||
| 36 | match Keys::from_sk_str(&pk) { | ||
| 37 | Ok(key) => { | ||
| 38 | break key; | ||
| 39 | } | ||
| 40 | Err(e) => { | ||
| 41 | if nsec.is_some() { | ||
| 42 | return Err(e).context( | ||
| 43 | "invalid nsec - supplied parameter could not be converted into a nostr private key", | ||
| 44 | ); | ||
| 45 | } | ||
| 46 | prompt = "invalid nsec. try again with nsec (or hex private key)"; | ||
| 47 | } | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | let mut pass = match password.clone() { | ||
| 52 | Some(pass) => pass, | ||
| 25 | None => self | 53 | None => self |
| 26 | .interactor | 54 | .interactor |
| 27 | .input( | 55 | .password( |
| 28 | PromptInputParms::default().with_prompt("login with nsec (or hex private key)"), | 56 | PromptPasswordParms::default() |
| 57 | .with_prompt("encrypt with password") | ||
| 58 | .with_confirm(), | ||
| 29 | ) | 59 | ) |
| 30 | .context("failed to get nsec input from interactor.input")?, | 60 | .context("failed to get password input from interactor.password")?, |
| 31 | }; | 61 | }; |
| 32 | 62 | ||
| 63 | let encrypted_secret_key = self | ||
| 64 | .encryptor | ||
| 65 | .encrypt_key(&keys, &pass) | ||
| 66 | .context("failed to encrypt nsec with password.")?; | ||
| 67 | pass.zeroize(); | ||
| 68 | |||
| 69 | let user_ref = config::UserRef { | ||
| 70 | public_key: keys.public_key(), | ||
| 71 | encrypted_key: encrypted_secret_key, | ||
| 72 | }; | ||
| 73 | |||
| 74 | // remove any duplicate entries for key before adding it to config | ||
| 75 | let mut cfg = self.config_manager.load().context("failed to load application config to find and remove any old versions of the user's encrypted key")?; | ||
| 76 | cfg.users = cfg | ||
| 77 | .users | ||
| 78 | .clone() | ||
| 79 | .into_iter() | ||
| 80 | .filter(|r| !r.public_key.eq(&keys.public_key())) | ||
| 81 | .collect(); | ||
| 82 | cfg.users.push(user_ref); | ||
| 33 | self.config_manager | 83 | self.config_manager |
| 34 | .save(&MyConfig { | 84 | .save(&cfg) |
| 35 | users: vec![UserRef { | ||
| 36 | nsec: nsec.to_string(), | ||
| 37 | }], | ||
| 38 | ..MyConfig::default() | ||
| 39 | }) | ||
| 40 | .context("failed to save application configuration with new user details in")?; | 85 | .context("failed to save application configuration with new user details in")?; |
| 41 | 86 | ||
| 42 | println!("logged in as {nsec}"); | 87 | Ok(keys) |
| 43 | |||
| 44 | Ok(()) | ||
| 45 | } | 88 | } |
| 46 | } | 89 | } |
| 47 | 90 | ||
| @@ -50,12 +93,17 @@ mod tests { | |||
| 50 | use test_utils::*; | 93 | use test_utils::*; |
| 51 | 94 | ||
| 52 | use super::*; | 95 | use super::*; |
| 53 | use crate::{cli_interactor::MockInteractorPrompt, config::MockConfigManagement}; | 96 | use crate::{ |
| 97 | cli_interactor::MockInteractorPrompt, | ||
| 98 | config::{MockConfigManagement, MyConfig, UserRef}, | ||
| 99 | key_handling::encryption::MockEncryptDecrypt, | ||
| 100 | }; | ||
| 54 | 101 | ||
| 55 | #[derive(Default)] | 102 | #[derive(Default)] |
| 56 | pub struct MockUserManager { | 103 | pub struct MockUserManager { |
| 57 | pub config_manager: MockConfigManagement, | 104 | pub config_manager: MockConfigManagement, |
| 58 | pub interactor: MockInteractorPrompt, | 105 | pub interactor: MockInteractorPrompt, |
| 106 | pub encryptor: MockEncryptDecrypt, | ||
| 59 | } | 107 | } |
| 60 | 108 | ||
| 61 | mod add { | 109 | mod add { |
| @@ -70,28 +118,88 @@ mod tests { | |||
| 70 | self.interactor | 118 | self.interactor |
| 71 | .expect_input() | 119 | .expect_input() |
| 72 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); | 120 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); |
| 121 | self.interactor | ||
| 122 | .expect_password() | ||
| 123 | .returning(|_| Ok(TEST_PASSWORD.into())); | ||
| 124 | self.encryptor | ||
| 125 | .expect_encrypt_key() | ||
| 126 | .returning(|_, _| Ok(TEST_KEY_1_ENCRYPTED.into())); | ||
| 73 | self | 127 | self |
| 74 | } | 128 | } |
| 75 | } | 129 | } |
| 76 | 130 | ||
| 77 | mod when_nsec_is_passed { | 131 | fn reuable_user_isnt_prompted(nsec: &str) { |
| 132 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 133 | m.interactor = MockInteractorPrompt::default(); | ||
| 134 | m.interactor.expect_input().never(); | ||
| 135 | m.interactor.expect_password().never(); | ||
| 136 | let _ = m.add(&Some(nsec.into()), &Some(TEST_PASSWORD.to_string())); | ||
| 137 | } | ||
| 138 | |||
| 139 | fn reuable_config_isnt_modified(nsec: &str) { | ||
| 140 | let mut m = MockUserManager::default(); | ||
| 141 | m.config_manager.expect_save().never(); | ||
| 142 | let _ = m.add(&Some(nsec.into()), &Some(TEST_PASSWORD.to_string())); | ||
| 143 | } | ||
| 144 | |||
| 145 | mod when_valid_nsec_and_password_is_passed { | ||
| 78 | use super::*; | 146 | use super::*; |
| 79 | 147 | ||
| 80 | #[test] | 148 | #[test] |
| 81 | fn user_isnt_prompted() { | 149 | fn user_isnt_prompted() { |
| 150 | reuable_user_isnt_prompted(TEST_KEY_1_NSEC); | ||
| 151 | } | ||
| 152 | |||
| 153 | #[test] | ||
| 154 | fn results_in_correct_keys() { | ||
| 82 | let mut m = MockUserManager::default().add_return_expected_responses(); | 155 | let mut m = MockUserManager::default().add_return_expected_responses(); |
| 83 | m.interactor = MockInteractorPrompt::default(); | 156 | m.interactor = MockInteractorPrompt::default(); |
| 84 | m.interactor.expect_input().never(); | 157 | m.interactor.expect_input().never(); |
| 85 | 158 | m.interactor.expect_password().never(); | |
| 86 | let _ = m.add(&Some(TEST_KEY_1_NSEC.into())); | 159 | let r = m.add( |
| 160 | &Some(TEST_KEY_1_NSEC.into()), | ||
| 161 | &Some(TEST_PASSWORD.to_string()), | ||
| 162 | ); | ||
| 163 | assert!(r.is_ok(), "should result in keys"); | ||
| 164 | assert!( | ||
| 165 | r.is_ok_and(|k| k | ||
| 166 | .secret_key() | ||
| 167 | .is_ok_and(|k| k.display_secret().to_string().eq(TEST_KEY_1_SK_HEX))), | ||
| 168 | "keys should reflect nsec" | ||
| 169 | ); | ||
| 87 | } | 170 | } |
| 88 | } | 171 | } |
| 172 | mod when_invalid_nsec_is_passed_with_password { | ||
| 173 | use super::*; | ||
| 89 | 174 | ||
| 175 | #[test] | ||
| 176 | fn user_isnt_prompted() { | ||
| 177 | reuable_user_isnt_prompted(TEST_INVALID_NSEC); | ||
| 178 | } | ||
| 179 | |||
| 180 | #[test] | ||
| 181 | fn config_isnt_modified() { | ||
| 182 | reuable_config_isnt_modified(TEST_INVALID_NSEC); | ||
| 183 | } | ||
| 184 | |||
| 185 | #[test] | ||
| 186 | fn results_in_an_error() { | ||
| 187 | let m = MockUserManager::default(); | ||
| 188 | assert!( | ||
| 189 | m.add( | ||
| 190 | &Some(TEST_INVALID_NSEC.into()), | ||
| 191 | &Some(TEST_PASSWORD.to_string()) | ||
| 192 | ) | ||
| 193 | .is_err(), | ||
| 194 | "should result in an error" | ||
| 195 | ); | ||
| 196 | } | ||
| 197 | } | ||
| 90 | mod when_no_nsec_is_passed { | 198 | mod when_no_nsec_is_passed { |
| 91 | use super::*; | 199 | use super::*; |
| 92 | 200 | ||
| 93 | #[test] | 201 | #[test] |
| 94 | fn prompt_for_nsec() { | 202 | fn prompt_for_nsec_and_password() { |
| 95 | let mut m = MockUserManager::default().add_return_expected_responses(); | 203 | let mut m = MockUserManager::default().add_return_expected_responses(); |
| 96 | 204 | ||
| 97 | m.interactor = MockInteractorPrompt::new(); | 205 | m.interactor = MockInteractorPrompt::new(); |
| @@ -100,12 +208,31 @@ mod tests { | |||
| 100 | .once() | 208 | .once() |
| 101 | .withf(|p| p.prompt.eq("login with nsec (or hex private key)")) | 209 | .withf(|p| p.prompt.eq("login with nsec (or hex private key)")) |
| 102 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); | 210 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); |
| 211 | m.interactor | ||
| 212 | .expect_password() | ||
| 213 | .once() | ||
| 214 | .withf(|p| p.prompt.eq("encrypt with password")) | ||
| 215 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); | ||
| 103 | 216 | ||
| 104 | let _ = m.add(&None); | 217 | let _ = m.add(&None, &None); |
| 105 | } | 218 | } |
| 106 | 219 | ||
| 107 | #[test] | 220 | #[test] |
| 108 | fn stored_in_config() { | 221 | fn results_in_correct_keys() { |
| 222 | let m = MockUserManager::default().add_return_expected_responses(); | ||
| 223 | |||
| 224 | let r = m.add(&None, &None); | ||
| 225 | assert!(r.is_ok(), "should result in keys"); | ||
| 226 | assert!( | ||
| 227 | r.is_ok_and(|k| k | ||
| 228 | .secret_key() | ||
| 229 | .is_ok_and(|k| k.display_secret().to_string().eq(TEST_KEY_1_SK_HEX))), | ||
| 230 | "keys should reflect nsec" | ||
| 231 | ); | ||
| 232 | } | ||
| 233 | |||
| 234 | #[test] | ||
| 235 | fn stores_encrypted_key_in_config() { | ||
| 109 | let mut m = MockUserManager::default().add_return_expected_responses(); | 236 | let mut m = MockUserManager::default().add_return_expected_responses(); |
| 110 | 237 | ||
| 111 | m.config_manager = MockConfigManagement::new(); | 238 | m.config_manager = MockConfigManagement::new(); |
| @@ -114,10 +241,91 @@ mod tests { | |||
| 114 | .returning(|| Ok(MyConfig::default())); | 241 | .returning(|| Ok(MyConfig::default())); |
| 115 | m.config_manager | 242 | m.config_manager |
| 116 | .expect_save() | 243 | .expect_save() |
| 117 | .withf(|cfg| cfg.users.len().eq(&1) && cfg.users[0].nsec.eq(TEST_KEY_1_NSEC)) | 244 | .withf(|cfg| { |
| 245 | cfg.users.len().eq(&1) | ||
| 246 | && cfg.users[0].encrypted_key.eq(TEST_KEY_1_ENCRYPTED) | ||
| 247 | }) | ||
| 118 | .returning(|_| Ok(())); | 248 | .returning(|_| Ok(())); |
| 119 | 249 | ||
| 120 | let _ = m.add(&None); | 250 | let _ = m.add(&None, &None); |
| 251 | } | ||
| 252 | |||
| 253 | #[test] | ||
| 254 | fn stored_key_encrypted_with_password() { | ||
| 255 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 256 | |||
| 257 | m.encryptor = MockEncryptDecrypt::new(); | ||
| 258 | m.encryptor | ||
| 259 | .expect_encrypt_key() | ||
| 260 | .once() | ||
| 261 | .withf(|k, p| { | ||
| 262 | k.eq(&Keys::from_sk_str(TEST_KEY_1_NSEC).unwrap()) && p.eq(TEST_PASSWORD) | ||
| 263 | }) | ||
| 264 | .returning(|_, _| Ok(TEST_KEY_1_ENCRYPTED.into())); | ||
| 265 | |||
| 266 | let _ = m.add(&None, &None); | ||
| 267 | } | ||
| 268 | |||
| 269 | mod when_user_key_already_stored { | ||
| 270 | use super::*; | ||
| 271 | use crate::config::UserRef; | ||
| 272 | |||
| 273 | /// key overwritten as password may have changed | ||
| 274 | #[test] | ||
| 275 | fn key_not_saved_as_duplicate_but_encrypted_key_overwritten() { | ||
| 276 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 277 | |||
| 278 | m.config_manager = MockConfigManagement::default(); | ||
| 279 | m.config_manager.expect_load().returning(|| { | ||
| 280 | Ok(MyConfig { | ||
| 281 | users: vec![UserRef { | ||
| 282 | public_key: TEST_KEY_1_KEYS.public_key(), | ||
| 283 | // different key to TEST_KEY_1_ENCYPTED | ||
| 284 | encrypted_key: TEST_KEY_2_ENCRYPTED.into(), | ||
| 285 | }], | ||
| 286 | ..MyConfig::default() | ||
| 287 | }) | ||
| 288 | }); | ||
| 289 | m.config_manager | ||
| 290 | .expect_save() | ||
| 291 | .withf(|cfg| { | ||
| 292 | cfg.users.len() == 1 | ||
| 293 | && cfg.users[0].encrypted_key.eq(TEST_KEY_1_ENCRYPTED) | ||
| 294 | }) | ||
| 295 | .returning(|_| Ok(())); | ||
| 296 | |||
| 297 | let _ = m.add(&None, &None); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | mod when_multiple_users_added { | ||
| 302 | use super::*; | ||
| 303 | |||
| 304 | #[test] | ||
| 305 | fn both_user_keys_are_stored() { | ||
| 306 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 307 | |||
| 308 | m.config_manager = MockConfigManagement::default(); | ||
| 309 | m.config_manager.expect_load().returning(|| { | ||
| 310 | Ok(MyConfig { | ||
| 311 | users: vec![UserRef { | ||
| 312 | public_key: TEST_KEY_2_KEYS.public_key(), | ||
| 313 | encrypted_key: TEST_KEY_2_ENCRYPTED.into(), | ||
| 314 | }], | ||
| 315 | ..MyConfig::default() | ||
| 316 | }) | ||
| 317 | }); | ||
| 318 | m.config_manager | ||
| 319 | .expect_save() | ||
| 320 | .withf(|cfg| { | ||
| 321 | cfg.users.len() == 2 | ||
| 322 | // latest user stored at end of array | ||
| 323 | && cfg.users[1].encrypted_key.eq(TEST_KEY_1_ENCRYPTED) | ||
| 324 | }) | ||
| 325 | .returning(|_| Ok(())); | ||
| 326 | |||
| 327 | let _ = m.add(&None, &None); | ||
| 328 | } | ||
| 121 | } | 329 | } |
| 122 | } | 330 | } |
| 123 | } | 331 | } |
diff --git a/src/login.rs b/src/login.rs index da19a75..a6ce76d 100644 --- a/src/login.rs +++ b/src/login.rs | |||
| @@ -1,16 +1,85 @@ | |||
| 1 | use anyhow::{Context, Result}; | 1 | use anyhow::{bail, Context, Result}; |
| 2 | use nostr::prelude::{FromSkStr, ToBech32}; | ||
| 3 | use zeroize::Zeroize; | ||
| 2 | 4 | ||
| 3 | use crate::{ | 5 | use crate::{ |
| 6 | cli_interactor::{Interactor, InteractorPrompt, PromptPasswordParms}, | ||
| 4 | config::{ConfigManagement, ConfigManager}, | 7 | config::{ConfigManagement, ConfigManager}, |
| 5 | key_handling::users::{UserManagement, UserManager}, | 8 | key_handling::{ |
| 9 | encryption::{EncryptDecrypt, Encryptor}, | ||
| 10 | users::{UserManagement, UserManager}, | ||
| 11 | }, | ||
| 6 | }; | 12 | }; |
| 7 | 13 | ||
| 8 | pub fn launch(nsec: &Option<String>) -> Result<()> { | 14 | /// handles the encrpytion and storage of key material |
| 15 | pub fn launch(nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys> { | ||
| 16 | // if nsec parameter | ||
| 17 | if let Some(nsec_unwrapped) = nsec { | ||
| 18 | // get key or fail without prompts | ||
| 19 | let key = nostr::Keys::from_sk_str(nsec_unwrapped).context("invalid nsec parameter")?; | ||
| 20 | println!( | ||
| 21 | "logged in as {}", | ||
| 22 | &key.public_key() | ||
| 23 | .to_bech32() | ||
| 24 | .context("public key should always produce bech32")? | ||
| 25 | ); | ||
| 26 | |||
| 27 | // if password, add user to enable password login in future | ||
| 28 | if password.is_some() { | ||
| 29 | UserManager::default() | ||
| 30 | .add(nsec, password) | ||
| 31 | .context("could not store identity")?; | ||
| 32 | } | ||
| 33 | return Ok(key); | ||
| 34 | } | ||
| 35 | |||
| 36 | // if encrypted nsec stored, attempt password | ||
| 9 | let cfg = ConfigManager | 37 | let cfg = ConfigManager |
| 10 | .load() | 38 | .load() |
| 11 | .context("failed to load application config")?; | 39 | .context("failed to load application config")?; |
| 12 | if !cfg.users.is_empty() { | 40 | let key = if let Some(user) = cfg.users.last() { |
| 13 | println!("logged in as {}", cfg.users[0].nsec); | 41 | let mut pass = if let Some(p) = password.clone() { |
| 14 | } | 42 | p |
| 15 | UserManager::default().add(nsec) | 43 | } else { |
| 44 | println!( | ||
| 45 | "login as {}", | ||
| 46 | &user | ||
| 47 | .public_key | ||
| 48 | .to_bech32() | ||
| 49 | .context("public key should always produce bech32")? | ||
| 50 | ); | ||
| 51 | Interactor::default() | ||
| 52 | .password(PromptPasswordParms::default().with_prompt("password")) | ||
| 53 | .context("failed to get password input from interactor.password")? | ||
| 54 | }; | ||
| 55 | |||
| 56 | let key_result = Encryptor | ||
| 57 | .decrypt_key(&user.encrypted_key, pass.as_str()) | ||
| 58 | .context("failed to decrypt key with provided password"); | ||
| 59 | pass.zeroize(); | ||
| 60 | |||
| 61 | key_result.context(format!( | ||
| 62 | "failed to log in as {}", | ||
| 63 | &user | ||
| 64 | .public_key | ||
| 65 | .to_bech32() | ||
| 66 | .context("public key should always produce bech32")? | ||
| 67 | ))? | ||
| 68 | } else { | ||
| 69 | // no nsec but password supplied | ||
| 70 | if password.is_some() { | ||
| 71 | bail!("no nsec available to decrypt with specified password"); | ||
| 72 | } | ||
| 73 | // otherwise add new user with nsec and password prompts | ||
| 74 | UserManager::default() | ||
| 75 | .add(nsec, password) | ||
| 76 | .context("failed to add user")? | ||
| 77 | }; | ||
| 78 | println!( | ||
| 79 | "logged in as {}", | ||
| 80 | &key.public_key() | ||
| 81 | .to_bech32() | ||
| 82 | .context("public key should always produce bech32")? | ||
| 83 | ); | ||
| 84 | Ok(key) | ||
| 16 | } | 85 | } |
diff --git a/src/main.rs b/src/main.rs index d16f1a3..e6eac32 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -17,8 +17,11 @@ pub struct Cli { | |||
| 17 | #[command(subcommand)] | 17 | #[command(subcommand)] |
| 18 | command: Commands, | 18 | command: Commands, |
| 19 | /// nsec or hex private key | 19 | /// nsec or hex private key |
| 20 | #[arg(short, long)] | 20 | #[arg(short, long, global = true)] |
| 21 | nsec: Option<String>, | 21 | nsec: Option<String>, |
| 22 | /// password to decrypt nsec | ||
| 23 | #[arg(short, long, global = true)] | ||
| 24 | password: Option<String>, | ||
| 22 | } | 25 | } |
| 23 | 26 | ||
| 24 | #[derive(Subcommand)] | 27 | #[derive(Subcommand)] |
diff --git a/src/sub_commands/login.rs b/src/sub_commands/login.rs index d61f578..5391024 100644 --- a/src/sub_commands/login.rs +++ b/src/sub_commands/login.rs | |||
| @@ -7,5 +7,6 @@ use crate::{login, Cli}; | |||
| 7 | pub struct SubCommandArgs; | 7 | pub struct SubCommandArgs; |
| 8 | 8 | ||
| 9 | pub fn launch(args: &Cli, _command_args: &SubCommandArgs) -> Result<()> { | 9 | pub fn launch(args: &Cli, _command_args: &SubCommandArgs) -> Result<()> { |
| 10 | login::launch(&args.nsec) | 10 | let _ = login::launch(&args.nsec, &args.password)?; |
| 11 | Ok(()) | ||
| 11 | } | 12 | } |
diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index e1f6090..1a39957 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml | |||
| @@ -8,5 +8,7 @@ anyhow = "1.0.75" | |||
| 8 | assert_cmd = "2.0.12" | 8 | assert_cmd = "2.0.12" |
| 9 | dialoguer = "0.10.4" | 9 | dialoguer = "0.10.4" |
| 10 | directories = "5.0.1" | 10 | directories = "5.0.1" |
| 11 | nostr = "0.23.0" | ||
| 12 | once_cell = "1.18.0" | ||
| 11 | rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } | 13 | rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } |
| 12 | strip-ansi-escapes = "0.2.0" | 14 | strip-ansi-escapes = "0.2.0" |
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 495e8d2..1a4231a 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs | |||
| @@ -3,14 +3,38 @@ use std::ffi::OsStr; | |||
| 3 | use anyhow::{ensure, Context, Result}; | 3 | use anyhow::{ensure, Context, Result}; |
| 4 | use dialoguer::theme::{ColorfulTheme, Theme}; | 4 | use dialoguer::theme::{ColorfulTheme, Theme}; |
| 5 | use directories::ProjectDirs; | 5 | use directories::ProjectDirs; |
| 6 | use nostr::{self, prelude::FromSkStr}; | ||
| 7 | use once_cell::sync::Lazy; | ||
| 6 | use rexpect::session::{Options, PtySession}; | 8 | use rexpect::session::{Options, PtySession}; |
| 7 | use strip_ansi_escapes::strip_str; | 9 | use strip_ansi_escapes::strip_str; |
| 8 | 10 | ||
| 9 | pub static TEST_KEY_1_NSEC: &str = | 11 | pub static TEST_KEY_1_NSEC: &str = |
| 10 | "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq"; | 12 | "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq"; |
| 13 | pub static TEST_KEY_1_SK_HEX: &str = | ||
| 14 | "08608a436aee4c07ea5c36f85cb17c58f52b3ad7094f9318cc777771f0bf218b"; | ||
| 15 | pub static TEST_KEY_1_NPUB: &str = | ||
| 16 | "npub175lyhnt6nn00qjw0v3navw9pxgv43txnku0tpxprl4h6mvpr6a5qlphudg"; | ||
| 17 | pub static TEST_KEY_1_DISPLAY_NAME: &str = "bob"; | ||
| 18 | pub static TEST_KEY_1_ENCRYPTED: &str = "ncryptsec1qyq607h3cykxc3f2a44u89cdk336fptccn3fm5pf3nmf93d3c86qpunc7r6klwcn6lyszjy72wxwqq9aljg4pm6atvjrds9e248yhv76xfnt464265kgnjsvg8rlg06wg4sp9uljzfpu8zuaztcvfn2j8ggdrg8mldh850cy75efsyqqansert9wqmn4e6khpgvfz7h5le9"; | ||
| 19 | pub static TEST_KEY_1_ENCRYPTED_WEAK: &str = "ncryptsec1qy8ke0tjqnn8wt3w6lnc86c27ry3qrptxctjfcgruryxy0at238kwyjwsswd7z88thysruzw3awlrsxjvw5uptcd7vt70ft9rtkx00m8cgy3khm4hxa5d2gfnc6athnfruy2eyl6pkas8k34jg85z7xjqqadzfzh9rp0fzxqtw0tvxksac3n8yc98uksvuf93e0lcvqy8j6"; | ||
| 20 | pub static TEST_KEY_1_KEYS: Lazy<nostr::Keys> = | ||
| 21 | Lazy::new(|| nostr::Keys::from_sk_str(TEST_KEY_1_NSEC).unwrap()); | ||
| 11 | 22 | ||
| 12 | pub static TEST_KEY_2_NSEC: &str = | 23 | pub static TEST_KEY_2_NSEC: &str = |
| 13 | "nsec1ypglg6nj6ep0g2qmyfqcv2al502gje3jvpwye6mthmkvj93tqkesknv6qm"; | 24 | "nsec1ypglg6nj6ep0g2qmyfqcv2al502gje3jvpwye6mthmkvj93tqkesknv6qm"; |
| 25 | pub static TEST_KEY_2_NPUB: &str = | ||
| 26 | "npub1h2yz2eh0798nh25hvypenrz995nla9dktfuk565ljf3ghnkhdljsul834e"; | ||
| 27 | |||
| 28 | pub static TEST_KEY_2_DISPLAY_NAME: &str = "carole"; | ||
| 29 | pub static TEST_KEY_2_ENCRYPTED: &str = "...2"; | ||
| 30 | pub static TEST_KEY_2_KEYS: Lazy<nostr::Keys> = | ||
| 31 | Lazy::new(|| nostr::Keys::from_sk_str(TEST_KEY_2_NSEC).unwrap()); | ||
| 32 | |||
| 33 | pub static TEST_INVALID_NSEC: &str = "nsec1ppsg5sm2aex"; | ||
| 34 | pub static TEST_PASSWORD: &str = "769dfd£pwega8SHGv3!#Bsfd5t"; | ||
| 35 | pub static TEST_INVALID_PASSWORD: &str = "INVALID769dfd£pwega8SHGv3!"; | ||
| 36 | pub static TEST_WEAK_PASSWORD: &str = "fhaiuhfwe"; | ||
| 37 | pub static TEST_RANDOM_TOKEN: &str = "lkjh2398HLKJ43hrweiJ6FaPfdssgtrg"; | ||
| 14 | 38 | ||
| 15 | /// wrapper for a cli testing tool - currently wraps rexpect and dialoguer | 39 | /// wrapper for a cli testing tool - currently wraps rexpect and dialoguer |
| 16 | /// | 40 | /// |
| @@ -41,6 +65,16 @@ impl CliTester { | |||
| 41 | i.prompt(true).context("initial input prompt")?; | 65 | i.prompt(true).context("initial input prompt")?; |
| 42 | Ok(i) | 66 | Ok(i) |
| 43 | } | 67 | } |
| 68 | |||
| 69 | pub fn expect_password(&mut self, prompt: &str) -> Result<CliTesterPasswordPrompt> { | ||
| 70 | let mut i = CliTesterPasswordPrompt { | ||
| 71 | tester: self, | ||
| 72 | prompt: prompt.to_string(), | ||
| 73 | confirmation_prompt: "".to_string(), | ||
| 74 | }; | ||
| 75 | i.prompt().context("initial password prompt")?; | ||
| 76 | Ok(i) | ||
| 77 | } | ||
| 44 | } | 78 | } |
| 45 | 79 | ||
| 46 | pub struct CliTesterInputPrompt<'a> { | 80 | pub struct CliTesterInputPrompt<'a> { |
| @@ -101,6 +135,70 @@ impl CliTesterInputPrompt<'_> { | |||
| 101 | } | 135 | } |
| 102 | } | 136 | } |
| 103 | 137 | ||
| 138 | pub struct CliTesterPasswordPrompt<'a> { | ||
| 139 | tester: &'a mut CliTester, | ||
| 140 | prompt: String, | ||
| 141 | confirmation_prompt: String, | ||
| 142 | } | ||
| 143 | |||
| 144 | impl CliTesterPasswordPrompt<'_> { | ||
| 145 | fn prompt(&mut self) -> Result<&mut Self> { | ||
| 146 | let p = match self.confirmation_prompt.is_empty() { | ||
| 147 | true => self.prompt.as_str(), | ||
| 148 | false => self.confirmation_prompt.as_str(), | ||
| 149 | }; | ||
| 150 | |||
| 151 | let mut s = String::new(); | ||
| 152 | self.tester | ||
| 153 | .formatter | ||
| 154 | .format_password_prompt(&mut s, p) | ||
| 155 | .expect("diagluer theme formatter should succeed"); | ||
| 156 | |||
| 157 | ensure!(s.contains(p), "dialoguer must be broken"); | ||
| 158 | |||
| 159 | self.tester | ||
| 160 | .expect(format!("\r{}", sanatize(s)).as_str()) | ||
| 161 | .context("expect password input prompt")?; | ||
| 162 | Ok(self) | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn with_confirmation(&mut self, prompt: &str) -> Result<&mut Self> { | ||
| 166 | self.confirmation_prompt = prompt.to_string(); | ||
| 167 | Ok(self) | ||
| 168 | } | ||
| 169 | |||
| 170 | pub fn succeeds_with(&mut self, password: &str) -> Result<&mut Self> { | ||
| 171 | self.tester.send_line(password)?; | ||
| 172 | |||
| 173 | self.tester | ||
| 174 | .expect("\r\n") | ||
| 175 | .context("expect new lines after password input")?; | ||
| 176 | |||
| 177 | if !self.confirmation_prompt.is_empty() { | ||
| 178 | self.prompt() | ||
| 179 | .context("expect password confirmation prompt")?; | ||
| 180 | self.tester.send_line(password)?; | ||
| 181 | self.tester | ||
| 182 | .expect("\r\n\r") | ||
| 183 | .context("expect new lines after password confirmation input")?; | ||
| 184 | } | ||
| 185 | |||
| 186 | let mut s = String::new(); | ||
| 187 | self.tester | ||
| 188 | .formatter | ||
| 189 | .format_password_prompt_selection(&mut s, self.prompt.as_str()) | ||
| 190 | .expect("diagluer theme formatter should succeed"); | ||
| 191 | |||
| 192 | ensure!(s.contains(self.prompt.as_str()), "dialoguer must be broken"); | ||
| 193 | |||
| 194 | self.tester | ||
| 195 | .expect(format!("\r{}\r\n", sanatize(s)).as_str()) | ||
| 196 | .context("expect password prompt success")?; | ||
| 197 | |||
| 198 | Ok(self) | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 104 | impl CliTester { | 202 | impl CliTester { |
| 105 | pub fn new<I, S>(args: I) -> Self | 203 | pub fn new<I, S>(args: I) -> Self |
| 106 | where | 204 | where |
| @@ -108,7 +206,17 @@ impl CliTester { | |||
| 108 | S: AsRef<OsStr>, | 206 | S: AsRef<OsStr>, |
| 109 | { | 207 | { |
| 110 | Self { | 208 | Self { |
| 111 | rexpect_session: rexpect_with(args).expect("rexpect to spawn new process"), | 209 | rexpect_session: rexpect_with(args, 2000).expect("rexpect to spawn new process"), |
| 210 | formatter: ColorfulTheme::default(), | ||
| 211 | } | ||
| 212 | } | ||
| 213 | pub fn new_with_timeout<I, S>(timeout_ms: u64, args: I) -> Self | ||
| 214 | where | ||
| 215 | I: IntoIterator<Item = S>, | ||
| 216 | S: AsRef<OsStr>, | ||
| 217 | { | ||
| 218 | Self { | ||
| 219 | rexpect_session: rexpect_with(args, timeout_ms).expect("rexpect to spawn new process"), | ||
| 112 | formatter: ColorfulTheme::default(), | 220 | formatter: ColorfulTheme::default(), |
| 113 | } | 221 | } |
| 114 | } | 222 | } |
| @@ -122,7 +230,7 @@ impl CliTester { | |||
| 122 | .process | 230 | .process |
| 123 | .exit() | 231 | .exit() |
| 124 | .expect("process to exit"); | 232 | .expect("process to exit"); |
| 125 | self.rexpect_session = rexpect_with(args).expect("rexpect to spawn new process"); | 233 | self.rexpect_session = rexpect_with(args, 2000).expect("rexpect to spawn new process"); |
| 126 | self | 234 | self |
| 127 | } | 235 | } |
| 128 | 236 | ||
| @@ -213,7 +321,7 @@ fn sanatize(s: String) -> String { | |||
| 213 | .collect::<String>() | 321 | .collect::<String>() |
| 214 | } | 322 | } |
| 215 | 323 | ||
| 216 | pub fn rexpect_with<I, S>(args: I) -> Result<PtySession, rexpect::error::Error> | 324 | pub fn rexpect_with<I, S>(args: I, timeout_ms: u64) -> Result<PtySession, rexpect::error::Error> |
| 217 | where | 325 | where |
| 218 | I: IntoIterator<Item = S>, | 326 | I: IntoIterator<Item = S>, |
| 219 | S: AsRef<std::ffi::OsStr>, | 327 | S: AsRef<std::ffi::OsStr>, |
| @@ -224,7 +332,7 @@ where | |||
| 224 | rexpect::session::spawn_with_options( | 332 | rexpect::session::spawn_with_options( |
| 225 | cmd, | 333 | cmd, |
| 226 | Options { | 334 | Options { |
| 227 | timeout_ms: Some(2000), | 335 | timeout_ms: Some(timeout_ms), |
| 228 | strip_ansi_escape_codes: true, | 336 | strip_ansi_escape_codes: true, |
| 229 | }, | 337 | }, |
| 230 | ) | 338 | ) |
diff --git a/tests/login.rs b/tests/login.rs index a7e1889..a75608d 100644 --- a/tests/login.rs +++ b/tests/login.rs | |||
| @@ -3,6 +3,9 @@ use serial_test::serial; | |||
| 3 | use test_utils::*; | 3 | use test_utils::*; |
| 4 | 4 | ||
| 5 | static EXPECTED_NSEC_PROMPT: &str = "login with nsec (or hex private key)"; | 5 | static EXPECTED_NSEC_PROMPT: &str = "login with nsec (or hex private key)"; |
| 6 | static EXPECTED_SET_PASSWORD_PROMPT: &str = "encrypt with password"; | ||
| 7 | static EXPECTED_SET_PASSWORD_CONFIRM_PROMPT: &str = "confirm password"; | ||
| 8 | static EXPECTED_PASSWORD_PROMPT: &str = "password"; | ||
| 6 | 9 | ||
| 7 | fn standard_login() -> Result<CliTester> { | 10 | fn standard_login() -> Result<CliTester> { |
| 8 | let mut p = CliTester::new(["login"]); | 11 | let mut p = CliTester::new(["login"]); |
| @@ -10,6 +13,10 @@ fn standard_login() -> Result<CliTester> { | |||
| 10 | p.expect_input_eventually(EXPECTED_NSEC_PROMPT)? | 13 | p.expect_input_eventually(EXPECTED_NSEC_PROMPT)? |
| 11 | .succeeds_with(TEST_KEY_1_NSEC)?; | 14 | .succeeds_with(TEST_KEY_1_NSEC)?; |
| 12 | 15 | ||
| 16 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 17 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 18 | .succeeds_with(TEST_PASSWORD)?; | ||
| 19 | |||
| 13 | p.expect_end_eventually()?; | 20 | p.expect_end_eventually()?; |
| 14 | Ok(p) | 21 | Ok(p) |
| 15 | } | 22 | } |
| @@ -19,11 +26,10 @@ mod when_first_time_login { | |||
| 19 | 26 | ||
| 20 | #[test] | 27 | #[test] |
| 21 | #[serial] | 28 | #[serial] |
| 22 | fn prompts_for_nsec() -> Result<()> { | 29 | fn prompts_for_nsec_and_password() -> Result<()> { |
| 23 | with_fresh_config(|| { | 30 | before()?; |
| 24 | standard_login()?; | 31 | standard_login()?; |
| 25 | Ok(()) | 32 | after() |
| 26 | }) | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | #[test] | 35 | #[test] |
| @@ -35,36 +41,137 @@ mod when_first_time_login { | |||
| 35 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 41 | p.expect_input(EXPECTED_NSEC_PROMPT)? |
| 36 | .succeeds_with(TEST_KEY_1_NSEC)?; | 42 | .succeeds_with(TEST_KEY_1_NSEC)?; |
| 37 | 43 | ||
| 38 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str()) | 44 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? |
| 45 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 46 | .succeeds_with(TEST_PASSWORD)?; | ||
| 47 | |||
| 48 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 39 | }) | 49 | }) |
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | #[test] | 52 | #[test] |
| 43 | #[serial] | 53 | #[serial] |
| 44 | fn next_time_returns_logged_in_as_npub() -> Result<()> { | 54 | fn succeeds_with_hex_secret_key_in_place_of_nsec() -> Result<()> { |
| 55 | with_fresh_config(|| { | ||
| 56 | let mut p = CliTester::new(["login"]); | ||
| 57 | |||
| 58 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 59 | .succeeds_with(TEST_KEY_1_SK_HEX)?; | ||
| 60 | |||
| 61 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 62 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 63 | .succeeds_with(TEST_PASSWORD)?; | ||
| 64 | |||
| 65 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 66 | }) | ||
| 67 | } | ||
| 68 | |||
| 69 | mod when_invalid_nsec { | ||
| 70 | use super::*; | ||
| 71 | |||
| 72 | #[test] | ||
| 73 | #[serial] | ||
| 74 | fn prompts_for_nsec_until_valid() -> Result<()> { | ||
| 75 | with_fresh_config(|| { | ||
| 76 | let invalid_nsec_response = | ||
| 77 | "invalid nsec. try again with nsec (or hex private key)"; | ||
| 78 | |||
| 79 | let mut p = CliTester::new(["login"]); | ||
| 80 | |||
| 81 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 82 | // this behaviour is intentional. rejecting the response with dialoguer hides | ||
| 83 | // the original input from the user so they cannot see the | ||
| 84 | // mistake they made. | ||
| 85 | .succeeds_with(TEST_INVALID_NSEC)?; | ||
| 86 | |||
| 87 | p.expect_input(invalid_nsec_response)? | ||
| 88 | .succeeds_with(TEST_INVALID_NSEC)?; | ||
| 89 | |||
| 90 | p.expect_input(invalid_nsec_response)? | ||
| 91 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 92 | |||
| 93 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 94 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 95 | .succeeds_with(TEST_PASSWORD)?; | ||
| 96 | |||
| 97 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 98 | }) | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | mod when_second_time_login { | ||
| 104 | use super::*; | ||
| 105 | |||
| 106 | #[test] | ||
| 107 | #[serial] | ||
| 108 | fn prints_login_as_npub() -> Result<()> { | ||
| 45 | with_fresh_config(|| { | 109 | with_fresh_config(|| { |
| 46 | standard_login()?.exit()?; | 110 | standard_login()?.exit()?; |
| 47 | 111 | ||
| 48 | CliTester::new(["login"]) | 112 | CliTester::new(["login"]) |
| 49 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 113 | .expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? |
| 50 | .exit() | 114 | .exit() |
| 51 | }) | 115 | }) |
| 52 | } | 116 | } |
| 117 | |||
| 118 | #[test] | ||
| 119 | #[serial] | ||
| 120 | fn prompts_for_password_and_succeeds_with_logged_in_as_npub() -> Result<()> { | ||
| 121 | with_fresh_config(|| { | ||
| 122 | standard_login()?.exit()?; | ||
| 123 | |||
| 124 | let mut p = CliTester::new(["login"]); | ||
| 125 | |||
| 126 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 127 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 128 | .succeeds_with(TEST_PASSWORD)?; | ||
| 129 | |||
| 130 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 131 | }) | ||
| 132 | } | ||
| 133 | |||
| 134 | #[test] | ||
| 135 | #[serial] | ||
| 136 | fn when_invalid_password_exit_with_error() -> Result<()> { | ||
| 137 | with_fresh_config(|| { | ||
| 138 | standard_login()?.exit()?; | ||
| 139 | |||
| 140 | let mut p = CliTester::new(["login"]); | ||
| 141 | |||
| 142 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 143 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 144 | .succeeds_with(TEST_INVALID_PASSWORD)?; | ||
| 145 | p.expect_end_with(format!("Error: failed to log in as {}\r\n\r\nCaused by:\r\n 0: failed to decrypt key with provided password\r\n 1: failed to decrypt\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 146 | }) | ||
| 147 | } | ||
| 53 | } | 148 | } |
| 54 | 149 | ||
| 55 | mod when_called_with_nsec_parameter { | 150 | mod when_called_with_nsec_parameter_only { |
| 56 | use super::*; | 151 | use super::*; |
| 57 | 152 | ||
| 58 | #[test] | 153 | #[test] |
| 59 | #[serial] | 154 | #[serial] |
| 60 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { | 155 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { |
| 61 | with_fresh_config(|| { | 156 | with_fresh_config(|| { |
| 62 | CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) | 157 | CliTester::new(["login", "--nsec", TEST_KEY_1_NSEC]) |
| 63 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | 158 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) |
| 159 | }) | ||
| 160 | } | ||
| 161 | |||
| 162 | #[test] | ||
| 163 | #[serial] | ||
| 164 | fn forgets_identity() -> Result<()> { | ||
| 165 | with_fresh_config(|| { | ||
| 166 | CliTester::new(["login", "--nsec", TEST_KEY_1_NSEC]) | ||
| 167 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 64 | 168 | ||
| 65 | CliTester::new(["login"]) | 169 | let mut p = CliTester::new(["login"]); |
| 66 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | 170 | |
| 67 | .exit() | 171 | p.expect_input(EXPECTED_NSEC_PROMPT)? |
| 172 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 173 | |||
| 174 | p.exit() | ||
| 68 | }) | 175 | }) |
| 69 | } | 176 | } |
| 70 | 177 | ||
| @@ -77,69 +184,212 @@ mod when_called_with_nsec_parameter { | |||
| 77 | with_fresh_config(|| { | 184 | with_fresh_config(|| { |
| 78 | standard_login()?.exit()?; | 185 | standard_login()?.exit()?; |
| 79 | 186 | ||
| 80 | CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) | 187 | CliTester::new(["login", "--nsec", TEST_KEY_2_NSEC]) |
| 81 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 188 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str()) |
| 82 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | ||
| 83 | |||
| 84 | CliTester::new(["login"]) | ||
| 85 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | ||
| 86 | .exit() | ||
| 87 | }) | 189 | }) |
| 88 | } | 190 | } |
| 89 | } | 191 | } |
| 192 | #[test] | ||
| 193 | #[serial] | ||
| 194 | fn invalid_nsec_param_fails_without_prompts() -> Result<()> { | ||
| 195 | with_fresh_config(|| { | ||
| 196 | CliTester::new(["login", "--nsec", TEST_INVALID_NSEC]).expect_end_with( | ||
| 197 | "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n", | ||
| 198 | ) | ||
| 199 | }) | ||
| 200 | } | ||
| 90 | } | 201 | } |
| 91 | 202 | ||
| 92 | mod when_logged_in { | 203 | mod when_called_with_nsec_and_password_parameter { |
| 93 | use super::*; | 204 | use super::*; |
| 94 | 205 | ||
| 95 | #[test] | 206 | #[test] |
| 96 | #[serial] | 207 | #[serial] |
| 97 | fn returns_logged_in_as_npub() -> Result<()> { | 208 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { |
| 98 | with_fresh_config(|| { | 209 | with_fresh_config(|| { |
| 99 | standard_login()?.exit()?; | 210 | CliTester::new([ |
| 211 | "login", | ||
| 212 | "--nsec", | ||
| 213 | TEST_KEY_1_NSEC, | ||
| 214 | "--password", | ||
| 215 | TEST_PASSWORD, | ||
| 216 | ]) | ||
| 217 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 218 | }) | ||
| 219 | } | ||
| 220 | |||
| 221 | #[test] | ||
| 222 | #[serial] | ||
| 223 | fn remembers_identity() -> Result<()> { | ||
| 224 | with_fresh_config(|| { | ||
| 225 | CliTester::new([ | ||
| 226 | "login", | ||
| 227 | "--nsec", | ||
| 228 | TEST_KEY_1_NSEC, | ||
| 229 | "--password", | ||
| 230 | TEST_PASSWORD, | ||
| 231 | ]) | ||
| 232 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 100 | 233 | ||
| 101 | CliTester::new(["login"]) | 234 | CliTester::new(["login"]) |
| 102 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 235 | .expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? |
| 103 | .exit() | 236 | .exit() |
| 104 | }) | 237 | }) |
| 105 | } | 238 | } |
| 106 | 239 | ||
| 107 | #[test] | 240 | #[test] |
| 108 | #[serial] | 241 | #[serial] |
| 109 | fn prompts_to_log_in_with_different_nsec() -> Result<()> { | 242 | fn parameters_can_be_called_globally() -> Result<()> { |
| 110 | with_fresh_config(|| { | 243 | with_fresh_config(|| { |
| 111 | standard_login()?.exit()?; | 244 | CliTester::new([ |
| 112 | 245 | "--nsec", | |
| 113 | let mut p = CliTester::new(["login"]); | 246 | TEST_KEY_1_NSEC, |
| 114 | p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; | 247 | "--password", |
| 115 | 248 | TEST_PASSWORD, | |
| 116 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 249 | "login", |
| 117 | .succeeds_with(TEST_KEY_2_NSEC)?; | 250 | ]) |
| 118 | 251 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | |
| 119 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str()) | ||
| 120 | }) | 252 | }) |
| 121 | } | 253 | } |
| 254 | |||
| 122 | mod when_logging_in_as_different_nsec { | 255 | mod when_logging_in_as_different_nsec { |
| 123 | use super::*; | 256 | use super::*; |
| 124 | 257 | ||
| 125 | #[test] | 258 | #[test] |
| 126 | #[serial] | 259 | #[serial] |
| 127 | fn confirmed_as_logged_in_as_additional_user() -> Result<()> { | 260 | fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> { |
| 128 | with_fresh_config(|| { | 261 | with_fresh_config(|| { |
| 129 | standard_login()?.exit()?; | 262 | standard_login()?.exit()?; |
| 130 | 263 | ||
| 131 | let mut p = CliTester::new(["login"]); | 264 | CliTester::new([ |
| 132 | p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; | 265 | "login", |
| 266 | "--nsec", | ||
| 267 | TEST_KEY_2_NSEC, | ||
| 268 | "--password", | ||
| 269 | TEST_PASSWORD, | ||
| 270 | ]) | ||
| 271 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str()) | ||
| 272 | }) | ||
| 273 | } | ||
| 133 | 274 | ||
| 134 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 275 | #[test] |
| 135 | .succeeds_with(TEST_KEY_2_NSEC)?; | 276 | #[serial] |
| 277 | fn remembers_identity() -> Result<()> { | ||
| 278 | with_fresh_config(|| { | ||
| 279 | standard_login()?.exit()?; | ||
| 136 | 280 | ||
| 137 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | 281 | CliTester::new([ |
| 282 | "login", | ||
| 283 | "--nsec", | ||
| 284 | TEST_KEY_2_NSEC, | ||
| 285 | "--password", | ||
| 286 | TEST_PASSWORD, | ||
| 287 | ]) | ||
| 288 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())?; | ||
| 138 | 289 | ||
| 139 | CliTester::new(["login"]) | 290 | CliTester::new(["login"]) |
| 140 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | 291 | .expect(format!("login as {}\r\n", TEST_KEY_2_NPUB).as_str())? |
| 141 | .exit() | 292 | .exit() |
| 142 | }) | 293 | }) |
| 143 | } | 294 | } |
| 144 | } | 295 | } |
| 296 | |||
| 297 | mod when_provided_with_new_password { | ||
| 298 | use super::*; | ||
| 299 | |||
| 300 | #[test] | ||
| 301 | #[serial] | ||
| 302 | fn password_changes() -> Result<()> { | ||
| 303 | with_fresh_config(|| { | ||
| 304 | standard_login()?.exit()?; | ||
| 305 | |||
| 306 | CliTester::new([ | ||
| 307 | "login", | ||
| 308 | "--nsec", | ||
| 309 | TEST_KEY_1_NSEC, | ||
| 310 | "--password", | ||
| 311 | TEST_INVALID_PASSWORD, | ||
| 312 | ]) | ||
| 313 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 314 | |||
| 315 | CliTester::new(["--password", TEST_INVALID_PASSWORD, "login"]) | ||
| 316 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 317 | }) | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | #[test] | ||
| 322 | #[serial] | ||
| 323 | fn invalid_nsec_param_fails_without_prompts() -> Result<()> { | ||
| 324 | with_fresh_config(|| { | ||
| 325 | CliTester::new([ | ||
| 326 | "login", | ||
| 327 | "--nsec", | ||
| 328 | TEST_INVALID_NSEC, | ||
| 329 | "--password", | ||
| 330 | TEST_PASSWORD, | ||
| 331 | ]) | ||
| 332 | .expect_end_with( | ||
| 333 | "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n", | ||
| 334 | ) | ||
| 335 | }) | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | mod when_called_with_password_parameter_only { | ||
| 340 | use super::*; | ||
| 341 | |||
| 342 | #[test] | ||
| 343 | #[serial] | ||
| 344 | fn when_nsec_stored_logs_in_without_prompts() -> Result<()> { | ||
| 345 | with_fresh_config(|| { | ||
| 346 | standard_login()?.exit()?; | ||
| 347 | |||
| 348 | CliTester::new(["login", "--password", TEST_PASSWORD]) | ||
| 349 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 350 | }) | ||
| 351 | } | ||
| 352 | |||
| 353 | #[test] | ||
| 354 | #[serial] | ||
| 355 | fn when_no_nsec_stored_logs_error() -> Result<()> { | ||
| 356 | with_fresh_config(|| { | ||
| 357 | CliTester::new(["login", "--password", TEST_PASSWORD]) | ||
| 358 | .expect_end_with("Error: no nsec available to decrypt with specified password\r\n") | ||
| 359 | }) | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | mod when_weak_password { | ||
| 364 | use super::*; | ||
| 365 | |||
| 366 | #[test] | ||
| 367 | #[serial] | ||
| 368 | // combined into a single test as it is computationally expensive to run | ||
| 369 | fn warns_it_might_take_a_few_seconds_then_succeeds_then_second_login_prompts_for_password_then_warns_again_then_succeeds() | ||
| 370 | -> Result<()> { | ||
| 371 | with_fresh_config(|| { | ||
| 372 | let mut p = CliTester::new_with_timeout(10000, ["login"]); | ||
| 373 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 374 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 375 | |||
| 376 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 377 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 378 | .succeeds_with(TEST_WEAK_PASSWORD)?; | ||
| 379 | |||
| 380 | p.expect("this may take a few seconds...\r\n")?; | ||
| 381 | |||
| 382 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 383 | |||
| 384 | p = CliTester::new_with_timeout(10000, ["login"]); | ||
| 385 | |||
| 386 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 387 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 388 | .succeeds_with(TEST_WEAK_PASSWORD)?; | ||
| 389 | |||
| 390 | p.expect("this may take a few seconds...\r\n")?; | ||
| 391 | |||
| 392 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 393 | }) | ||
| 394 | } | ||
| 145 | } | 395 | } |