diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | Cargo.lock | 449 | ||||
-rw-r--r-- | Cargo.toml | 18 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rwxr-xr-x | bin/rbw-fzf | 2 | ||||
-rw-r--r-- | src/actions.rs | 33 | ||||
-rw-r--r-- | src/api.rs | 76 | ||||
-rw-r--r-- | src/bin/rbw-agent/actions.rs | 109 | ||||
-rw-r--r-- | src/bin/rbw-agent/agent.rs | 8 | ||||
-rw-r--r-- | src/bin/rbw/actions.rs | 4 | ||||
-rw-r--r-- | src/bin/rbw/commands.rs | 47 | ||||
-rw-r--r-- | src/bin/rbw/main.rs | 33 | ||||
-rw-r--r-- | src/cipherstring.rs | 116 | ||||
-rw-r--r-- | src/config.rs | 17 | ||||
-rw-r--r-- | src/edit.rs | 2 | ||||
-rw-r--r-- | src/error.rs | 9 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/locked.rs | 37 | ||||
-rw-r--r-- | src/pinentry.rs | 10 | ||||
-rw-r--r-- | src/protocol.rs | 1 |
20 files changed, 625 insertions, 362 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6fd96..e34f769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.4.0] - 2021-10-27 + +### Fixed + +* Add `rbw register` to allow `rbw` to work with the official Bitwarden server + again - see the README for details (#71) + ## [1.3.0] - 2021-07-05 ### Changed @@ -1,10 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aes" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495ee669413bfbe9e8cace80f4d3d78e6d8c8d99579f97fb93bde351b185f2d4" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if", "cipher", @@ -32,9 +34,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" +checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" [[package]] name = "arrayvec" @@ -44,9 +46,9 @@ checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" [[package]] name = "async-trait" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" dependencies = [ "proc-macro2", "quote", @@ -90,15 +92,15 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d27fb6b6f1e43147af148af49d49329413ba781aa0d5e10979831c210173b5" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" @@ -133,9 +135,9 @@ checksum = "5988cb1d626264ac94100be357308f29ff7cbdd3b36bda27f450a4ee3f713426" [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "byteorder" @@ -145,15 +147,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cc" -version = "1.0.68" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" [[package]] name = "cfg-if" @@ -162,16 +164,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] name = "cipher" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -197,10 +189,16 @@ dependencies = [ ] [[package]] +name = "const-oid" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" + +[[package]] name = "core-foundation" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" dependencies = [ "core-foundation-sys", "libc", @@ -208,24 +206,35 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.1.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ "libc", ] [[package]] +name = "crypto-bigint" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" +dependencies = [ + "generic-array", + "rand_core", + "subtle", +] + +[[package]] name = "crypto-mac" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ "generic-array", "subtle", @@ -242,6 +251,16 @@ dependencies = [ ] [[package]] +name = "der" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2" +dependencies = [ + "const-oid", + "crypto-bigint", +] + +[[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -252,9 +271,9 @@ dependencies = [ [[package]] name = "directories" -version = "3.0.2" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ "dirs-sys", ] @@ -272,18 +291,18 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" dependencies = [ "cfg-if", ] [[package]] name = "env_logger" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" dependencies = [ "atty", "humantime", @@ -310,30 +329,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" [[package]] name = "futures-io" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" [[package]] name = "futures-macro" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" dependencies = [ "autocfg 1.0.1", "proc-macro-hack", @@ -344,21 +363,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" [[package]] name = "futures-task" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" [[package]] name = "futures-util" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" dependencies = [ "autocfg 1.0.1", "futures-core", @@ -396,9 +415,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" +checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" dependencies = [ "bytes", "fnv", @@ -459,9 +478,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" dependencies = [ "bytes", "fnv", @@ -470,9 +489,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes", "http", @@ -481,9 +500,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] name = "httpdate" @@ -499,9 +518,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.9" +version = "0.14.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07d6baa1b441335f3ce5098ac421fb6547c46dda735ca1bc6d0153c838f9dd83" +checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" dependencies = [ "bytes", "futures-channel", @@ -559,9 +578,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] @@ -574,15 +593,15 @@ checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "js-sys" -version = "0.3.51" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" dependencies = [ "wasm-bindgen", ] @@ -598,9 +617,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.97" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" [[package]] name = "libm" @@ -610,9 +629,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] @@ -637,15 +656,15 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memoffset" @@ -664,9 +683,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -686,9 +705,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3728fec49d363a50a8828a190b379a446cc5cf085c06259bbbeb34447e4ec7" +checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" dependencies = [ "bitflags", "cc", @@ -707,17 +726,6 @@ dependencies = [ ] [[package]] -name = "num-bigint" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" -dependencies = [ - "autocfg 1.0.1", - "num-integer", - "num-traits", -] - -[[package]] name = "num-bigint-dig" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -796,9 +804,9 @@ checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -807,9 +815,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if", "instant", @@ -821,9 +829,9 @@ dependencies = [ [[package]] name = "password-hash" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a5d4e9c205d2c1ae73b84aab6240e98218c0e72e63b50422cfb2d1ca952282" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" dependencies = [ "base64ct", "rand_core", @@ -859,11 +867,10 @@ checksum = "7f0b59668fe80c5afe998f0c0bf93322bf2cd66cafeeb80581f291716f3467f2" [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ - "base64ct", "crypto-mac", "hmac", "password-hash", @@ -871,14 +878,12 @@ dependencies = [ ] [[package]] -name = "pem" -version = "0.8.3" +name = "pem-rfc7468" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +checksum = "8f22eb0e3c593294a99e9ff4b24cf6b752d43f193aa4415fe5077c159996d497" dependencies = [ - "base64", - "once_cell", - "regex", + "base64ct", ] [[package]] @@ -900,10 +905,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] +name = "pkcs1" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "116bee8279d783c0cf370efa1a94632f2108e5ef0bb32df31f051647810a4e2c" +dependencies = [ + "der", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +dependencies = [ + "der", + "pem-rfc7468", + "pkcs1", + "spki", + "zeroize", +] + +[[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "proc-macro-error" @@ -943,18 +972,18 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1001,7 +1030,7 @@ dependencies = [ [[package]] name = "rbw" -version = "1.3.0" +version = "1.4.0" dependencies = [ "aes", "anyhow", @@ -1047,9 +1076,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] @@ -1083,9 +1112,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "region" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ "bitflags", "libc", @@ -1104,9 +1133,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" +checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" dependencies = [ "base64", "bytes", @@ -1155,9 +1184,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28" +checksum = "e05c2603e2823634ab331437001b411b9ed11660fbc4066f3908c84a9439260d" dependencies = [ "byteorder", "digest", @@ -1166,9 +1195,9 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "pem", + "pkcs1", + "pkcs8", "rand", - "simple_asn1", "subtle", "zeroize", ] @@ -1232,9 +1261,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.3.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" dependencies = [ "bitflags", "core-foundation", @@ -1245,9 +1274,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.3.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" dependencies = [ "core-foundation-sys", "libc", @@ -1255,18 +1284,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.126" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -1275,9 +1304,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" dependencies = [ "itoa", "ryu", @@ -1286,9 +1315,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f6109f0506e20f7e0f910e51a0079acf41da8e0694e6442527c4ddf5a2b158" +checksum = "d0421d4f173fab82d72d6babf36d57fae38b994ca5c2d78e704260ba6d12118b" dependencies = [ "serde", ] @@ -1318,9 +1347,9 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer", "cfg-if", @@ -1331,9 +1360,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", "cfg-if", @@ -1352,34 +1381,22 @@ dependencies = [ ] [[package]] -name = "simple_asn1" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" -dependencies = [ - "chrono", - "num-bigint", - "num-traits", - "thiserror", -] - -[[package]] name = "slab" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "smallvec" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "socket2" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" dependencies = [ "libc", "winapi", @@ -1392,6 +1409,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] +name = "spki" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +dependencies = [ + "der", +] + +[[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1399,9 +1425,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.22" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" dependencies = [ "clap", "lazy_static", @@ -1411,9 +1437,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.15" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", @@ -1424,15 +1450,15 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.73" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ "proc-macro2", "quote", @@ -1441,9 +1467,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", @@ -1496,18 +1522,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -1516,9 +1542,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" dependencies = [ "tinyvec_macros", ] @@ -1531,9 +1557,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570c2eb13b3ab38208130eccd41be92520388791207fde783bda7c1e8ace28d4" +checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" dependencies = [ "autocfg 1.0.1", "bytes", @@ -1551,9 +1577,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" +checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd" dependencies = [ "proc-macro2", "quote", @@ -1573,9 +1599,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" +checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" dependencies = [ "bytes", "futures-core", @@ -1605,9 +1631,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.26" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if", "pin-project-lite", @@ -1616,9 +1642,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.18" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" dependencies = [ "lazy_static", ] @@ -1631,18 +1657,15 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "typenum" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "unicode-bidi" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" [[package]] name = "unicode-normalization" @@ -1661,9 +1684,9 @@ checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" @@ -1728,21 +1751,19 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.74" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ "cfg-if", - "serde", - "serde_json", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.74" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", @@ -1755,9 +1776,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.24" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" dependencies = [ "cfg-if", "js-sys", @@ -1767,9 +1788,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.74" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1777,9 +1798,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.74" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ "proc-macro2", "quote", @@ -1790,15 +1811,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.74" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" [[package]] name = "web-sys" -version = "0.3.51" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" dependencies = [ "js-sys", "wasm-bindgen", @@ -1856,18 +1877,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.3.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1" +checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" dependencies = [ "proc-macro2", "quote", @@ -1,6 +1,6 @@ [package] name = "rbw" -version = "1.3.0" +version = "1.4.0" authors = ["Jesse Luehrs <doy@tozt.net>"] edition = "2018" @@ -21,21 +21,21 @@ base64 = "0.13" block-modes = "0.8" block-padding = "0.2" daemonize = "0.4" -directories = "3.0" -env_logger = "0.8" +directories = "4.0" +env_logger = "0.9" hkdf = "0.11" hmac = { version = "0.11", features = ["std"] } humantime = "2.1" libc = "0.2" log = "0.4" -nix = "0.21" +nix = "0.23" paw = "1.0" -pbkdf2 = "0.8" +pbkdf2 = "0.9" percent-encoding = "2.1" rand = "0.8" -region = "2.2" +region = "3.0" reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls-native-roots"] } -rsa = "0.4" +rsa = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_path_to_error = "0.1" @@ -47,11 +47,11 @@ tempfile = "3.2" term_size = "0.3" textwrap = "0.11" thiserror = "1.0" -tokio = { version = "1.8", features = ["full"] } +tokio = { version = "1.12", features = ["full"] } totp-lite = "1.0" url = "2.2" uuid = { version = "0.8", features = ["v4"] } -zeroize = "1.3" +zeroize = "1.4" [package.metadata.deb] depends = "pinentry" @@ -81,6 +81,14 @@ out by running `rbw purge`, and you can explicitly lock the database by running `rbw help` can be used to get more information about the available functionality. +*Note to users of the official Bitwarden server (at bitwarden.com)*: The +official server has a tendency to detect command line traffic as bot traffic +(see [this issue](https://github.com/bitwarden/cli/issues/383) for details). In +order to use `rbw` with the official Bitwarden server, you will need to first +run `rbw register` to register each device using `rbw` with the Bitwarden +server. This will prompt you for your personal API key which you can find using +the instructions [here](https://bitwarden.com/help/article/personal-api-key/). + ## Related projects * [rofi-rbw](https://github.com/fdw/rofi-rbw): A rofi frontend for Bitwarden diff --git a/bin/rbw-fzf b/bin/rbw-fzf index dc0ba9c..cbf15c4 100755 --- a/bin/rbw-fzf +++ b/bin/rbw-fzf @@ -2,4 +2,4 @@ set -eu set -o pipefail -rbw ls --fields name,user,folder | perl -plE'/^([^\t]*)\t([^\t]*)\t([^\t]*)$/; $_ = join("/", grep { length } ($3, $1, $2)) . "\t$_"' | sort | fzf --with-nth=1 | perl -ple'/^([^\t]*)\t([^\t]*)\t([^\t]*)\t([^\t]*)$/; $_ = "$2 $3"' | xargs -r rbw get +rbw ls --fields name,user,folder | perl -plE'/^([^\t]*)\t([^\t]*)\t([^\t]*)$/; $_ = join("/", grep { length } ($3, $1, $2)) . "\0$_"' | sort | fzf --with-nth=1 -d '\x00' | perl -ple'/^([^\0]*)\0([^\t]*)\t([^\t]*)\t([^\t]*)$/; $_ = "$2 $3"; $_ .= " --folder=\"$4\"" if length $4' | xargs -r rbw get diff --git a/src/actions.rs b/src/actions.rs index 0d8c88d..02ec854 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,37 +1,42 @@ use crate::prelude::*; +pub async fn register( + email: &str, + apikey: crate::locked::ApiKey, +) -> Result<()> { + let config = crate::config::Config::load_async().await?; + let client = + crate::api::Client::new(&config.base_url(), &config.identity_url()); + + client.register(email, &config.device_id, &apikey).await?; + + Ok(()) +} + pub async fn login( email: &str, - password: &crate::locked::Password, + password: crate::locked::Password, two_factor_token: Option<&str>, two_factor_provider: Option<crate::api::TwoFactorProviderType>, -) -> Result<(String, String, u32, String, crate::locked::Keys)> { +) -> Result<(String, String, u32, String)> { let config = crate::config::Config::load_async().await?; let client = crate::api::Client::new(&config.base_url(), &config.identity_url()); let iterations = client.prelogin(email).await?; let identity = - crate::identity::Identity::new(email, password, iterations)?; - + crate::identity::Identity::new(email, &password, iterations)?; let (access_token, refresh_token, protected_key) = client .login( - &identity.email, + email, + &config.device_id, &identity.master_password_hash, two_factor_token, two_factor_provider, ) .await?; - let master_keys = crate::cipherstring::CipherString::new(&protected_key)? - .decrypt_locked_symmetric(&identity.keys)?; - Ok(( - access_token, - refresh_token, - iterations, - protected_key, - crate::locked::Keys::new(master_keys), - )) + Ok((access_token, refresh_token, iterations, protected_key)) } pub async fn unlock( @@ -148,9 +148,10 @@ struct PreloginRes { struct ConnectPasswordReq { grant_type: String, username: String, - password: String, + password: Option<String>, scope: String, client_id: String, + client_secret: Option<String>, #[serde(rename = "deviceType")] device_type: u32, #[serde(rename = "deviceIdentifier")] @@ -178,7 +179,7 @@ struct ConnectPasswordRes { #[derive(serde::Deserialize, Debug)] struct ConnectErrorRes { error: String, - error_description: String, + error_description: Option<String>, #[serde(rename = "ErrorModel")] error_model: Option<ConnectErrorResErrorModel>, #[serde(rename = "TwoFactorProviders")] @@ -575,23 +576,62 @@ impl Client { Ok(prelogin_res.kdf_iterations) } + pub async fn register( + &self, + email: &str, + device_id: &str, + apikey: &crate::locked::ApiKey, + ) -> Result<()> { + let connect_req = ConnectPasswordReq { + grant_type: "client_credentials".to_string(), + username: email.to_string(), + password: None, + scope: "api".to_string(), + // XXX unwraps here are not necessarily safe + client_id: String::from_utf8(apikey.client_id().to_vec()) + .unwrap(), + client_secret: Some( + String::from_utf8(apikey.client_secret().to_vec()).unwrap(), + ), + device_type: 8, + device_identifier: device_id.to_string(), + device_name: "rbw".to_string(), + device_push_token: "".to_string(), + two_factor_token: None, + two_factor_provider: None, + }; + let client = reqwest::Client::new(); + let res = client + .post(&self.identity_url("/connect/token")) + .form(&connect_req) + .send() + .await + .map_err(|source| Error::Reqwest { source })?; + if let reqwest::StatusCode::OK = res.status() { + Ok(()) + } else { + let code = res.status().as_u16(); + Err(classify_login_error(&res.json_with_path().await?, code)) + } + } + pub async fn login( &self, email: &str, - master_password_hash: &crate::locked::PasswordHash, + device_id: &str, + password_hash: &crate::locked::PasswordHash, two_factor_token: Option<&str>, two_factor_provider: Option<TwoFactorProviderType>, ) -> Result<(String, String, String)> { let connect_req = ConnectPasswordReq { grant_type: "password".to_string(), username: email.to_string(), - password: base64::encode(master_password_hash.hash()), + password: Some(base64::encode(password_hash.hash())), scope: "api offline_access".to_string(), client_id: "desktop".to_string(), + client_secret: None, device_type: 8, - device_identifier: uuid::Uuid::new_v4() - .to_hyphenated() - .to_string(), + device_identifier: device_id.to_string(), device_name: "rbw".to_string(), device_push_token: "".to_string(), two_factor_token: two_factor_token @@ -602,6 +642,10 @@ impl Client { let res = client .post(&self.identity_url("/connect/token")) .form(&connect_req) + .header( + "auth-email", + base64::encode_config(email, base64::URL_SAFE_NO_PAD), + ) .send() .await .map_err(|source| Error::Reqwest { source })?; @@ -708,7 +752,7 @@ impl Client { password: password.clone(), totp: totp.clone(), uris, - }) + }); } crate::db::EntryData::Card { cardholder_name, @@ -1046,15 +1090,17 @@ impl Client { } fn classify_login_error(error_res: &ConnectErrorRes, code: u16) -> Error { + let error_desc = error_res.error_description.clone(); + let error_desc = error_desc.as_deref(); match error_res.error.as_str() { - "invalid_grant" => match error_res.error_description.as_str() { - "invalid_username_or_password" => { + "invalid_grant" => match error_desc { + Some("invalid_username_or_password") => { if let Some(error_model) = error_res.error_model.as_ref() { let message = error_model.message.as_str().to_string(); return Error::IncorrectPassword { message }; } } - "Two factor required." => { + Some("Two factor required.") => { if let Some(providers) = error_res.two_factor_providers.as_ref() { @@ -1063,12 +1109,18 @@ fn classify_login_error(error_res: &ConnectErrorRes, code: u16) -> Error { }; } } + Some("Captcha required.") => { + return Error::RegistrationRequired; + } _ => {} }, + "invalid_client" => { + return Error::IncorrectApiKey; + } "" => { // bitwarden_rs returns an empty error and error_description for // this case, for some reason - if error_res.error_description.is_empty() { + if error_desc.is_none() || error_desc == Some("") { if let Some(error_model) = error_res.error_model.as_ref() { let message = error_model.message.as_str().to_string(); match message.as_str() { diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs index 4f4096e..1cc71c3 100644 --- a/src/bin/rbw-agent/actions.rs +++ b/src/bin/rbw-agent/actions.rs @@ -1,5 +1,84 @@ use anyhow::Context as _; +pub async fn register( + sock: &mut crate::sock::Sock, + tty: Option<&str>, +) -> anyhow::Result<()> { + let db = load_db().await.unwrap_or_else(|_| rbw::db::Db::new()); + + if db.needs_login() { + let url_str = config_base_url().await?; + let url = reqwest::Url::parse(&url_str) + .context("failed to parse base url")?; + let host = if let Some(host) = url.host_str() { + host + } else { + return Err(anyhow::anyhow!( + "couldn't find host in rbw base url {}", + url_str + )); + }; + + let email = config_email().await?; + + let mut err_msg = None; + for i in 1_u8..=3 { + let err = if i > 1 { + // this unwrap is safe because we only ever continue the loop + // if we have set err_msg + Some(format!("{} (attempt {}/3)", err_msg.unwrap(), i)) + } else { + None + }; + let client_id = rbw::pinentry::getpin( + &config_pinentry().await?, + "API key client__id", + &format!("Log in to {}", host), + err.as_deref(), + tty, + false, + ) + .await + .context("failed to read client_id from pinentry")?; + let client_secret = rbw::pinentry::getpin( + &config_pinentry().await?, + "API key client__secret", + &format!("Log in to {}", host), + err.as_deref(), + tty, + false, + ) + .await + .context("failed to read client_secret from pinentry")?; + let apikey = rbw::locked::ApiKey::new(client_id, client_secret); + match rbw::actions::register(&email, apikey.clone()).await { + Ok(()) => { + break; + } + Err(rbw::error::Error::IncorrectPassword { message }) => { + if i == 3 { + return Err(rbw::error::Error::IncorrectPassword { + message, + }) + .context("failed to log in to bitwarden instance"); + } else { + err_msg = Some(message); + continue; + } + } + Err(e) => { + return Err(e) + .context("failed to log in to bitwarden instance") + } + } + } + } + + respond_ack(sock).await?; + + Ok(()) +} + pub async fn login( sock: &mut crate::sock::Sock, state: std::sync::Arc<tokio::sync::RwLock<crate::agent::State>>, @@ -37,16 +116,18 @@ pub async fn login( &format!("Log in to {}", host), err.as_deref(), tty, + true, ) .await .context("failed to read password from pinentry")?; - match rbw::actions::login(&email, &password, None, None).await { + match rbw::actions::login(&email, password.clone(), None, None) + .await + { Ok(( access_token, refresh_token, iterations, protected_key, - _, )) => { login_success( sock, @@ -74,7 +155,7 @@ pub async fn login( ) = two_factor( tty, &email, - &password, + password.clone(), rbw::api::TwoFactorProviderType::Authenticator, ) .await?; @@ -122,7 +203,7 @@ pub async fn login( async fn two_factor( tty: Option<&str>, email: &str, - password: &rbw::locked::Password, + password: rbw::locked::Password, provider: rbw::api::TwoFactorProviderType, ) -> anyhow::Result<(String, String, u32, String)> { let mut err_msg = None; @@ -140,26 +221,21 @@ async fn two_factor( "Enter the 6 digit verification code from your authenticator app.", err.as_deref(), tty, + true, ) .await .context("failed to read code from pinentry")?; let code = std::str::from_utf8(code.password()) .context("code was not valid utf8")?; match rbw::actions::login( - &email, - &password, + email, + password.clone(), Some(code), Some(provider), ) .await { - Ok(( - access_token, - refresh_token, - iterations, - protected_key, - _, - )) => { + Ok((access_token, refresh_token, iterations, protected_key)) => { return Ok(( access_token, refresh_token, @@ -300,6 +376,7 @@ pub async fn unlock( "Unlock the local database", err.as_deref(), tty, + true, ) .await .context("failed to read password from pinentry")?; @@ -430,7 +507,7 @@ pub async fn decrypt( .context("failed to parse encrypted secret")?; let plaintext = String::from_utf8( cipherstring - .decrypt_symmetric(&keys) + .decrypt_symmetric(keys) .context("failed to decrypt encrypted secret")?, ) .context("failed to parse decrypted secret")?; @@ -512,7 +589,7 @@ async fn config_email() -> anyhow::Result<String> { async fn load_db() -> anyhow::Result<rbw::db::Db> { let config = rbw::config::Config::load_async().await?; if let Some(email) = &config.email { - rbw::db::Db::load_async(&config.server_name(), &email) + rbw::db::Db::load_async(&config.server_name(), email) .await .map_err(anyhow::Error::new) } else { @@ -523,7 +600,7 @@ async fn load_db() -> anyhow::Result<rbw::db::Db> { async fn save_db(db: &rbw::db::Db) -> anyhow::Result<()> { let config = rbw::config::Config::load_async().await?; if let Some(email) = &config.email { - db.save_async(&config.server_name(), &email) + db.save_async(&config.server_name(), email) .await .map_err(anyhow::Error::new) } else { diff --git a/src/bin/rbw-agent/agent.rs b/src/bin/rbw-agent/agent.rs index 760a1fc..fae8c7b 100644 --- a/src/bin/rbw-agent/agent.rs +++ b/src/bin/rbw-agent/agent.rs @@ -133,6 +133,10 @@ async fn handle_request( } }; let set_timeout = match &req.action { + rbw::protocol::Action::Register => { + crate::actions::register(sock, req.tty.as_deref()).await?; + true + } rbw::protocol::Action::Login => { crate::actions::login(sock, state.clone(), req.tty.as_deref()) .await?; @@ -167,7 +171,7 @@ async fn handle_request( crate::actions::decrypt( sock, state.clone(), - &cipherstring, + cipherstring, org_id.as_deref(), ) .await?; @@ -177,7 +181,7 @@ async fn handle_request( crate::actions::encrypt( sock, state.clone(), - &plaintext, + plaintext, org_id.as_deref(), ) .await?; diff --git a/src/bin/rbw/actions.rs b/src/bin/rbw/actions.rs index 75703f9..39fde15 100644 --- a/src/bin/rbw/actions.rs +++ b/src/bin/rbw/actions.rs @@ -1,6 +1,10 @@ use anyhow::Context as _; use std::io::Read as _; +pub fn register() -> anyhow::Result<()> { + simple_action(rbw::protocol::Action::Register) +} + pub fn login() -> anyhow::Result<()> { simple_action(rbw::protocol::Action::Login) } diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index c1f9291..9efd966 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -465,6 +465,13 @@ pub fn config_unset(key: &str) -> anyhow::Result<()> { Ok(()) } +pub fn register() -> anyhow::Result<()> { + ensure_agent()?; + crate::actions::register()?; + + Ok(()) +} + pub fn login() -> anyhow::Result<()> { ensure_agent()?; crate::actions::login()?; @@ -642,7 +649,7 @@ pub fn add( let mut folder_id = None; if let Some(folder_name) = folder { let (new_access_token, folders) = - rbw::actions::list_folders(&access_token, &refresh_token)?; + rbw::actions::list_folders(&access_token, refresh_token)?; if let Some(new_access_token) = new_access_token { access_token = new_access_token.clone(); db.access_token = Some(new_access_token); @@ -663,7 +670,7 @@ pub fn add( if folder_id.is_none() { let (new_access_token, id) = rbw::actions::create_folder( &access_token, - &refresh_token, + refresh_token, &crate::actions::encrypt(folder_name, None)?, )?; if let Some(new_access_token) = new_access_token { @@ -677,7 +684,7 @@ pub fn add( if let (Some(access_token), ()) = rbw::actions::add( &access_token, - &refresh_token, + refresh_token, &name, &rbw::db::EntryData::Login { username, @@ -735,7 +742,7 @@ pub fn generate( let mut folder_id = None; if let Some(folder_name) = folder { let (new_access_token, folders) = - rbw::actions::list_folders(&access_token, &refresh_token)?; + rbw::actions::list_folders(&access_token, refresh_token)?; if let Some(new_access_token) = new_access_token { access_token = new_access_token.clone(); db.access_token = Some(new_access_token); @@ -758,7 +765,7 @@ pub fn generate( if folder_id.is_none() { let (new_access_token, id) = rbw::actions::create_folder( &access_token, - &refresh_token, + refresh_token, &crate::actions::encrypt(folder_name, None)?, )?; if let Some(new_access_token) = new_access_token { @@ -772,7 +779,7 @@ pub fn generate( if let (Some(access_token), ()) = rbw::actions::add( &access_token, - &refresh_token, + refresh_token, &name, &rbw::db::EntryData::Login { username, @@ -880,8 +887,8 @@ pub fn edit( }; if let (Some(access_token), ()) = rbw::actions::edit( - &access_token, - &refresh_token, + access_token, + refresh_token, &entry.id, entry.org_id.as_deref(), &entry.name, @@ -921,7 +928,7 @@ pub fn remove( .with_context(|| format!("couldn't find entry for '{}'", desc))?; if let (Some(access_token), ()) = - rbw::actions::remove(&access_token, &refresh_token, &entry.id)? + rbw::actions::remove(access_token, refresh_token, &entry.id)? { db.access_token = Some(access_token); save_db(&db)?; @@ -1053,7 +1060,7 @@ fn find_entry( Ok(_) => { for cipher in &db.entries { if name == cipher.id { - return Ok((cipher.clone(), decrypt_cipher(&cipher)?)); + return Ok((cipher.clone(), decrypt_cipher(cipher)?)); } } Err(anyhow::anyhow!("no entry found")) @@ -1183,10 +1190,7 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> { .name .as_ref() .map(|name| { - crate::actions::decrypt( - &name, - entry.org_id.as_deref(), - ) + crate::actions::decrypt(name, entry.org_id.as_deref()) }) .transpose()?, value: field @@ -1194,7 +1198,7 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> { .as_ref() .map(|value| { crate::actions::decrypt( - &value, + value, entry.org_id.as_deref(), ) }) @@ -1444,7 +1448,7 @@ fn parse_editor(contents: &str) -> (Option<String>, Option<String>) { fn load_db() -> anyhow::Result<rbw::db::Db> { let config = rbw::config::Config::load()?; if let Some(email) = &config.email { - rbw::db::Db::load(&config.server_name(), &email) + rbw::db::Db::load(&config.server_name(), email) .map_err(anyhow::Error::new) } else { Err(anyhow::anyhow!("failed to find email address in config")) @@ -1454,7 +1458,7 @@ fn load_db() -> anyhow::Result<rbw::db::Db> { fn save_db(db: &rbw::db::Db) -> anyhow::Result<()> { let config = rbw::config::Config::load()?; if let Some(email) = &config.email { - db.save(&config.server_name(), &email) + db.save(&config.server_name(), email) .map_err(anyhow::Error::new) } else { Err(anyhow::anyhow!("failed to find email address in config")) @@ -1464,7 +1468,7 @@ fn save_db(db: &rbw::db::Db) -> anyhow::Result<()> { fn remove_db() -> anyhow::Result<()> { let config = rbw::config::Config::load()?; if let Some(email) = &config.email { - rbw::db::Db::remove(&config.server_name(), &email) + rbw::db::Db::remove(&config.server_name(), email) .map_err(anyhow::Error::new) } else { Err(anyhow::anyhow!("failed to find email address in config")) @@ -1494,8 +1498,11 @@ fn parse_totp_secret(secret: &str) -> anyhow::Result<Vec<u8>> { } else { secret.to_string() }; - base32::decode(base32::Alphabet::RFC4648 { padding: false }, &secret_str.replace(" ", "")) - .ok_or_else(|| anyhow::anyhow!("totp secret was not valid base32")) + base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + &secret_str.replace(" ", ""), + ) + .ok_or_else(|| anyhow::anyhow!("totp secret was not valid base32")) } fn generate_totp(secret: &str) -> anyhow::Result<String> { diff --git a/src/bin/rbw/main.rs b/src/bin/rbw/main.rs index 6c6c33e..85631c5 100644 --- a/src/bin/rbw/main.rs +++ b/src/bin/rbw/main.rs @@ -17,6 +17,17 @@ enum Opt { config: Config, }, + #[structopt( + about = "Register this device with the Bitwarden server", + long_about = "Register this device with the Bitwarden server\n\n\ + The official Bitwarden server includes bot detection to prevent \ + brute force attacks. In order to avoid being detected as bot \ + traffic, you will need to use this command to log in with your \ + personal API key (instead of your password) first before regular \ + logins will work." + )] + Register, + #[structopt(about = "Log in to the Bitwarden server")] Login, @@ -214,6 +225,7 @@ impl Opt { Self::Config { config } => { format!("config {}", config.subcommand_name()) } + Self::Register => "register".to_string(), Self::Login => "login".to_string(), Self::Unlock => "unlock".to_string(), Self::Unlocked => "unlocked".to_string(), @@ -281,22 +293,23 @@ fn main(opt: Opt) { let res = match &opt { Opt::Config { config } => match config { Config::Show => commands::config_show(), - Config::Set { key, value } => commands::config_set(&key, &value), - Config::Unset { key } => commands::config_unset(&key), + Config::Set { key, value } => commands::config_set(key, value), + Config::Unset { key } => commands::config_unset(key), }, + Opt::Register => commands::register(), Opt::Login => commands::login(), Opt::Unlock => commands::unlock(), Opt::Unlocked => commands::unlocked(), Opt::Sync => commands::sync(), - Opt::List { fields } => commands::list(&fields), + Opt::List { fields } => commands::list(fields), Opt::Get { name, user, folder, full, - } => commands::get(&name, user.as_deref(), folder.as_deref(), *full), + } => commands::get(name, user.as_deref(), folder.as_deref(), *full), Opt::Code { name, user, folder } => { - commands::code(&name, user.as_deref(), folder.as_deref()) + commands::code(name, user.as_deref(), folder.as_deref()) } Opt::Add { name, @@ -304,7 +317,7 @@ fn main(opt: Opt) { uri, folder, } => commands::add( - &name, + name, user.as_deref(), uri.iter() // XXX not sure what the ui for specifying the match type @@ -349,18 +362,18 @@ fn main(opt: Opt) { ) } Opt::Edit { name, user, folder } => { - commands::edit(&name, user.as_deref(), folder.as_deref()) + commands::edit(name, user.as_deref(), folder.as_deref()) } Opt::Remove { name, user, folder } => { - commands::remove(&name, user.as_deref(), folder.as_deref()) + commands::remove(name, user.as_deref(), folder.as_deref()) } Opt::History { name, user, folder } => { - commands::history(&name, user.as_deref(), folder.as_deref()) + commands::history(name, user.as_deref(), folder.as_deref()) } Opt::Lock => commands::lock(), Opt::Purge => commands::purge(), Opt::StopAgent => commands::stop_agent(), - Opt::GenCompletions { shell } => gen_completions(&shell), + Opt::GenCompletions { shell } => gen_completions(shell), } .context(format!("rbw {}", opt.subcommand_name())); diff --git a/src/cipherstring.rs b/src/cipherstring.rs index 73eeeb6..39254c7 100644 --- a/src/cipherstring.rs +++ b/src/cipherstring.rs @@ -4,6 +4,7 @@ use block_modes::BlockMode as _; use block_padding::Padding as _; use hmac::{Mac as _, NewMac as _}; use rand::RngCore as _; +use rsa::pkcs8::FromPrivateKey as _; use zeroize::Zeroize as _; pub enum CipherString { @@ -122,27 +123,27 @@ impl CipherString { &self, keys: &crate::locked::Keys, ) -> Result<Vec<u8>> { - match self { - Self::Symmetric { + if let Self::Symmetric { + iv, + ciphertext, + mac, + } = self + { + let cipher = decrypt_common_symmetric( + keys, iv, ciphertext, - mac, - } => { - let cipher = decrypt_common_symmetric( - keys, - iv, - ciphertext, - mac.as_deref(), - )?; - cipher - .decrypt_vec(ciphertext) - .map_err(|source| Error::Decrypt { source }) - } - _ => Err(Error::InvalidCipherString { + mac.as_deref(), + )?; + cipher + .decrypt_vec(ciphertext) + .map_err(|source| Error::Decrypt { source }) + } else { + Err(Error::InvalidCipherString { reason: "found an asymmetric cipherstring, expecting symmetric" .to_string(), - }), + }) } } @@ -150,30 +151,30 @@ impl CipherString { &self, keys: &crate::locked::Keys, ) -> Result<crate::locked::Vec> { - match self { - Self::Symmetric { + if let Self::Symmetric { + iv, + ciphertext, + mac, + } = self + { + let mut res = crate::locked::Vec::new(); + res.extend(ciphertext.iter().copied()); + let cipher = decrypt_common_symmetric( + keys, iv, ciphertext, - mac, - } => { - let mut res = crate::locked::Vec::new(); - res.extend(ciphertext.iter().copied()); - let cipher = decrypt_common_symmetric( - keys, - iv, - ciphertext, - mac.as_deref(), - )?; - cipher - .decrypt(res.data_mut()) - .map_err(|source| Error::Decrypt { source })?; - Ok(res) - } - _ => Err(Error::InvalidCipherString { + mac.as_deref(), + )?; + cipher + .decrypt(res.data_mut()) + .map_err(|source| Error::Decrypt { source })?; + Ok(res) + } else { + Err(Error::InvalidCipherString { reason: "found an asymmetric cipherstring, expecting symmetric" .to_string(), - }), + }) } } @@ -181,34 +182,33 @@ impl CipherString { &self, private_key: &crate::locked::PrivateKey, ) -> Result<crate::locked::Vec> { - match self { - Self::Asymmetric { ciphertext } => { - let privkey_data = private_key.private_key(); - let privkey_data = block_padding::Pkcs7::unpad(privkey_data) - .map_err(|_| Error::Padding)?; - let pkey = rsa::RSAPrivateKey::from_pkcs8(privkey_data) - .map_err(|source| Error::Rsa { source })?; - let mut bytes = pkey - .decrypt( - rsa::padding::PaddingScheme::new_oaep::<sha1::Sha1>(), - ciphertext, - ) - .map_err(|source| Error::Rsa { source })?; + if let Self::Asymmetric { ciphertext } = self { + let privkey_data = private_key.private_key(); + let privkey_data = block_padding::Pkcs7::unpad(privkey_data) + .map_err(|_| Error::Padding)?; + let pkey = rsa::RsaPrivateKey::from_pkcs8_der(privkey_data) + .map_err(|source| Error::RsaPkcs8 { source })?; + let mut bytes = pkey + .decrypt( + rsa::padding::PaddingScheme::new_oaep::<sha1::Sha1>(), + ciphertext, + ) + .map_err(|source| Error::Rsa { source })?; - // XXX it'd be great if the rsa crate would let us decrypt - // into a preallocated buffer directly to avoid the - // intermediate vec that needs to be manually zeroized, etc - let mut res = crate::locked::Vec::new(); - res.extend(bytes.iter().copied()); - bytes.zeroize(); + // XXX it'd be great if the rsa crate would let us decrypt + // into a preallocated buffer directly to avoid the + // intermediate vec that needs to be manually zeroized, etc + let mut res = crate::locked::Vec::new(); + res.extend(bytes.iter().copied()); + bytes.zeroize(); - Ok(res) - } - _ => Err(Error::InvalidCipherString { + Ok(res) + } else { + Err(Error::InvalidCipherString { reason: "found a symmetric cipherstring, expecting asymmetric" .to_string(), - }), + }) } } } diff --git a/src/config.rs b/src/config.rs index c6e0787..bbc39f7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,6 +12,8 @@ pub struct Config { pub lock_timeout: u64, #[serde(default = "default_pinentry")] pub pinentry: String, + #[serde(default = "stub_device_id")] + pub device_id: String, } impl Default for Config { @@ -22,6 +24,7 @@ impl Default for Config { identity_url: Default::default(), lock_timeout: default_lock_timeout(), pinentry: default_pinentry(), + device_id: default_device_id(), } } } @@ -34,6 +37,14 @@ pub fn default_pinentry() -> String { "pinentry".to_string() } +fn default_device_id() -> String { + uuid::Uuid::new_v4().to_hyphenated().to_string() +} + +fn stub_device_id() -> String { + String::from("fix") +} + impl Config { pub fn new() -> Self { Self::default() @@ -116,10 +127,14 @@ impl Config { } pub fn validate() -> Result<()> { - let config = Self::load()?; + let mut config = Self::load()?; if config.email.is_none() { return Err(Error::ConfigMissingEmail); } + if config.device_id == stub_device_id() { + config.device_id = default_device_id(); + config.save()?; + } Ok(()) } diff --git a/src/edit.rs b/src/edit.rs index 45e9534..f7d8131 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -13,7 +13,7 @@ pub fn edit(contents: &str, help: &str) -> Result<String> { let mut args = vec![]; match editor.file_name() { Some(editor) => match editor.to_str() { - Some("vim") => { + Some("vim") | Some("nvim") => { // disable swap files and viminfo for password entry args.push(std::ffi::OsStr::new("-ni")); args.push(std::ffi::OsStr::new("NONE")); diff --git a/src/error.rs b/src/error.rs index bc97087..d584e53 100644 --- a/src/error.rs +++ b/src/error.rs @@ -46,6 +46,9 @@ pub enum Error { #[error("failed to expand with hkdf")] HkdfExpand, + #[error("incorrect api key")] + IncorrectApiKey, + #[error("{message}")] IncorrectPassword { message: String }, @@ -132,6 +135,9 @@ pub enum Error { #[error("error waiting for pinentry to exit")] PinentryWait { source: tokio::io::Error }, + #[error("This device has not yet been registered with the Bitwarden server. Run `rbw register` first, and then try again.")] + RegistrationRequired, + #[error("failed to remove db at {}", .file.display())] RemoveDb { source: std::io::Error, @@ -150,6 +156,9 @@ pub enum Error { #[error("failed to decrypt")] Rsa { source: rsa::errors::Error }, + #[error("failed to decrypt")] + RsaPkcs8 { source: rsa::pkcs8::Error }, + #[error("failed to save config to {}", .file.display())] SaveConfig { source: std::io::Error, @@ -12,6 +12,7 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_lines)] #![allow(clippy::type_complexity)] +#![allow(clippy::unused_async)] pub mod actions; pub mod api; diff --git a/src/locked.rs b/src/locked.rs index 611e57e..4ddf021 100644 --- a/src/locked.rs +++ b/src/locked.rs @@ -32,7 +32,7 @@ impl Vec { pub fn zero(&mut self) { self.truncate(0); - self.data.extend(std::iter::repeat(0).take(LEN)) + self.data.extend(std::iter::repeat(0).take(LEN)); } pub fn extend(&mut self, it: impl Iterator<Item = u8>) { @@ -51,6 +51,15 @@ impl Drop for Vec { } } +impl Clone for Vec { + fn clone(&self) -> Self { + let mut new_vec = Self::new(); + new_vec.extend(self.data().iter().copied()); + new_vec + } +} + +#[derive(Clone)] pub struct Password { password: Vec, } @@ -65,6 +74,7 @@ impl Password { } } +#[derive(Clone)] pub struct Keys { keys: Vec, } @@ -83,6 +93,7 @@ impl Keys { } } +#[derive(Clone)] pub struct PasswordHash { hash: Vec, } @@ -97,6 +108,7 @@ impl PasswordHash { } } +#[derive(Clone)] pub struct PrivateKey { private_key: Vec, } @@ -110,3 +122,26 @@ impl PrivateKey { self.private_key.data() } } + +#[derive(Clone)] +pub struct ApiKey { + client_id: Password, + client_secret: Password, +} + +impl ApiKey { + pub fn new(client_id: Password, client_secret: Password) -> Self { + Self { + client_id, + client_secret, + } + } + + pub fn client_id(&self) -> &[u8] { + self.client_id.password() + } + + pub fn client_secret(&self) -> &[u8] { + self.client_secret.password() + } +} diff --git a/src/pinentry.rs b/src/pinentry.rs index d62d4b2..b4d2bb0 100644 --- a/src/pinentry.rs +++ b/src/pinentry.rs @@ -8,15 +8,19 @@ pub async fn getpin( desc: &str, err: Option<&str>, tty: Option<&str>, + grab: bool, ) -> Result<crate::locked::Password> { let mut opts = tokio::process::Command::new(pinentry); opts.stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()); + let mut args = vec!["-o", "0"]; if let Some(tty) = tty { - opts.args(&["-T", tty, "-o", "0"]); - } else { - opts.args(&["-o", "0"]); + args.extend(&["-T", tty]); } + if !grab { + args.push("-g"); + } + opts.args(args); let mut child = opts.spawn().map_err(|source| Error::Spawn { source })?; // unwrap is safe because we specified stdin as piped in the command opts // above diff --git a/src/protocol.rs b/src/protocol.rs index a4b9722..14fa7f9 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -23,6 +23,7 @@ pub struct Request { #[serde(tag = "type")] pub enum Action { Login, + Register, Unlock, CheckLock, Lock, |