Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
Use `ssh-agent-lib` instead of `radicle-ssh` for SSH agent features
Merged did:key:z6Mkothx...jCPc opened 1 year ago

Note that this patch is a work in progress and also a testing ground for me. I hope I won’t break radicle as I try to submit it :)

Signed-off-by: Wiktor Kwapisiewicz wiktor@metacode.biz

16 files changed +664 -1279 48551cde 48551cde
modified Cargo.lock
@@ -113,47 +113,48 @@ dependencies = [

[[package]]
name = "anstream"
-
version = "0.6.13"
+
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
 "anstyle",
 "anstyle-parse",
 "anstyle-query",
 "anstyle-wincon",
 "colorchoice",
+
 "is_terminal_polyfill",
 "utf8parse",
]

[[package]]
name = "anstyle"
-
version = "1.0.6"
+
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"

[[package]]
name = "anstyle-parse"
-
version = "0.2.3"
+
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
 "utf8parse",
]

[[package]]
name = "anstyle-query"
-
version = "1.0.2"
+
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [
 "windows-sys 0.52.0",
]

[[package]]
name = "anstyle-wincon"
-
version = "3.0.2"
+
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
 "anstyle",
 "windows-sys 0.52.0",
@@ -161,9 +162,9 @@ dependencies = [

[[package]]
name = "anyhow"
-
version = "1.0.82"
+
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
+
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"

[[package]]
name = "arc-swap"
@@ -179,9 +180,9 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"

[[package]]
name = "autocfg"
-
version = "1.2.0"
+
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"

[[package]]
name = "base-x"
@@ -322,9 +323,9 @@ dependencies = [

[[package]]
name = "cc"
-
version = "1.0.95"
+
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
+
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
dependencies = [
 "jobserver",
 "libc",
@@ -386,9 +387,9 @@ dependencies = [

[[package]]
name = "colorchoice"
-
version = "1.0.0"
+
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"

[[package]]
name = "colored"
@@ -423,27 +424,27 @@ dependencies = [

[[package]]
name = "crc32fast"
-
version = "1.4.0"
+
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
 "cfg-if",
]

[[package]]
name = "crossbeam-channel"
-
version = "0.5.12"
+
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-utils"
-
version = "0.8.19"
+
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"

[[package]]
name = "crypto-bigint"
@@ -484,6 +485,33 @@ dependencies = [
]

[[package]]
+
name = "curve25519-dalek"
+
version = "4.1.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+
dependencies = [
+
 "cfg-if",
+
 "cpufeatures",
+
 "curve25519-dalek-derive",
+
 "digest",
+
 "fiat-crypto",
+
 "platforms",
+
 "rustc_version",
+
 "subtle",
+
]
+

+
[[package]]
+
name = "curve25519-dalek-derive"
+
version = "0.1.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
]
+

+
[[package]]
name = "cypheraddr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -521,15 +549,15 @@ dependencies = [

[[package]]
name = "data-encoding"
-
version = "2.5.0"
+
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"

[[package]]
name = "data-encoding-macro"
-
version = "0.1.14"
+
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e"
+
checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639"
dependencies = [
 "data-encoding",
 "data-encoding-macro-internal",
@@ -537,9 +565,9 @@ dependencies = [

[[package]]
name = "data-encoding-macro-internal"
-
version = "0.1.12"
+
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3"
+
checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f"
dependencies = [
 "data-encoding",
 "syn 1.0.109",
@@ -583,6 +611,17 @@ dependencies = [
]

[[package]]
+
name = "displaydoc"
+
version = "0.2.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
]
+

+
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -595,7 +634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfd533a2fc01178c738c99412ae1f7e1ad2cb37c2e14bfd87e9d4618171c825"
dependencies = [
 "ct-codecs",
-
 "ed25519",
+
 "ed25519 1.5.3",
 "getrandom",
]

@@ -623,10 +662,31 @@ dependencies = [
]

[[package]]
+
name = "ed25519"
+
version = "2.2.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+
dependencies = [
+
 "signature 2.2.0",
+
]
+

+
[[package]]
+
name = "ed25519-dalek"
+
version = "2.1.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+
dependencies = [
+
 "curve25519-dalek",
+
 "ed25519 2.2.3",
+
 "sha2",
+
 "subtle",
+
]
+

+
[[package]]
name = "either"
-
version = "1.11.0"
+
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"

[[package]]
name = "elliptic-curve"
@@ -655,9 +715,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"

[[package]]
name = "errno"
-
version = "0.3.8"
+
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
 "libc",
 "windows-sys 0.52.0",
@@ -665,9 +725,9 @@ dependencies = [

[[package]]
name = "escargot"
-
version = "0.5.10"
+
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "4f474c6844cbd04e783d0f25757583db4f491770ca618bedf2fb01815fc79939"
+
checksum = "650eb5f6eeda986377996e9ed570cbc20cc16d30440696f82f129c863e4e3e83"
dependencies = [
 "log",
 "once_cell",
@@ -695,9 +755,9 @@ dependencies = [

[[package]]
name = "fastrand"
-
version = "2.0.2"
+
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"

[[package]]
name = "ff"
@@ -710,6 +770,12 @@ dependencies = [
]

[[package]]
+
name = "fiat-crypto"
+
version = "0.2.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+

+
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -717,15 +783,15 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
 "cfg-if",
 "libc",
-
 "redox_syscall",
+
 "redox_syscall 0.4.1",
 "windows-sys 0.52.0",
]

[[package]]
name = "flate2"
-
version = "1.0.28"
+
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
 "crc32fast",
 "miniz_oxide",
@@ -762,9 +828,9 @@ dependencies = [

[[package]]
name = "getrandom"
-
version = "0.2.14"
+
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
 "cfg-if",
 "libc",
@@ -829,16 +895,16 @@ dependencies = [

[[package]]
name = "gix-actor"
-
version = "0.31.1"
+
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "45c3a3bde455ad2ee8ba8a195745241ce0b770a8a26faae59fcf409d01b28c46"
+
checksum = "d69c59d392c7e6c94385b6fd6089d6df0fe945f32b4357687989f3aee253cd7f"
dependencies = [
 "bstr",
 "gix-date",
 "gix-utils",
 "itoa",
 "thiserror",
-
 "winnow 0.6.8",
+
 "winnow 0.6.13",
]

[[package]]
@@ -861,9 +927,9 @@ dependencies = [

[[package]]
name = "gix-command"
-
version = "0.3.6"
+
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f90009020dc4b3de47beed28e1334706e0a330ddd17f5cfeb097df3b15a54b77"
+
checksum = "6c22e086314095c43ffe5cdc5c0922d5439da4fd726f3b0438c56147c34dc225"
dependencies = [
 "bstr",
 "gix-path",
@@ -879,7 +945,7 @@ checksum = "f7b102311085da4af18823413b5176d7c500fb2272eaf391cfa8635d8bcb12c4"
dependencies = [
 "bstr",
 "gix-chunk",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-hash 0.14.2",
 "memmap2",
 "thiserror",
@@ -921,10 +987,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c70146183bd3c7119329a3c7392d1aa0e0adbe48d727f4df31828fe6d8fdaa1"
dependencies = [
 "bstr",
-
 "gix-command 0.3.6",
+
 "gix-command 0.3.7",
 "gix-config-value",
 "gix-path",
-
 "gix-prompt 0.8.4",
+
 "gix-prompt 0.8.5",
 "gix-sec",
 "gix-trace",
 "gix-url 0.27.3",
@@ -933,9 +999,9 @@ dependencies = [

[[package]]
name = "gix-date"
-
version = "0.8.5"
+
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "180b130a4a41870edfbd36ce4169c7090bca70e195da783dea088dd973daa59c"
+
checksum = "367ee9093b0c2b04fd04c5c7c8b6a1082713534eab537597ae343663a518fa99"
dependencies = [
 "bstr",
 "itoa",
@@ -969,9 +1035,9 @@ dependencies = [

[[package]]
name = "gix-features"
-
version = "0.38.1"
+
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "db4254037d20a247a0367aa79333750146a369719f0c6617fec4f5752cc62b37"
+
checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69"
dependencies = [
 "crc32fast",
 "flate2",
@@ -991,7 +1057,7 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2184c40e7910529677831c8b481acf788ffd92427ed21fad65b6aa637e631b8"
dependencies = [
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-utils",
]

@@ -1028,21 +1094,21 @@ dependencies = [

[[package]]
name = "gix-object"
-
version = "0.42.1"
+
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3d4f8efae72030df1c4a81d02dbe2348e748d9b9a11e108ed6efbd846326e051"
+
checksum = "1fe2dc4a41191c680c942e6ebd630c8107005983c4679214fdb1007dcf5ae1df"
dependencies = [
 "bstr",
 "gix-actor",
 "gix-date",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-hash 0.14.2",
 "gix-utils",
 "gix-validate",
 "itoa",
 "smallvec",
 "thiserror",
-
 "winnow 0.6.8",
+
 "winnow 0.6.13",
]

[[package]]
@@ -1053,7 +1119,7 @@ checksum = "e8bbb43d2fefdc4701ffdf9224844d05b136ae1b9a73c2f90710c8dd27a93503"
dependencies = [
 "arc-swap",
 "gix-date",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-fs",
 "gix-hash 0.14.2",
 "gix-object",
@@ -1073,7 +1139,7 @@ checksum = "b58bad27c7677fa6b587aab3a1aca0b6c97373bd371a0a4290677c838c9bcaf1"
dependencies = [
 "gix-chunk",
 "gix-diff",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-hash 0.14.2",
 "gix-hashtable",
 "gix-object",
@@ -1137,11 +1203,11 @@ dependencies = [

[[package]]
name = "gix-prompt"
-
version = "0.8.4"
+
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f5325eb17ce7b5e5d25dec5c2315d642a09d55b9888b3bf46b7d72e1621a55d8"
+
checksum = "fddabbc7c51c241600ab3c4623b19fa53bde7c1a2f637f61043ed5fcadf000cc"
dependencies = [
-
 "gix-command 0.3.6",
+
 "gix-command 0.3.7",
 "gix-config-value",
 "parking_lot",
 "rustix",
@@ -1168,20 +1234,20 @@ dependencies = [

[[package]]
name = "gix-protocol"
-
version = "0.45.0"
+
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "aed3bb6179835a3250403baa9d7022579e559fc45f2efc416d9de1a14b5acf11"
+
checksum = "3c140d4c6d209048826bad78f021a01b612830f89da356efeb31afe8957f8bee"
dependencies = [
 "bstr",
 "gix-credentials 0.24.2",
 "gix-date",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-hash 0.14.2",
-
 "gix-transport 0.42.0",
+
 "gix-transport 0.42.1",
 "gix-utils",
 "maybe-async",
 "thiserror",
-
 "winnow 0.6.8",
+
 "winnow 0.6.13",
]

[[package]]
@@ -1197,9 +1263,9 @@ dependencies = [

[[package]]
name = "gix-revwalk"
-
version = "0.13.0"
+
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e0a7d393ae814eeaae41a333c0ff684b243121cc61ccdc5bbe9897094588047d"
+
checksum = "4181db9cfcd6d1d0fd258e91569dbb61f94cb788b441b5294dd7f1167a3e788f"
dependencies = [
 "gix-commitgraph",
 "gix-date",
@@ -1259,13 +1325,13 @@ dependencies = [

[[package]]
name = "gix-transport"
-
version = "0.42.0"
+
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9d2f783b2fe86bf2a8cf1f3b8669d65b01ab4932f32cc0101d3893e1b16a3bd6"
+
checksum = "eb0ffa5f869977f5b9566399154055902f05d7e85c787d5eacf551acdd0c4adf"
dependencies = [
 "bstr",
-
 "gix-command 0.3.6",
-
 "gix-features 0.38.1",
+
 "gix-command 0.3.7",
+
 "gix-features 0.38.2",
 "gix-packetline 0.17.5",
 "gix-quote",
 "gix-sec",
@@ -1275,9 +1341,9 @@ dependencies = [

[[package]]
name = "gix-traverse"
-
version = "0.39.0"
+
version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f4029ec209b0cc480d209da3837a42c63801dd8548f09c1f4502c60accb62aeb"
+
checksum = "f20cb69b63eb3e4827939f42c05b7756e3488ef49c25c412a876691d568ee2a0"
dependencies = [
 "bitflags 2.5.0",
 "gix-commitgraph",
@@ -1311,7 +1377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0db829ebdca6180fbe32be7aed393591df6db4a72dbbc0b8369162390954d1cf"
dependencies = [
 "bstr",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-path",
 "home",
 "thiserror",
@@ -1330,9 +1396,9 @@ dependencies = [

[[package]]
name = "gix-validate"
-
version = "0.8.4"
+
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e39fc6e06044985eac19dd34d474909e517307582e462b2eb4c8fa51b6241545"
+
checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf"
dependencies = [
 "bstr",
 "thiserror",
@@ -1351,9 +1417,9 @@ dependencies = [

[[package]]
name = "hashbrown"
-
version = "0.14.3"
+
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"

[[package]]
name = "hmac"
@@ -1397,13 +1463,133 @@ dependencies = [
]

[[package]]
+
name = "icu_collections"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+
dependencies = [
+
 "displaydoc",
+
 "yoke",
+
 "zerofrom",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_locid"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+
dependencies = [
+
 "displaydoc",
+
 "litemap",
+
 "tinystr",
+
 "writeable",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_locid_transform"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+
dependencies = [
+
 "displaydoc",
+
 "icu_locid",
+
 "icu_locid_transform_data",
+
 "icu_provider",
+
 "tinystr",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_locid_transform_data"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+

+
[[package]]
+
name = "icu_normalizer"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+
dependencies = [
+
 "displaydoc",
+
 "icu_collections",
+
 "icu_normalizer_data",
+
 "icu_properties",
+
 "icu_provider",
+
 "smallvec",
+
 "utf16_iter",
+
 "utf8_iter",
+
 "write16",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_normalizer_data"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+

+
[[package]]
+
name = "icu_properties"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036"
+
dependencies = [
+
 "displaydoc",
+
 "icu_collections",
+
 "icu_locid_transform",
+
 "icu_properties_data",
+
 "icu_provider",
+
 "tinystr",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_properties_data"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+

+
[[package]]
+
name = "icu_provider"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+
dependencies = [
+
 "displaydoc",
+
 "icu_locid",
+
 "icu_provider_macros",
+
 "stable_deref_trait",
+
 "tinystr",
+
 "writeable",
+
 "yoke",
+
 "zerofrom",
+
 "zerovec",
+
]
+

+
[[package]]
+
name = "icu_provider_macros"
+
version = "1.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
]
+

+
[[package]]
name = "idna"
-
version = "0.5.0"
+
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+
checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed"
dependencies = [
-
 "unicode-bidi",
-
 "unicode-normalization",
+
 "icu_normalizer",
+
 "icu_properties",
+
 "smallvec",
+
 "utf8_iter",
]

[[package]]
@@ -1456,6 +1642,12 @@ dependencies = [
]

[[package]]
+
name = "is_terminal_polyfill"
+
version = "1.70.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+

+
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1535,14 +1727,14 @@ checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
dependencies = [
 "bitflags 2.5.0",
 "libc",
-
 "redox_syscall",
+
 "redox_syscall 0.4.1",
]

[[package]]
name = "libz-sys"
-
version = "1.1.16"
+
version = "1.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9"
+
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
dependencies = [
 "cc",
 "libc",
@@ -1552,9 +1744,15 @@ dependencies = [

[[package]]
name = "linux-raw-sys"
-
version = "0.4.13"
+
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+

+
[[package]]
+
name = "litemap"
+
version = "0.7.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"

[[package]]
name = "localtime"
@@ -1567,9 +1765,9 @@ dependencies = [

[[package]]
name = "lock_api"
-
version = "0.4.11"
+
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
 "autocfg",
 "scopeguard",
@@ -1589,7 +1787,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
dependencies = [
 "proc-macro2",
 "quote",
-
 "syn 2.0.60",
+
 "syn 2.0.66",
]

[[package]]
@@ -1609,9 +1807,9 @@ dependencies = [

[[package]]
name = "miniz_oxide"
-
version = "0.7.2"
+
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [
 "adler",
]
@@ -1716,9 +1914,9 @@ dependencies = [

[[package]]
name = "num-iter"
-
version = "0.1.44"
+
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
+
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
 "autocfg",
 "num-integer",
@@ -1727,9 +1925,9 @@ dependencies = [

[[package]]
name = "num-traits"
-
version = "0.2.18"
+
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
 "autocfg",
 "libm",
@@ -1802,9 +2000,9 @@ dependencies = [

[[package]]
name = "parking_lot"
-
version = "0.12.1"
+
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
 "lock_api",
 "parking_lot_core",
@@ -1812,15 +2010,15 @@ dependencies = [

[[package]]
name = "parking_lot_core"
-
version = "0.9.9"
+
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
 "cfg-if",
 "libc",
-
 "redox_syscall",
+
 "redox_syscall 0.5.1",
 "smallvec",
-
 "windows-targets 0.48.5",
+
 "windows-targets 0.52.5",
]

[[package]]
@@ -1876,6 +2074,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"

[[package]]
+
name = "platforms"
+
version = "3.4.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
+

+
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1964,9 +2168,9 @@ dependencies = [

[[package]]
name = "proc-macro2"
-
version = "1.0.81"
+
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
 "unicode-ident",
]
@@ -2036,7 +2240,6 @@ dependencies = [
 "radicle-cob",
 "radicle-crypto",
 "radicle-git-ext",
-
 "radicle-ssh",
 "serde",
 "serde_json",
 "siphasher 1.0.1",
@@ -2069,10 +2272,11 @@ dependencies = [
 "serde",
 "serde_json",
 "shlex",
+
 "ssh-agent-lib",
 "tempfile",
 "thiserror",
 "timeago",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
 "tree-sitter-bash",
 "tree-sitter-c",
 "tree-sitter-css",
@@ -2148,9 +2352,9 @@ dependencies = [
 "qcheck",
 "qcheck-macros",
 "radicle-git-ext",
-
 "radicle-ssh",
 "serde",
 "sqlite",
+
 "ssh-agent-lib",
 "ssh-key",
 "tempfile",
 "thiserror",
@@ -2171,12 +2375,12 @@ dependencies = [
 "bstr",
 "either",
 "gix-actor",
-
 "gix-features 0.38.1",
+
 "gix-features 0.38.2",
 "gix-hash 0.14.2",
 "gix-odb",
 "gix-pack",
-
 "gix-protocol 0.45.0",
-
 "gix-transport 0.42.0",
+
 "gix-protocol 0.45.1",
+
 "gix-transport 0.42.1",
 "log",
 "nonempty 0.9.0",
 "radicle",
@@ -2258,16 +2462,6 @@ dependencies = [
]

[[package]]
-
name = "radicle-ssh"
-
version = "0.9.0"
-
dependencies = [
-
 "byteorder",
-
 "log",
-
 "thiserror",
-
 "zeroize",
-
]
-

-
[[package]]
name = "radicle-std-ext"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2356,6 +2550,15 @@ dependencies = [
]

[[package]]
+
name = "raunch"
+
version = "1.0.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1a67226789ccd419c55f5c826e04f7d58690b0593364f61bc8ed6e7dbbab49c5"
+
dependencies = [
+
 "libc",
+
]
+

+
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2365,6 +2568,15 @@ dependencies = [
]

[[package]]
+
name = "redox_syscall"
+
version = "0.5.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+
dependencies = [
+
 "bitflags 2.5.0",
+
]
+

+
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2372,9 +2584,9 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"

[[package]]
name = "regex"
-
version = "1.10.4"
+
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
 "aho-corasick",
 "memchr",
@@ -2384,9 +2596,9 @@ dependencies = [

[[package]]
name = "regex-automata"
-
version = "0.4.6"
+
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
 "aho-corasick",
 "memchr",
@@ -2395,9 +2607,9 @@ dependencies = [

[[package]]
name = "regex-syntax"
-
version = "0.8.3"
+
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"

[[package]]
name = "rfc6979"
@@ -2431,6 +2643,15 @@ dependencies = [
]

[[package]]
+
name = "rustc_version"
+
version = "0.4.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+
dependencies = [
+
 "semver",
+
]
+

+
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2445,9 +2666,9 @@ dependencies = [

[[package]]
name = "ryu"
-
version = "1.0.17"
+
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"

[[package]]
name = "salsa20"
@@ -2499,30 +2720,45 @@ dependencies = [
]

[[package]]
+
name = "secrecy"
+
version = "0.8.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e"
+
dependencies = [
+
 "zeroize",
+
]
+

+
[[package]]
+
name = "semver"
+
version = "1.0.23"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+

+
[[package]]
name = "serde"
-
version = "1.0.198"
+
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
+
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
-
version = "1.0.198"
+
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
+
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
 "proc-macro2",
 "quote",
-
 "syn 2.0.60",
+
 "syn 2.0.66",
]

[[package]]
name = "serde_json"
-
version = "1.0.116"
+
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
 "indexmap",
 "itoa",
@@ -2531,6 +2767,15 @@ dependencies = [
]

[[package]]
+
name = "service-binding"
+
version = "3.0.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "25663b9d8b83193ef83183edb3fd01e5447adb3e5baa35466ff9a6cd2408d6e8"
+
dependencies = [
+
 "raunch",
+
]
+

+
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2624,9 +2869,9 @@ dependencies = [

[[package]]
name = "snapbox-macros"
-
version = "0.3.8"
+
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e1c4b838b05d15ab22754068cb73500b2f3b07bf09d310e15b27f88160f1de40"
+
checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d"
dependencies = [
 "anstream",
]
@@ -2698,6 +2943,21 @@ dependencies = [
]

[[package]]
+
name = "ssh-agent-lib"
+
version = "0.4.0"
+
source = "git+https://github.com/wiktor-k/ssh-agent-lib?rev=refs/pull/81/head#d247e7edd91b80fa31feb19d18aa427d68c58b0c"
+
dependencies = [
+
 "byteorder",
+
 "secrecy",
+
 "service-binding",
+
 "signature 2.2.0",
+
 "ssh-encoding",
+
 "ssh-key",
+
 "subtle",
+
 "thiserror",
+
]
+

+
[[package]]
name = "ssh-cipher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2732,6 +2992,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc"
dependencies = [
 "bcrypt-pbkdf",
+
 "ed25519-dalek",
+
 "num-bigint-dig",
 "p256",
 "p384",
 "p521",
@@ -2747,6 +3009,12 @@ dependencies = [
]

[[package]]
+
name = "stable_deref_trait"
+
version = "1.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+

+
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2765,9 +3033,9 @@ dependencies = [

[[package]]
name = "syn"
-
version = "2.0.60"
+
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
 "proc-macro2",
 "quote",
@@ -2775,10 +3043,21 @@ dependencies = [
]

[[package]]
+
name = "synstructure"
+
version = "0.13.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
]
+

+
[[package]]
name = "tar"
-
version = "0.4.40"
+
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
+
checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909"
dependencies = [
 "filetime",
 "libc",
@@ -2823,22 +3102,22 @@ dependencies = [

[[package]]
name = "thiserror"
-
version = "1.0.59"
+
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
 "thiserror-impl",
]

[[package]]
name = "thiserror-impl"
-
version = "1.0.59"
+
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
 "proc-macro2",
 "quote",
-
 "syn 2.0.60",
+
 "syn 2.0.66",
]

[[package]]
@@ -2881,6 +3160,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6"

[[package]]
+
name = "tinystr"
+
version = "0.7.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+
dependencies = [
+
 "displaydoc",
+
 "zerovec",
+
]
+

+
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2906,13 +3195,23 @@ dependencies = [
]

[[package]]
+
name = "tree-sitter"
+
version = "0.22.6"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca"
+
dependencies = [
+
 "cc",
+
 "regex",
+
]
+

+
[[package]]
name = "tree-sitter-bash"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da2032c37eb2ce29fd18df7d3b94355fec8d6d854d8f80934955df542b5906"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2922,7 +3221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bbd5f3d8658c08581f8f2adac6c391c2e9fa00fe9246bf6c5f52213b9cc6b72"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2932,7 +3231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3306ddefa1d2681adda2613d11974ffabfbeb215e23235da6c862f3493a04fd"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2942,7 +3241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad6d11f19441b961af2fda7f12f5d0dac325f6d6de83836a1d3750018cc5114"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2953,17 +3252,17 @@ checksum = "042342584c5a7a0b833d9fc4e2bdab3f9868ddc6c4b339a1e01451c6720868bc"
dependencies = [
 "regex",
 "thiserror",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
name = "tree-sitter-html"
-
version = "0.20.0"
+
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "017822b6bd42843c4bd67fabb834f61ce23254e866282dd93871350fd6b7fa1d"
+
checksum = "95b3492b08a786bf5cc79feb0ef2ff3b115d5174364e0ddfd7860e0b9b088b53"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.22.6",
]

[[package]]
@@ -2973,7 +3272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9a38a9c679b55cc8d17350381ec08d69fa1a17a53fcf197f344516e485ed4d"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2983,7 +3282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c20d3ef8d202430b644a307e6299d84bf8ed87fa1b796e4638f8805a595060c"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -2993,7 +3292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c93b1b1fbd0d399db3445f51fd3058e43d0b4dcff62ddbdb46e66550978aa5"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -3003,7 +3302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d50ef383469df8485f024c5fb01faced8cb90368192a7ba02605b43b2427fe"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -3013,7 +3312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -3023,7 +3322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca517f578a98b23d20780247cc2688407fa81effad5b627a5a364ec3339b53e8"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -3033,7 +3332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8bc1d2c24276a48ef097a71b56888ac9db63717e8f8d0b324668a27fd619670"
dependencies = [
 "cc",
-
 "tree-sitter",
+
 "tree-sitter 0.20.10",
]

[[package]]
@@ -3043,12 +3342,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"

[[package]]
-
name = "unicode-bidi"
-
version = "0.3.15"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-

-
[[package]]
name = "unicode-display-width"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3080,9 +3373,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"

[[package]]
name = "unicode-width"
-
version = "0.1.11"
+
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"

[[package]]
name = "universal-hash"
@@ -3096,9 +3389,9 @@ dependencies = [

[[package]]
name = "url"
-
version = "2.5.0"
+
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+
checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56"
dependencies = [
 "form_urlencoded",
 "idna",
@@ -3106,10 +3399,22 @@ dependencies = [
]

[[package]]
+
name = "utf16_iter"
+
version = "1.0.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+

+
[[package]]
+
name = "utf8_iter"
+
version = "1.0.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+

+
[[package]]
name = "utf8parse"
-
version = "0.2.1"
+
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"

[[package]]
name = "vcpkg"
@@ -3160,7 +3465,7 @@ dependencies = [
 "once_cell",
 "proc-macro2",
 "quote",
-
 "syn 2.0.60",
+
 "syn 2.0.66",
 "wasm-bindgen-shared",
]

@@ -3182,7 +3487,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
 "proc-macro2",
 "quote",
-
 "syn 2.0.60",
+
 "syn 2.0.66",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]
@@ -3195,9 +3500,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"

[[package]]
name = "winapi-util"
-
version = "0.1.7"
+
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697"
+
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
 "windows-sys 0.52.0",
]
@@ -3361,14 +3666,26 @@ dependencies = [

[[package]]
name = "winnow"
-
version = "0.6.8"
+
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
+
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
dependencies = [
 "memchr",
]

[[package]]
+
name = "write16"
+
version = "1.0.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+

+
[[package]]
+
name = "writeable"
+
version = "0.5.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+

+
[[package]]
name = "xattr"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3386,7 +3703,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

[[package]]
+
name = "yoke"
+
version = "0.7.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
+
dependencies = [
+
 "serde",
+
 "stable_deref_trait",
+
 "yoke-derive",
+
 "zerofrom",
+
]
+

+
[[package]]
+
name = "yoke-derive"
+
version = "0.7.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
 "synstructure",
+
]
+

+
[[package]]
+
name = "zerofrom"
+
version = "0.1.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
+
dependencies = [
+
 "zerofrom-derive",
+
]
+

+
[[package]]
+
name = "zerofrom-derive"
+
version = "0.1.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
 "synstructure",
+
]
+

+
[[package]]
name = "zeroize"
-
version = "1.7.0"
+
version = "1.8.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+

+
[[package]]
+
name = "zerovec"
+
version = "0.10.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c"
+
dependencies = [
+
 "yoke",
+
 "zerofrom",
+
 "zerovec-derive",
+
]
+

+
[[package]]
+
name = "zerovec-derive"
+
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.66",
+
]
modified Cargo.toml
@@ -11,7 +11,6 @@ members = [
  "radicle-fetch",
  "radicle-node",
  "radicle-remote-helper",
-
  "radicle-ssh",
  "radicle-tools",
  "radicle-signals",
]
@@ -22,7 +21,6 @@ default-members = [
  "radicle-crdt",
  "radicle-crypto",
  "radicle-node",
-
  "radicle-ssh",
  "radicle-remote-helper",
  "radicle-term",
  "radicle-signals",
modified radicle-cli/Cargo.toml
@@ -45,6 +45,7 @@ tree-sitter-bash = { version = "0.20" }
tree-sitter-go = { version = "0.20.0" }
tree-sitter-md = { version = "0.1.5" }
zeroize = { version = "1.1" }
+
ssh-agent-lib = { version = "0.4.0", git = "https://github.com/wiktor-k/ssh-agent-lib", rev = "refs/pull/81/head", default-features = false }

[dependencies.radicle]
version = "0.11.0"
modified radicle-cli/src/commands/auth.rs
@@ -1,6 +1,6 @@
#![allow(clippy::or_fun_call)]
use std::ffi::OsString;
-
use std::ops::Not as _;
+
use std::ops::{Deref as _, Not as _};
use std::str::FromStr;

use anyhow::anyhow;
@@ -126,7 +126,7 @@ pub fn init(options: Options) -> anyhow::Result<()> {
                    spinner.warn();
                }
            }
-
            Err(e) if e.is_not_running() => {
+
            Err(_) if ssh::agent::Agent::pid().is_none() => {
                agent = false;
            }
            Err(e) => Err(e)?,
@@ -180,7 +180,16 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {
    // register it; only if it is running.
    match ssh::agent::Agent::connect() {
        Ok(mut agent) => {
-
            if agent.request_identities()?.contains(&profile.public_key) {
+
            let ids = agent.request_identities().unwrap();
+
            let has_pubkey = ids.iter().any(|id| {
+
                if let ssh_agent_lib::ssh_key::public::KeyData::Ed25519(key) = id.pubkey {
+
                    *key.as_ref() == **profile.public_key.deref()
+
                } else {
+
                    false
+
                }
+
            });
+

+
            if has_pubkey {
                term::success!("Radicle key already in ssh-agent");
                return Ok(());
            }
@@ -197,7 +206,7 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {

            return Ok(());
        }
-
        Err(e) if e.is_not_running() => {}
+
        Err(_) if ssh::agent::Agent::pid().is_none() => {}
        Err(e) => Err(e)?,
    };

modified radicle-cli/src/commands/self.rs
@@ -144,11 +144,15 @@ fn all(profile: &Profile) -> anyhow::Result<()> {
    ]);

    let ssh_agent = match ssh::agent::Agent::connect() {
-
        Ok(c) => term::format::positive(format!(
+
        Ok(_) => term::format::positive(format!(
            "running ({})",
-
            c.pid().map(|p| p.to_string()).unwrap_or(String::from("?"))
+
            ssh::agent::Agent::pid()
+
                .map(|p| p.to_string())
+
                .unwrap_or(String::from("?"))
        )),
-
        Err(e) if e.is_not_running() => term::format::yellow(String::from("not running")),
+
        Err(_) if ssh::agent::Agent::pid().is_none() => {
+
            term::format::yellow(String::from("not running"))
+
        }
        Err(e) => term::format::negative(format!("error: {e}")),
    };
    table.push([
modified radicle-crypto/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2021"

[features]
test = ["fastrand", "qcheck"]
-
ssh = ["radicle-ssh", "ssh-key"]
+
ssh = ["ssh-agent-lib", "ssh-key"]

[dependencies]
amplify = { version = "4.0.0" }
@@ -25,6 +25,7 @@ serde = { version = "1", features = ["derive"] }
sqlite = { version = "0.32.0", optional = true, features = ["bundled"] }
thiserror = { version = "1" }
zeroize = { version = "1.5.7" }
+
ssh-agent-lib = { version = "0.4.0", git = "https://github.com/wiktor-k/ssh-agent-lib", rev = "refs/pull/81/head", default-features = false, optional = true }

[dependencies.radicle-git-ext]
version = "0.7.0"
@@ -42,12 +43,6 @@ version = "1"
default-features = false
optional = true

-
[dependencies.radicle-ssh]
-
path = "../radicle-ssh"
-
version = "0"
-
default-features = false
-
optional = true
-

[dev-dependencies]
fastrand = { version = "2.0.0", default-features = false }
qcheck-macros = { version = "1", default-features = false }
modified radicle-crypto/src/ssh.rs
@@ -5,13 +5,7 @@ use std::io;

use thiserror::Error;

-
use radicle_ssh::encoding;
-
use radicle_ssh::encoding::Encodable;
-
use radicle_ssh::encoding::Encoding;
-
use radicle_ssh::encoding::Reader;
-

use crate as crypto;
-
use crate::PublicKey;

pub use keystore::{Keystore, Passphrase};

@@ -126,76 +120,25 @@ pub enum SignatureError {
    #[error(transparent)]
    Invalid(#[from] crypto::Error),
    #[error(transparent)]
-
    Encoding(#[from] encoding::Error),
+
    Encoding(#[from] ssh_agent_lib::proto::ProtoError),
    #[error("unknown algorithm '{0}'")]
    UnknownAlgorithm(String),
}

-
impl Encodable for crypto::Signature {
-
    type Error = SignatureError;
-

-
    fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
-
        let buf = r.read_string()?;
-
        let mut inner_strs = buf.reader(0);
-

-
        let sig_type = inner_strs.read_string()?;
-
        if sig_type != b"ssh-ed25519" {
-
            return Err(SignatureError::UnknownAlgorithm(
-
                String::from_utf8_lossy(sig_type).to_string(),
-
            ));
-
        }
-
        let sig = crypto::Signature::try_from(inner_strs.read_string()?)?;
-

-
        Ok(sig)
-
    }
-

-
    fn write<E: Encoding>(&self, buf: &mut E) {
-
        let mut inner_strs = Vec::new();
-
        inner_strs.extend_ssh_string(b"ssh-ed25519");
-
        inner_strs.extend_ssh_string(self.as_ref());
-
        buf.extend_ssh_string(&inner_strs);
-
    }
-
}
-

#[derive(Debug, Error)]
pub enum PublicKeyError {
    #[error(transparent)]
    Invalid(#[from] crypto::Error),
    #[error(transparent)]
-
    Encoding(#[from] encoding::Error),
+
    Encoding(#[from] ssh_agent_lib::proto::ProtoError),
    #[error("unknown algorithm '{0}'")]
    UnknownAlgorithm(String),
}

-
impl Encodable for PublicKey {
-
    type Error = PublicKeyError;
-

-
    fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
-
        match r.read_string()? {
-
            b"ssh-ed25519" => {
-
                let s = r.read_string()?;
-
                let p = PublicKey::try_from(s)?;
-

-
                Ok(p)
-
            }
-
            v => Err(PublicKeyError::UnknownAlgorithm(
-
                String::from_utf8_lossy(v).to_string(),
-
            )),
-
        }
-
    }
-

-
    fn write<E: Encoding>(&self, w: &mut E) {
-
        let mut str_w: Vec<u8> = Vec::<u8>::new();
-
        str_w.extend_ssh_string(b"ssh-ed25519");
-
        str_w.extend_ssh_string(&self[..]);
-
        w.extend_ssh_string(&str_w)
-
    }
-
}
-

#[derive(Debug, Error)]
pub enum SecretKeyError {
    #[error(transparent)]
-
    Encoding(#[from] encoding::Error),
+
    Encoding(#[from] ssh_agent_lib::proto::ProtoError),
    #[error(transparent)]
    Crypto(#[from] crypto::Error),
    #[error(transparent)]
@@ -206,80 +149,13 @@ pub enum SecretKeyError {
    Mismatch,
}

-
impl Encodable for crypto::SecretKey {
-
    type Error = SecretKeyError;
-

-
    fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
-
        match r.read_string()? {
-
            b"ssh-ed25519" => {
-
                let public = r.read_string()?;
-
                let pair = r.read_string()?;
-
                let _comment = r.read_string()?;
-
                let key = crypto::SecretKey::try_from(pair)?;
-

-
                if public != key.public_key().as_ref() {
-
                    return Err(SecretKeyError::Mismatch);
-
                }
-
                Ok(key)
-
            }
-
            s => Err(SecretKeyError::UnknownAlgorithm(
-
                String::from_utf8_lossy(s).to_string(),
-
            )),
-
        }
-
    }
-

-
    fn write<E: Encoding>(&self, buf: &mut E) {
-
        let public = self.0.public_key();
-

-
        buf.extend_ssh_string(b"ssh-ed25519");
-
        buf.extend_ssh_string(public.as_ref());
-
        buf.extend_ssh_string(self.0.as_ref());
-
        buf.extend_ssh_string(b"radicle");
-
    }
-
}
-

#[cfg(test)]
mod test {
-
    use std::sync::{Arc, Mutex};
-

-
    use qcheck_macros::quickcheck;
-

-
    use crate as crypto;
-
    use crate::{PublicKey, SecretKey};
-
    use radicle_ssh::agent::client::{AgentClient, ClientStream, Error};
-
    use radicle_ssh::encoding::*;
-

-
    #[derive(Clone, Default)]
-
    struct DummyStream {
-
        incoming: Arc<Mutex<Vec<u8>>>,
-
    }
-

-
    impl ClientStream for DummyStream {
-
        fn connect<P>(_path: P) -> Result<AgentClient<Self>, Error>
-
        where
-
            P: AsRef<std::path::Path> + Send,
-
        {
-
            panic!("This function should never be called!")
-
        }
-

-
        fn request(&mut self, buf: &[u8]) -> Result<Buffer, Error> {
-
            *self.incoming.lock().unwrap() = buf.to_vec();
-

-
            Ok(Buffer::default())
-
        }
-
    }
-

-
    #[quickcheck]
-
    fn prop_encode_decode_sk(input: [u8; 64]) {
-
        let mut buf = Buffer::default();
-
        let sk = crypto::SecretKey::from(input);
-
        sk.write(&mut buf);
-

-
        let mut cursor = buf.reader(0);
-
        let output = SecretKey::read(&mut cursor).unwrap();
-

-
        assert_eq!(sk, output);
-
    }
+
    use crate::PublicKey;
+
    use ssh_agent_lib::blocking::Client;
+
    use ssh_agent_lib::proto::SignRequest;
+
    use ssh_agent_lib::ssh_key::public::{Ed25519PublicKey, KeyData};
+
    use std::ops::Deref;

    #[test]
    fn test_agent_encoding_remove() {
@@ -297,15 +173,15 @@ mod test {
            37, 145, 51, 176, 174, 61, 136, 160, 107, 4, 95, 175, 144, // Key
        ];

-
        let stream = DummyStream::default();
-
        let mut agent = AgentClient::connect(stream.clone());
+
        let mut client = Client::new(std::io::Cursor::new(vec![]));

-
        agent.remove_identity(&pk).unwrap();
+
        client
+
            .remove_identity(ssh_agent_lib::proto::RemoveIdentity {
+
                pubkey: KeyData::Ed25519(Ed25519PublicKey(**pk.deref())),
+
            })
+
            .ok();

-
        assert_eq!(
-
            stream.incoming.lock().unwrap().as_slice(),
-
            expected.as_slice()
-
        );
+
        assert_eq!(client.into_inner().into_inner(), expected.as_slice());
    }

    #[test]
@@ -327,15 +203,17 @@ mod test {
            0, 0, 0, 0, // Signature flags
        ];

-
        let stream = DummyStream::default();
-
        let mut agent = AgentClient::connect(stream.clone());
+
        let mut client = Client::new(std::io::Cursor::new(vec![]));
        let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];

-
        agent.sign(&pk, &data).ok();
+
        client
+
            .sign(SignRequest {
+
                pubkey: KeyData::Ed25519(Ed25519PublicKey(**pk.deref())),
+
                data: data.clone(),
+
                flags: 0,
+
            })
+
            .ok();

-
        assert_eq!(
-
            stream.incoming.lock().unwrap().as_slice(),
-
            expected.as_slice()
-
        );
+
        assert_eq!(client.into_inner().into_inner(), expected);
    }
}
modified radicle-crypto/src/ssh/agent.rs
@@ -1,9 +1,9 @@
use std::ops::{Deref, DerefMut};
use std::sync::Mutex;

-
pub use radicle_ssh::agent::client::AgentClient;
-
pub use radicle_ssh::agent::client::Error;
-
pub use radicle_ssh::{self as ssh, agent::client::ClientStream};
+
use ssh_agent_lib::proto::Credential;
+
pub use ssh_agent_lib::proto::ProtoError as Error;
+
use ssh_key::public::KeyData;

use crate::{PublicKey, SecretKey, Signature, Signer, SignerError};

@@ -13,28 +13,50 @@ pub use std::net::TcpStream as Stream;
pub use std::os::unix::net::UnixStream as Stream;

pub struct Agent {
-
    client: AgentClient<Stream>,
+
    client: ssh_agent_lib::blocking::Client<Stream>,
}

impl Agent {
    /// Connect to a running SSH agent.
-
    pub fn connect() -> Result<Self, ssh::agent::client::Error> {
-
        Stream::connect_env().map(|client| Self { client })
+
    pub fn connect() -> Result<Self, Error> {
+
        Ok(Agent {
+
            client: ssh_agent_lib::blocking::Client::new(Stream::connect(
+
                std::env::var("SSH_AUTH_SOCK").unwrap(),
+
            )?),
+
        })
    }

    /// Register a key with the agent.
-
    pub fn register(&mut self, key: &SecretKey) -> Result<(), ssh::Error> {
-
        self.client.add_identity(key, &[])
+
    pub fn register(&mut self, key: &SecretKey) -> Result<(), Error> {
+
        self.client
+
            .add_identity(ssh_agent_lib::proto::AddIdentity {
+
                credential: Credential::Key {
+
                    privkey: ssh_key::private::KeypairData::Ed25519(
+
                        ssh_key::private::Ed25519Keypair::from_bytes(&*key.deref()).unwrap(),
+
                    ),
+
                    comment: "".into(),
+
                },
+
            })
+
            .unwrap();
+

+
        Ok(())
    }

    /// Get a signer from this agent, given the public key.
    pub fn signer(self, key: PublicKey) -> AgentSigner {
        AgentSigner::new(self, key)
    }
+

+
    pub fn pid() -> Option<u32> {
+
        use std::str::FromStr;
+
        std::env::var("SSH_AGENT_PID")
+
            .ok()
+
            .and_then(|v| u32::from_str(&v).ok())
+
    }
}

impl Deref for Agent {
-
    type Target = AgentClient<Stream>;
+
    type Target = ssh_agent_lib::blocking::Client<Stream>;

    fn deref(&self) -> &Self::Target {
        &self.client
@@ -61,9 +83,14 @@ impl AgentSigner {
    }

    pub fn is_ready(&self) -> Result<bool, Error> {
-
        let ids = self.agent.lock().unwrap().request_identities()?;
-

-
        Ok(ids.contains(&self.public))
+
        let ids = self.agent.lock().unwrap().request_identities().unwrap();
+
        Ok(ids.iter().any(|id| {
+
            if let KeyData::Ed25519(key) = id.pubkey {
+
                *key.as_ref() == **self.public.deref()
+
            } else {
+
                false
+
            }
+
        }))
    }

    /// Box this signer into a [`Signer`].
@@ -85,11 +112,14 @@ impl Signer for AgentSigner {
        let sig = self
            .agent
            .lock()
-
            // We'll take our chances here; the worse that can happen is the agent returns an error.
            .unwrap_or_else(|e| e.into_inner())
-
            .sign(&self.public, msg)
+
            .sign(ssh_agent_lib::proto::SignRequest {
+
                pubkey: KeyData::Ed25519(ssh_key::public::Ed25519PublicKey(**self.public.deref())),
+
                data: msg.into(),
+
                flags: 0,
+
            })
            .map_err(SignerError::new)?;
-

-
        Ok(Signature::from(sig))
+
        let bytes: [u8; 64] = sig.as_bytes().try_into().unwrap();
+
        Ok(Signature::from(bytes))
    }
}
deleted radicle-ssh/Cargo.toml
@@ -1,19 +0,0 @@
-
[package]
-
name = "radicle-ssh"
-
description = "Radicle SSH library"
-
homepage = "https://radicle.xyz"
-
repository = "https://app.radicle.xyz/seeds/seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5"
-
license = "Apache-2.0"
-
version = "0.9.0"
-
authors = [
-
  "Fintan Halpenny <fintan.halpenny@gmail.com>",
-
  "Pierre-Étienne Meunier <pe@pijul.org>",
-
  "cloudhead <cloudhead@radicle.xyz>"
-
]
-
edition = "2021"
-

-
[dependencies]
-
byteorder = "1.4"
-
log = "0.4"
-
thiserror = "1.0"
-
zeroize = "1.5.7"
deleted radicle-ssh/LICENSE
@@ -1,177 +0,0 @@
-

-
                                 Apache License
-
                           Version 2.0, January 2004
-
                        http://www.apache.org/licenses/
-

-
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-

-
   1. Definitions.
-

-
      "License" shall mean the terms and conditions for use, reproduction,
-
      and distribution as defined by Sections 1 through 9 of this document.
-

-
      "Licensor" shall mean the copyright owner or entity authorized by
-
      the copyright owner that is granting the License.
-

-
      "Legal Entity" shall mean the union of the acting entity and all
-
      other entities that control, are controlled by, or are under common
-
      control with that entity. For the purposes of this definition,
-
      "control" means (i) the power, direct or indirect, to cause the
-
      direction or management of such entity, whether by contract or
-
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-
      outstanding shares, or (iii) beneficial ownership of such entity.
-

-
      "You" (or "Your") shall mean an individual or Legal Entity
-
      exercising permissions granted by this License.
-

-
      "Source" form shall mean the preferred form for making modifications,
-
      including but not limited to software source code, documentation
-
      source, and configuration files.
-

-
      "Object" form shall mean any form resulting from mechanical
-
      transformation or translation of a Source form, including but
-
      not limited to compiled object code, generated documentation,
-
      and conversions to other media types.
-

-
      "Work" shall mean the work of authorship, whether in Source or
-
      Object form, made available under the License, as indicated by a
-
      copyright notice that is included in or attached to the work
-
      (an example is provided in the Appendix below).
-

-
      "Derivative Works" shall mean any work, whether in Source or Object
-
      form, that is based on (or derived from) the Work and for which the
-
      editorial revisions, annotations, elaborations, or other modifications
-
      represent, as a whole, an original work of authorship. For the purposes
-
      of this License, Derivative Works shall not include works that remain
-
      separable from, or merely link (or bind by name) to the interfaces of,
-
      the Work and Derivative Works thereof.
-

-
      "Contribution" shall mean any work of authorship, including
-
      the original version of the Work and any modifications or additions
-
      to that Work or Derivative Works thereof, that is intentionally
-
      submitted to Licensor for inclusion in the Work by the copyright owner
-
      or by an individual or Legal Entity authorized to submit on behalf of
-
      the copyright owner. For the purposes of this definition, "submitted"
-
      means any form of electronic, verbal, or written communication sent
-
      to the Licensor or its representatives, including but not limited to
-
      communication on electronic mailing lists, source code control systems,
-
      and issue tracking systems that are managed by, or on behalf of, the
-
      Licensor for the purpose of discussing and improving the Work, but
-
      excluding communication that is conspicuously marked or otherwise
-
      designated in writing by the copyright owner as "Not a Contribution."
-

-
      "Contributor" shall mean Licensor and any individual or Legal Entity
-
      on behalf of whom a Contribution has been received by Licensor and
-
      subsequently incorporated within the Work.
-

-
   2. Grant of Copyright License. Subject to the terms and conditions of
-
      this License, each Contributor hereby grants to You a perpetual,
-
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-
      copyright license to reproduce, prepare Derivative Works of,
-
      publicly display, publicly perform, sublicense, and distribute the
-
      Work and such Derivative Works in Source or Object form.
-

-
   3. Grant of Patent License. Subject to the terms and conditions of
-
      this License, each Contributor hereby grants to You a perpetual,
-
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-
      (except as stated in this section) patent license to make, have made,
-
      use, offer to sell, sell, import, and otherwise transfer the Work,
-
      where such license applies only to those patent claims licensable
-
      by such Contributor that are necessarily infringed by their
-
      Contribution(s) alone or by combination of their Contribution(s)
-
      with the Work to which such Contribution(s) was submitted. If You
-
      institute patent litigation against any entity (including a
-
      cross-claim or counterclaim in a lawsuit) alleging that the Work
-
      or a Contribution incorporated within the Work constitutes direct
-
      or contributory patent infringement, then any patent licenses
-
      granted to You under this License for that Work shall terminate
-
      as of the date such litigation is filed.
-

-
   4. Redistribution. You may reproduce and distribute copies of the
-
      Work or Derivative Works thereof in any medium, with or without
-
      modifications, and in Source or Object form, provided that You
-
      meet the following conditions:
-

-
      (a) You must give any other recipients of the Work or
-
          Derivative Works a copy of this License; and
-

-
      (b) You must cause any modified files to carry prominent notices
-
          stating that You changed the files; and
-

-
      (c) You must retain, in the Source form of any Derivative Works
-
          that You distribute, all copyright, patent, trademark, and
-
          attribution notices from the Source form of the Work,
-
          excluding those notices that do not pertain to any part of
-
          the Derivative Works; and
-

-
      (d) If the Work includes a "NOTICE" text file as part of its
-
          distribution, then any Derivative Works that You distribute must
-
          include a readable copy of the attribution notices contained
-
          within such NOTICE file, excluding those notices that do not
-
          pertain to any part of the Derivative Works, in at least one
-
          of the following places: within a NOTICE text file distributed
-
          as part of the Derivative Works; within the Source form or
-
          documentation, if provided along with the Derivative Works; or,
-
          within a display generated by the Derivative Works, if and
-
          wherever such third-party notices normally appear. The contents
-
          of the NOTICE file are for informational purposes only and
-
          do not modify the License. You may add Your own attribution
-
          notices within Derivative Works that You distribute, alongside
-
          or as an addendum to the NOTICE text from the Work, provided
-
          that such additional attribution notices cannot be construed
-
          as modifying the License.
-

-
      You may add Your own copyright statement to Your modifications and
-
      may provide additional or different license terms and conditions
-
      for use, reproduction, or distribution of Your modifications, or
-
      for any such Derivative Works as a whole, provided Your use,
-
      reproduction, and distribution of the Work otherwise complies with
-
      the conditions stated in this License.
-

-
   5. Submission of Contributions. Unless You explicitly state otherwise,
-
      any Contribution intentionally submitted for inclusion in the Work
-
      by You to the Licensor shall be under the terms and conditions of
-
      this License, without any additional terms or conditions.
-
      Notwithstanding the above, nothing herein shall supersede or modify
-
      the terms of any separate license agreement you may have executed
-
      with Licensor regarding such Contributions.
-

-
   6. Trademarks. This License does not grant permission to use the trade
-
      names, trademarks, service marks, or product names of the Licensor,
-
      except as required for reasonable and customary use in describing the
-
      origin of the Work and reproducing the content of the NOTICE file.
-

-
   7. Disclaimer of Warranty. Unless required by applicable law or
-
      agreed to in writing, Licensor provides the Work (and each
-
      Contributor provides its Contributions) on an "AS IS" BASIS,
-
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-
      implied, including, without limitation, any warranties or conditions
-
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-
      PARTICULAR PURPOSE. You are solely responsible for determining the
-
      appropriateness of using or redistributing the Work and assume any
-
      risks associated with Your exercise of permissions under this License.
-

-
   8. Limitation of Liability. In no event and under no legal theory,
-
      whether in tort (including negligence), contract, or otherwise,
-
      unless required by applicable law (such as deliberate and grossly
-
      negligent acts) or agreed to in writing, shall any Contributor be
-
      liable to You for damages, including any direct, indirect, special,
-
      incidental, or consequential damages of any character arising as a
-
      result of this License or out of the use or inability to use the
-
      Work (including but not limited to damages for loss of goodwill,
-
      work stoppage, computer failure or malfunction, or any and all
-
      other commercial damages or losses), even if such Contributor
-
      has been advised of the possibility of such damages.
-

-
   9. Accepting Warranty or Additional Liability. While redistributing
-
      the Work or Derivative Works thereof, You may choose to offer,
-
      and charge a fee for, acceptance of support, warranty, indemnity,
-
      or other liability obligations and/or rights consistent with this
-
      License. However, in accepting such obligations, You may act only
-
      on Your own behalf and on Your sole responsibility, not on behalf
-
      of any other Contributor, and only if You agree to indemnify,
-
      defend, and hold each Contributor harmless for any liability
-
      incurred by, or claims asserted against, such Contributor by reason
-
      of your accepting any such warranty or additional liability.
-

-
   END OF TERMS AND CONDITIONS
deleted radicle-ssh/src/agent.rs
@@ -1,15 +0,0 @@
-
/// Write clients for SSH agents.
-
pub mod client;
-

-
mod msg;
-

-
/// Constraints on how keys can be used.
-
#[derive(Debug, PartialEq, Eq)]
-
pub enum Constraint {
-
    /// The key shall disappear from the agent's memory after that many seconds.
-
    KeyLifetime { seconds: u32 },
-
    /// Signatures need to be confirmed by the agent (for instance using a dialog).
-
    Confirm,
-
    /// Custom constraints
-
    Extensions { name: Vec<u8>, details: Vec<u8> },
-
}
deleted radicle-ssh/src/agent/client.rs
@@ -1,423 +0,0 @@
-
use std::fmt;
-
use std::io::{Read, Write};
-
use std::ops::DerefMut;
-
use std::os::unix::net::UnixStream;
-
use std::path::Path;
-
use std::str::FromStr;
-

-
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
-
use log::*;
-
use thiserror::Error;
-
use zeroize::Zeroize as _;
-

-
use crate::agent::msg;
-
use crate::agent::Constraint;
-
use crate::encoding::{self, Encodable};
-
use crate::encoding::{Buffer, Encoding, Reader};
-

-
/// An ed25519 Signature.
-
pub type Signature = [u8; 64];
-

-
#[derive(Debug, Error)]
-
pub enum Error {
-
    /// Agent protocol error.
-
    #[error("Agent protocol error")]
-
    AgentProtocolError,
-
    #[error("Agent failure")]
-
    AgentFailure,
-
    #[error("Unable to connect to ssh-agent. The environment variable `SSH_AUTH_SOCK` was set, but it points to a nonexistent file or directory.")]
-
    BadAuthSock,
-
    #[error(transparent)]
-
    Encoding(#[from] encoding::Error),
-
    #[error("Environment variable `{0}` not found")]
-
    EnvVar(&'static str),
-
    #[error(transparent)]
-
    Io(#[from] std::io::Error),
-
    #[error(transparent)]
-
    Private(Box<dyn std::error::Error + Send + Sync + 'static>),
-
    #[error(transparent)]
-
    Public(Box<dyn std::error::Error + Send + Sync + 'static>),
-
    #[error(transparent)]
-
    Signature(Box<dyn std::error::Error + Send + Sync + 'static>),
-
}
-

-
impl Error {
-
    pub fn is_not_running(&self) -> bool {
-
        matches!(self, Self::EnvVar("SSH_AUTH_SOCK"))
-
    }
-
}
-

-
/// SSH agent client.
-
pub struct AgentClient<S> {
-
    stream: S,
-
}
-

-
impl<S> AgentClient<S> {
-
    /// Connect to an SSH agent via the provided stream (on Unix, usually a Unix-domain socket).
-
    pub fn connect(stream: S) -> Self {
-
        AgentClient { stream }
-
    }
-

-
    /// Get the agent PID.
-
    pub fn pid(&self) -> Option<u32> {
-
        std::env::var("SSH_AGENT_PID")
-
            .ok()
-
            .and_then(|v| u32::from_str(&v).ok())
-
    }
-
}
-

-
pub trait ClientStream: Sized + Send + Sync {
-
    /// Send an agent request through the stream and read the response.
-
    fn request(&mut self, req: &[u8]) -> Result<Buffer, Error>;
-

-
    /// How to connect the streaming socket
-
    fn connect<P>(path: P) -> Result<AgentClient<Self>, Error>
-
    where
-
        P: AsRef<Path> + Send;
-

-
    fn connect_env() -> Result<AgentClient<Self>, Error> {
-
        let Ok(var) = std::env::var("SSH_AUTH_SOCK") else {
-
            return Err(Error::EnvVar("SSH_AUTH_SOCK"));
-
        };
-
        match Self::connect(var) {
-
            Err(Error::Io(io_err)) if io_err.kind() == std::io::ErrorKind::NotFound => {
-
                Err(Error::BadAuthSock)
-
            }
-
            other => other,
-
        }
-
    }
-
}
-

-
impl<S: ClientStream> AgentClient<S> {
-
    /// Send a key to the agent, with a (possibly empty) slice of constraints
-
    /// to apply when using the key to sign.
-
    pub fn add_identity<K>(&mut self, key: &K, constraints: &[Constraint]) -> Result<(), Error>
-
    where
-
        K: Encodable,
-
        K::Error: std::error::Error + Send + Sync + 'static,
-
    {
-
        let mut buf = Buffer::default();
-

-
        buf.resize(4, 0);
-

-
        if constraints.is_empty() {
-
            buf.push(msg::ADD_IDENTITY)
-
        } else {
-
            buf.push(msg::ADD_ID_CONSTRAINED)
-
        }
-
        key.write(&mut buf);
-

-
        if !constraints.is_empty() {
-
            for cons in constraints {
-
                match *cons {
-
                    Constraint::KeyLifetime { seconds } => {
-
                        buf.push(msg::CONSTRAIN_LIFETIME);
-
                        buf.deref_mut().write_u32::<BigEndian>(seconds)?
-
                    }
-
                    Constraint::Confirm => buf.push(msg::CONSTRAIN_CONFIRM),
-
                    Constraint::Extensions {
-
                        ref name,
-
                        ref details,
-
                    } => {
-
                        buf.push(msg::CONSTRAIN_EXTENSION);
-
                        buf.extend_ssh_string(name);
-
                        buf.extend_ssh_string(details);
-
                    }
-
                }
-
            }
-
        }
-
        buf.write_len();
-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Add a smart card to the agent, with a (possibly empty) set of
-
    /// constraints to apply when signing.
-
    pub fn add_smartcard_key(
-
        &mut self,
-
        id: &str,
-
        pin: &[u8],
-
        constraints: &[Constraint],
-
    ) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-

-
        buf.resize(4, 0);
-

-
        if constraints.is_empty() {
-
            buf.push(msg::ADD_SMARTCARD_KEY)
-
        } else {
-
            buf.push(msg::ADD_SMARTCARD_KEY_CONSTRAINED)
-
        }
-
        buf.extend_ssh_string(id.as_bytes());
-
        buf.extend_ssh_string(pin);
-

-
        if !constraints.is_empty() {
-
            buf.deref_mut()
-
                .write_u32::<BigEndian>(constraints.len() as u32)?;
-
            for cons in constraints {
-
                match *cons {
-
                    Constraint::KeyLifetime { seconds } => {
-
                        buf.push(msg::CONSTRAIN_LIFETIME);
-
                        buf.deref_mut().write_u32::<BigEndian>(seconds)?;
-
                    }
-
                    Constraint::Confirm => buf.push(msg::CONSTRAIN_CONFIRM),
-
                    Constraint::Extensions {
-
                        ref name,
-
                        ref details,
-
                    } => {
-
                        buf.push(msg::CONSTRAIN_EXTENSION);
-
                        buf.extend_ssh_string(name);
-
                        buf.extend_ssh_string(details);
-
                    }
-
                }
-
            }
-
        }
-
        buf.write_len();
-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Lock the agent, making it refuse to sign until unlocked.
-
    pub fn lock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-

-
        buf.resize(4, 0);
-
        buf.push(msg::LOCK);
-
        buf.extend_ssh_string(passphrase);
-
        buf.write_len();
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Unlock the agent, allowing it to sign again.
-
    pub fn unlock(&mut self, passphrase: &[u8]) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-
        buf.resize(4, 0);
-
        buf.push(msg::UNLOCK);
-
        buf.extend_ssh_string(passphrase);
-
        buf.write_len();
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Ask the agent for a list of the currently registered secret
-
    /// keys.
-
    pub fn request_identities<K>(&mut self) -> Result<Vec<K>, Error>
-
    where
-
        K: Encodable,
-
        K::Error: std::error::Error + Send + Sync + 'static,
-
    {
-
        let mut buf = Buffer::default();
-
        buf.resize(4, 0);
-
        buf.push(msg::REQUEST_IDENTITIES);
-
        buf.write_len();
-

-
        let mut keys = Vec::new();
-
        let resp = self.stream.request(&buf)?;
-

-
        if resp[0] == msg::IDENTITIES_ANSWER {
-
            let mut r = resp.reader(1);
-
            let n = r.read_u32()?;
-

-
            for _ in 0..n {
-
                let key = r.read_string()?;
-
                let _ = r.read_string()?;
-
                let mut r = key.reader(0);
-

-
                if let Ok(pk) = K::read(&mut r) {
-
                    keys.push(pk);
-
                }
-
            }
-
        }
-

-
        Ok(keys)
-
    }
-

-
    /// Ask the agent to sign the supplied piece of data.
-
    pub fn sign<K>(&mut self, public: &K, data: &[u8]) -> Result<Signature, Error>
-
    where
-
        K: Encodable + fmt::Debug,
-
    {
-
        let req = self.prepare_sign_request(public, data);
-
        let resp = self.stream.request(&req)?;
-

-
        if !resp.is_empty() && resp[0] == msg::SIGN_RESPONSE {
-
            self.read_signature(&resp)
-
        } else if !resp.is_empty() && resp[0] == msg::FAILURE {
-
            Err(Error::AgentFailure)
-
        } else {
-
            Err(Error::AgentProtocolError)
-
        }
-
    }
-

-
    fn prepare_sign_request<K>(&self, public: &K, data: &[u8]) -> Buffer
-
    where
-
        K: Encodable + fmt::Debug,
-
    {
-
        // byte                    SSH_AGENTC_SIGN_REQUEST
-
        // string                  key blob
-
        // string                  data
-
        // uint32                  flags
-

-
        let mut pk = Buffer::default();
-
        public.write(&mut pk);
-

-
        let total = 1 + pk.len() + 4 + data.len() + 4;
-

-
        let mut buf = Buffer::default();
-
        buf.write_u32::<BigEndian>(total as u32)
-
            .expect("Writing to a vector never fails");
-
        buf.push(msg::SIGN_REQUEST);
-
        buf.extend_from_slice(&pk);
-
        buf.extend_ssh_string(data);
-

-
        // Signature flags should be zero for ed25519.
-
        buf.write_u32::<BigEndian>(0).unwrap();
-
        buf
-
    }
-

-
    fn read_signature(&self, sig: &[u8]) -> Result<Signature, Error> {
-
        let mut r = sig.reader(1);
-
        let mut resp = r.read_string()?.reader(0);
-
        let _t = resp.read_string()?;
-
        let sig = resp.read_string()?;
-

-
        let mut out = [0; 64];
-
        out.copy_from_slice(sig);
-

-
        Ok(out)
-
    }
-

-
    /// Ask the agent to remove a key from its memory.
-
    pub fn remove_identity<K>(&mut self, public: &K) -> Result<(), Error>
-
    where
-
        K: Encodable,
-
    {
-
        let mut pk: Buffer = Vec::new().into();
-
        public.write(&mut pk);
-

-
        let total = 1 + pk.len();
-

-
        let mut buf = Buffer::default();
-
        buf.write_u32::<BigEndian>(total as u32)?;
-
        buf.push(msg::REMOVE_IDENTITY);
-
        buf.extend_from_slice(&pk);
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Ask the agent to remove a smartcard from its memory.
-
    pub fn remove_smartcard_key(&mut self, id: &str, pin: &[u8]) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-
        buf.resize(4, 0);
-
        buf.push(msg::REMOVE_SMARTCARD_KEY);
-
        buf.extend_ssh_string(id.as_bytes());
-
        buf.extend_ssh_string(pin);
-
        buf.write_len();
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Ask the agent to forget all known keys.
-
    pub fn remove_all_identities(&mut self) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-
        buf.resize(4, 0);
-
        buf.push(msg::REMOVE_ALL_IDENTITIES);
-
        buf.write_len();
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Send a custom message to the agent.
-
    pub fn extension(&mut self, typ: &[u8], ext: &[u8]) -> Result<(), Error> {
-
        let mut buf = Buffer::default();
-

-
        buf.resize(4, 0);
-
        buf.push(msg::EXTENSION);
-
        buf.extend_ssh_string(typ);
-
        buf.extend_ssh_string(ext);
-
        buf.write_len();
-

-
        self.stream.request(&buf)?;
-

-
        Ok(())
-
    }
-

-
    /// Ask the agent about supported extensions.
-
    pub fn query_extension(&mut self, typ: &[u8], mut ext: Buffer) -> Result<bool, Error> {
-
        let mut req = Buffer::default();
-

-
        req.resize(4, 0);
-
        req.push(msg::EXTENSION);
-
        req.extend_ssh_string(typ);
-
        req.write_len();
-

-
        let resp = self.stream.request(&req)?;
-
        let mut r = resp.reader(1);
-
        ext.extend(r.read_string()?);
-

-
        Ok(!resp.is_empty() && resp[0] == msg::SUCCESS)
-
    }
-
}
-

-
#[cfg(not(unix))]
-
impl ClientStream for TcpStream {
-
    fn connect_uds<P>(_: P) -> Result<AgentClient<Self>, Error>
-
    where
-
        P: AsRef<Path> + Send,
-
    {
-
        Err(Error::AgentFailure)
-
    }
-

-
    fn read_response(&mut self, _: &mut Buffer) -> Result<(), Error> {
-
        Err(Error::AgentFailure)
-
    }
-

-
    fn connect_env() -> Result<AgentClient<Self>, Error> {
-
        Err(Error::AgentFailure)
-
    }
-
}
-

-
#[cfg(unix)]
-
impl ClientStream for UnixStream {
-
    fn connect<P>(path: P) -> Result<AgentClient<Self>, Error>
-
    where
-
        P: AsRef<Path> + Send,
-
    {
-
        let stream = UnixStream::connect(path)?;
-

-
        Ok(AgentClient { stream })
-
    }
-

-
    fn request(&mut self, msg: &[u8]) -> Result<Buffer, Error> {
-
        let mut resp = Buffer::default();
-

-
        // Write the message
-
        self.write_all(msg)?;
-
        self.flush()?;
-

-
        // Read the length
-
        resp.resize(4, 0);
-
        self.read_exact(&mut resp)?;
-

-
        // Read the rest of the buffer
-
        let len = BigEndian::read_u32(&resp) as usize;
-
        resp.zeroize();
-
        resp.resize(len, 0);
-
        self.read_exact(&mut resp)?;
-

-
        Ok(resp)
-
    }
-
}
deleted radicle-ssh/src/agent/msg.rs
@@ -1,23 +0,0 @@
-
pub const FAILURE: u8 = 5;
-
pub const SUCCESS: u8 = 6;
-
pub const IDENTITIES_ANSWER: u8 = 12;
-
pub const SIGN_RESPONSE: u8 = 14;
-
#[allow(dead_code)]
-
pub const EXTENSION_FAILURE: u8 = 28;
-

-
pub const REQUEST_IDENTITIES: u8 = 11;
-
pub const SIGN_REQUEST: u8 = 13;
-
pub const ADD_IDENTITY: u8 = 17;
-
pub const REMOVE_IDENTITY: u8 = 18;
-
pub const REMOVE_ALL_IDENTITIES: u8 = 19;
-
pub const ADD_ID_CONSTRAINED: u8 = 25;
-
pub const ADD_SMARTCARD_KEY: u8 = 20;
-
pub const REMOVE_SMARTCARD_KEY: u8 = 21;
-
pub const LOCK: u8 = 22;
-
pub const UNLOCK: u8 = 23;
-
pub const ADD_SMARTCARD_KEY_CONSTRAINED: u8 = 26;
-
pub const EXTENSION: u8 = 27;
-

-
pub const CONSTRAIN_LIFETIME: u8 = 1;
-
pub const CONSTRAIN_CONFIRM: u8 = 2;
-
pub const CONSTRAIN_EXTENSION: u8 = 3;
deleted radicle-ssh/src/encoding.rs
@@ -1,248 +0,0 @@
-
// Copyright 2016 Pierre-Étienne Meunier
-
//
-
// Licensed under the Apache License, Version 2.0 (the "License");
-
// you may not use this file except in compliance with the License.
-
// You may obtain a copy of the License at
-
//
-
// http://www.apache.org/licenses/LICENSE-2.0
-
//
-
// Unless required by applicable law or agreed to in writing, software
-
// distributed under the License is distributed on an "AS IS" BASIS,
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
// See the License for the specific language governing permissions and
-
// limitations under the License.
-
//
-
use std::ops::DerefMut;
-

-
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
-
use thiserror::Error;
-
use zeroize::Zeroizing;
-

-
/// General purpose writable byte buffer we use everywhere.
-
pub type Buffer = Zeroizing<Vec<u8>>;
-

-
#[derive(Debug, Error)]
-
pub enum Error {
-
    /// Index out of bounds
-
    #[error("Index out of bounds")]
-
    IndexOutOfBounds,
-
}
-

-
pub trait Encodable: Sized {
-
    type Error: std::error::Error + Send + Sync + 'static;
-

-
    /// Read from the SSH format.
-
    fn read(reader: &mut Cursor) -> Result<Self, Self::Error>;
-
    /// Write to the SSH format.
-
    fn write<E: Encoding>(&self, buf: &mut E);
-
}
-

-
/// Encode in the SSH format.
-
pub trait Encoding {
-
    /// Push an SSH-encoded string to `self`.
-
    fn extend_ssh_string(&mut self, s: &[u8]);
-
    /// Push an SSH-encoded blank string of length `s` to `self`.
-
    fn extend_ssh_string_blank(&mut self, s: usize) -> &mut [u8];
-
    /// Push an SSH-encoded multiple-precision integer.
-
    fn extend_ssh_mpint(&mut self, s: &[u8]);
-
    /// Push an SSH-encoded list.
-
    fn extend_list<'a, I: Iterator<Item = &'a [u8]>>(&mut self, list: I);
-
    /// Push an SSH-encoded unsigned 32-bit integer.
-
    fn extend_u32(&mut self, u: u32);
-
    /// Push an SSH-encoded empty list.
-
    fn write_empty_list(&mut self);
-
    /// Write the buffer length at the beginning of the buffer.
-
    fn write_len(&mut self);
-
}
-

-
/// Encoding length of the given mpint.
-
pub fn mpint_len(s: &[u8]) -> usize {
-
    let mut i = 0;
-
    while i < s.len() && s[i] == 0 {
-
        i += 1
-
    }
-
    (if s[i] & 0x80 != 0 { 5 } else { 4 }) + s.len() - i
-
}
-

-
impl Encoding for Vec<u8> {
-
    fn extend_ssh_string(&mut self, s: &[u8]) {
-
        self.write_u32::<BigEndian>(s.len() as u32).unwrap();
-
        self.extend(s);
-
    }
-

-
    fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
-
        self.write_u32::<BigEndian>(len as u32).unwrap();
-
        let current = self.len();
-
        self.resize(current + len, 0u8);
-

-
        &mut self[current..]
-
    }
-

-
    fn extend_ssh_mpint(&mut self, s: &[u8]) {
-
        // Skip initial 0s.
-
        let mut i = 0;
-
        while i < s.len() && s[i] == 0 {
-
            i += 1
-
        }
-
        // If the first non-zero is >= 128, write its length (u32, BE), followed by 0.
-
        if s[i] & 0x80 != 0 {
-
            self.write_u32::<BigEndian>((s.len() - i + 1) as u32)
-
                .unwrap();
-
            self.push(0)
-
        } else {
-
            self.write_u32::<BigEndian>((s.len() - i) as u32).unwrap();
-
        }
-
        self.extend(&s[i..]);
-
    }
-

-
    fn extend_u32(&mut self, s: u32) {
-
        let mut buf = [0x0; 4];
-
        BigEndian::write_u32(&mut buf, s);
-
        self.extend(buf);
-
    }
-

-
    fn extend_list<'a, I: Iterator<Item = &'a [u8]>>(&mut self, list: I) {
-
        let len0 = self.len();
-
        self.extend([0, 0, 0, 0]);
-

-
        let mut first = true;
-
        for i in list {
-
            if !first {
-
                self.push(b',')
-
            } else {
-
                first = false;
-
            }
-
            self.extend(i)
-
        }
-
        let len = (self.len() - len0 - 4) as u32;
-

-
        BigEndian::write_u32(&mut self[len0..], len);
-
    }
-

-
    fn write_empty_list(&mut self) {
-
        self.extend([0, 0, 0, 0]);
-
    }
-

-
    fn write_len(&mut self) {
-
        let len = self.len() - 4;
-
        BigEndian::write_u32(&mut self[..], len as u32);
-
    }
-
}
-

-
impl Encoding for Buffer {
-
    fn extend_ssh_string(&mut self, s: &[u8]) {
-
        self.deref_mut().extend_ssh_string(s)
-
    }
-

-
    fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
-
        self.deref_mut().extend_ssh_string_blank(len)
-
    }
-

-
    fn extend_ssh_mpint(&mut self, s: &[u8]) {
-
        self.deref_mut().extend_ssh_mpint(s)
-
    }
-

-
    fn extend_list<'a, I: Iterator<Item = &'a [u8]>>(&mut self, list: I) {
-
        self.deref_mut().extend_list(list)
-
    }
-

-
    fn write_empty_list(&mut self) {
-
        self.deref_mut().write_empty_list()
-
    }
-

-
    fn extend_u32(&mut self, s: u32) {
-
        self.deref_mut().extend_u32(s);
-
    }
-

-
    fn write_len(&mut self) {
-
        self.deref_mut().write_len()
-
    }
-
}
-

-
/// A cursor-like trait to read SSH-encoded things.
-
pub trait Reader {
-
    /// Create an SSH reader for `self`.
-
    fn reader(&self, starting_at: usize) -> Cursor;
-
}
-

-
impl Reader for Buffer {
-
    fn reader(&self, starting_at: usize) -> Cursor {
-
        Cursor {
-
            s: self,
-
            position: starting_at,
-
        }
-
    }
-
}
-

-
impl Reader for [u8] {
-
    fn reader(&self, starting_at: usize) -> Cursor {
-
        Cursor {
-
            s: self,
-
            position: starting_at,
-
        }
-
    }
-
}
-

-
/// A cursor-like type to read SSH-encoded values.
-
#[derive(Debug)]
-
pub struct Cursor<'a> {
-
    s: &'a [u8],
-
    #[doc(hidden)]
-
    pub position: usize,
-
}
-

-
impl<'a> Cursor<'a> {
-
    /// Read one string from this reader.
-
    pub fn read_string(&mut self) -> Result<&'a [u8], Error> {
-
        let len = self.read_u32()? as usize;
-
        if self.position + len <= self.s.len() {
-
            let result = &self.s[self.position..(self.position + len)];
-
            self.position += len;
-
            Ok(result)
-
        } else {
-
            Err(Error::IndexOutOfBounds)
-
        }
-
    }
-

-
    /// Read a `u32` from this reader.
-
    pub fn read_u32(&mut self) -> Result<u32, Error> {
-
        if self.position + 4 <= self.s.len() {
-
            let u = BigEndian::read_u32(&self.s[self.position..]);
-
            self.position += 4;
-
            Ok(u)
-
        } else {
-
            Err(Error::IndexOutOfBounds)
-
        }
-
    }
-

-
    /// Read one byte from this reader.
-
    pub fn read_byte(&mut self) -> Result<u8, Error> {
-
        if self.position < self.s.len() {
-
            let u = self.s[self.position];
-
            self.position += 1;
-
            Ok(u)
-
        } else {
-
            Err(Error::IndexOutOfBounds)
-
        }
-
    }
-

-
    pub fn read_bytes<const S: usize>(&mut self) -> Result<[u8; S], Error> {
-
        let mut buf = [0; S];
-
        for b in buf.iter_mut() {
-
            *b = self.read_byte()?;
-
        }
-
        Ok(buf)
-
    }
-

-
    /// Read one byte from this reader.
-
    pub fn read_mpint(&mut self) -> Result<&'a [u8], Error> {
-
        let len = self.read_u32()? as usize;
-
        if self.position + len <= self.s.len() {
-
            let result = &self.s[self.position..(self.position + len)];
-
            self.position += len;
-
            Ok(result)
-
        } else {
-
            Err(Error::IndexOutOfBounds)
-
        }
-
    }
-
}
deleted radicle-ssh/src/lib.rs
@@ -1,4 +0,0 @@
-
pub mod agent;
-
pub mod encoding;
-

-
pub use agent::client::Error;
modified radicle/Cargo.toml
@@ -58,11 +58,6 @@ path = "../radicle-crypto"
version = "0"
features = ["radicle-git-ext", "ssh", "sqlite", "cyphernet"]

-
[dependencies.radicle-ssh]
-
path = "../radicle-ssh"
-
version = "0"
-
default-features = false
-

[dependencies.qcheck]
version = "1"
default-features = false