Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
crypto: implement an OpenSSH keystore
Alexis Sellier committed 3 years ago
commit 5494490c40e494c72eb8ed86fb4ed020d7862cca
parent f5212405c52a1390b3f0af63b4b482deeba84761
4 files changed +597 -9
modified Cargo.lock
@@ -9,6 +9,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"

[[package]]
+
name = "aes"
+
version = "0.8.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241"
+
dependencies = [
+
 "cfg-if",
+
 "cipher",
+
 "cpufeatures",
+
]
+

+
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -130,12 +141,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"

[[package]]
+
name = "base16ct"
+
version = "0.1.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
+

+
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"

[[package]]
+
name = "base64ct"
+
version = "1.5.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
+

+
[[package]]
+
name = "bcrypt-pbkdf"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3806a8db60cf56efee531616a34a6aaa9a114d6da2add861b0fa4a188881b2c7"
+
dependencies = [
+
 "blowfish",
+
 "pbkdf2",
+
 "sha2 0.10.6",
+
]
+

+
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -198,6 +232,16 @@ dependencies = [
]

[[package]]
+
name = "blowfish"
+
version = "0.9.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
+
dependencies = [
+
 "byteorder",
+
 "cipher",
+
]
+

+
[[package]]
name = "bumpalo"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -267,6 +311,12 @@ dependencies = [
]

[[package]]
+
name = "const-oid"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661"
+

+
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -316,6 +366,18 @@ dependencies = [
]

[[package]]
+
name = "crypto-bigint"
+
version = "0.4.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
+
dependencies = [
+
 "generic-array",
+
 "rand_core 0.6.4",
+
 "subtle",
+
 "zeroize",
+
]
+

+
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -332,6 +394,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"

[[package]]
+
name = "ctr"
+
version = "0.9.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+
dependencies = [
+
 "cipher",
+
]
+

+
[[package]]
+
name = "curve25519-dalek"
+
version = "3.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
+
dependencies = [
+
 "byteorder",
+
 "digest 0.9.0",
+
 "rand_core 0.5.1",
+
 "subtle",
+
 "zeroize",
+
]
+

+
[[package]]
name = "data-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -358,6 +442,17 @@ dependencies = [
]

[[package]]
+
name = "der"
+
version = "0.6.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f"
+
dependencies = [
+
 "const-oid",
+
 "pem-rfc7468",
+
 "zeroize",
+
]
+

+
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -373,18 +468,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
dependencies = [
 "block-buffer 0.10.3",
+
 "const-oid",
 "crypto-common",
 "subtle",
]

[[package]]
+
name = "ecdsa"
+
version = "0.14.8"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
+
dependencies = [
+
 "der",
+
 "elliptic-curve",
+
 "rfc6979",
+
 "signature",
+
]
+

+
[[package]]
+
name = "ed25519"
+
version = "1.5.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369"
+
dependencies = [
+
 "signature",
+
]
+

+
[[package]]
name = "ed25519-compact"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8"
dependencies = [
 "ct-codecs",
-
 "getrandom",
+
 "getrandom 0.2.7",
+
]
+

+
[[package]]
+
name = "ed25519-dalek"
+
version = "1.0.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
+
dependencies = [
+
 "curve25519-dalek",
+
 "ed25519",
+
 "rand 0.7.3",
+
 "serde",
+
 "sha2 0.9.9",
+
 "zeroize",
+
]
+

+
[[package]]
+
name = "elliptic-curve"
+
version = "0.12.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
+
dependencies = [
+
 "base16ct",
+
 "crypto-bigint",
+
 "der",
+
 "digest 0.10.5",
+
 "ff",
+
 "generic-array",
+
 "group",
+
 "rand_core 0.6.4",
+
 "sec1",
+
 "subtle",
+
 "zeroize",
]

[[package]]
@@ -397,6 +547,16 @@ dependencies = [
]

[[package]]
+
name = "ff"
+
version = "0.12.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
+
dependencies = [
+
 "rand_core 0.6.4",
+
 "subtle",
+
]
+

+
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -472,6 +632,17 @@ dependencies = [

[[package]]
name = "getrandom"
+
version = "0.1.16"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+
dependencies = [
+
 "cfg-if",
+
 "libc",
+
 "wasi 0.9.0+wasi-snapshot-preview1",
+
]
+

+
[[package]]
+
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
@@ -527,6 +698,17 @@ dependencies = [
]

[[package]]
+
name = "group"
+
version = "0.12.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
+
dependencies = [
+
 "ff",
+
 "rand_core 0.6.4",
+
 "subtle",
+
]
+

+
[[package]]
name = "h2"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -719,6 +901,9 @@ name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
dependencies = [
+
 "spin",
+
]

[[package]]
name = "lexopt"
@@ -745,6 +930,12 @@ dependencies = [
]

[[package]]
+
name = "libm"
+
version = "0.2.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
+

+
[[package]]
name = "libz-sys"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -883,6 +1074,23 @@ dependencies = [
]

[[package]]
+
name = "num-bigint-dig"
+
version = "0.8.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011"
+
dependencies = [
+
 "byteorder",
+
 "lazy_static",
+
 "libm",
+
 "num-integer",
+
 "num-iter",
+
 "num-traits",
+
 "rand 0.8.5",
+
 "smallvec",
+
 "zeroize",
+
]
+

+
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -893,12 +1101,24 @@ dependencies = [
]

[[package]]
+
name = "num-iter"
+
version = "0.1.43"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+
dependencies = [
+
 "autocfg",
+
 "num-integer",
+
 "num-traits",
+
]
+

+
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
 "autocfg",
+
 "libm",
]

[[package]]
@@ -950,6 +1170,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"

[[package]]
+
name = "p256"
+
version = "0.11.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594"
+
dependencies = [
+
 "ecdsa",
+
 "elliptic-curve",
+
 "sha2 0.10.6",
+
]
+

+
[[package]]
+
name = "p384"
+
version = "0.11.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa"
+
dependencies = [
+
 "ecdsa",
+
 "elliptic-curve",
+
 "sha2 0.10.6",
+
]
+

+
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -959,6 +1201,15 @@ dependencies = [
]

[[package]]
+
name = "pem-rfc7468"
+
version = "0.6.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
+
dependencies = [
+
 "base64ct",
+
]
+

+
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -997,6 +1248,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"

[[package]]
+
name = "pkcs1"
+
version = "0.4.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
+
dependencies = [
+
 "der",
+
 "pkcs8",
+
 "spki",
+
 "zeroize",
+
]
+

+
[[package]]
+
name = "pkcs8"
+
version = "0.9.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
+
dependencies = [
+
 "der",
+
 "spki",
+
]
+

+
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1012,6 +1285,12 @@ dependencies = [
]

[[package]]
+
name = "ppv-lite86"
+
version = "0.2.16"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+

+
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1050,7 +1329,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
-
 "rand",
+
 "rand 0.8.5",
]

[[package]]
@@ -1119,8 +1398,9 @@ dependencies = [
 "serde",
 "sha2 0.10.6",
 "sqlite",
+
 "ssh-key",
+
 "tempfile",
 "thiserror",
-
 "zeroize",
]

[[package]]
@@ -1222,11 +1502,54 @@ dependencies = [

[[package]]
name = "rand"
+
version = "0.7.3"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+
dependencies = [
+
 "getrandom 0.1.16",
+
 "libc",
+
 "rand_chacha 0.2.2",
+
 "rand_core 0.5.1",
+
 "rand_hc",
+
]
+

+
[[package]]
+
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
-
 "rand_core",
+
 "rand_chacha 0.3.1",
+
 "rand_core 0.6.4",
+
]
+

+
[[package]]
+
name = "rand_chacha"
+
version = "0.2.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+
dependencies = [
+
 "ppv-lite86",
+
 "rand_core 0.5.1",
+
]
+

+
[[package]]
+
name = "rand_chacha"
+
version = "0.3.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+
dependencies = [
+
 "ppv-lite86",
+
 "rand_core 0.6.4",
+
]
+

+
[[package]]
+
name = "rand_core"
+
version = "0.5.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+
dependencies = [
+
 "getrandom 0.1.16",
]

[[package]]
@@ -1235,7 +1558,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
-
 "getrandom",
+
 "getrandom 0.2.7",
+
]
+

+
[[package]]
+
name = "rand_hc"
+
version = "0.2.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+
dependencies = [
+
 "rand_core 0.5.1",
]

[[package]]
@@ -1281,6 +1613,38 @@ dependencies = [
]

[[package]]
+
name = "rfc6979"
+
version = "0.3.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32"
+
dependencies = [
+
 "crypto-bigint",
+
 "hmac",
+
 "zeroize",
+
]
+

+
[[package]]
+
name = "rsa"
+
version = "0.7.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b0ecc3307be66bfb3574577895555bacfb9a37a8d5cd959444b72ff02495c618"
+
dependencies = [
+
 "byteorder",
+
 "digest 0.10.5",
+
 "num-bigint-dig",
+
 "num-integer",
+
 "num-iter",
+
 "num-traits",
+
 "pkcs1",
+
 "pkcs8",
+
 "rand_core 0.6.4",
+
 "signature",
+
 "smallvec",
+
 "subtle",
+
 "zeroize",
+
]
+

+
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1308,6 +1672,20 @@ dependencies = [
]

[[package]]
+
name = "sec1"
+
version = "0.3.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
+
dependencies = [
+
 "base16ct",
+
 "der",
+
 "generic-array",
+
 "pkcs8",
+
 "subtle",
+
 "zeroize",
+
]
+

+
[[package]]
name = "serde"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1398,6 +1776,16 @@ dependencies = [
]

[[package]]
+
name = "signature"
+
version = "1.6.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
+
dependencies = [
+
 "digest 0.10.5",
+
 "rand_core 0.6.4",
+
]
+

+
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1429,6 +1817,22 @@ dependencies = [
]

[[package]]
+
name = "spin"
+
version = "0.5.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+

+
[[package]]
+
name = "spki"
+
version = "0.6.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
+
dependencies = [
+
 "base64ct",
+
 "der",
+
]
+

+
[[package]]
name = "sqlite"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1459,6 +1863,38 @@ dependencies = [
]

[[package]]
+
name = "ssh-encoding"
+
version = "0.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "19cfdc32e0199062113edf41f344fbf784b8205a94600233c84eb838f45191e1"
+
dependencies = [
+
 "base64ct",
+
 "pem-rfc7468",
+
 "sha2 0.10.6",
+
]
+

+
[[package]]
+
name = "ssh-key"
+
version = "0.5.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "288d8f5562af5a3be4bda308dd374b2c807b940ac370b5efa1c99311da91d9a1"
+
dependencies = [
+
 "aes",
+
 "bcrypt-pbkdf",
+
 "ctr",
+
 "ed25519-dalek",
+
 "p256",
+
 "p384",
+
 "rand_core 0.6.4",
+
 "rsa",
+
 "sec1",
+
 "sha2 0.10.6",
+
 "signature",
+
 "ssh-encoding",
+
 "zeroize",
+
]
+

+
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1482,6 +1918,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"

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

+
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1761,6 +2209,12 @@ dependencies = [
]

[[package]]
+
name = "unicode-xid"
+
version = "0.2.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+

+
[[package]]
name = "unsigned-varint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1807,6 +2261,12 @@ dependencies = [

[[package]]
name = "wasi"
+
version = "0.9.0+wasi-snapshot-preview1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+

+
[[package]]
+
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
@@ -1941,3 +2401,18 @@ name = "zeroize"
version = "1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
+
dependencies = [
+
 "zeroize_derive",
+
]
+

+
[[package]]
+
name = "zeroize_derive"
+
version = "1.3.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
+
dependencies = [
+
 "proc-macro2",
+
 "quote",
+
 "syn",
+
 "synstructure",
+
]
modified radicle-crypto/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2021"

[features]
test = ["fastrand", "quickcheck"]
-
ssh = ["base64", "radicle-ssh", "sha2"]
+
ssh = ["base64", "radicle-ssh", "sha2", "ssh-key"]

[dependencies]
ed25519-compact = { version = "1.0.12", features = ["pem"] }
@@ -28,6 +28,12 @@ optional = true
version = "0.1"
optional = true

+
[dependencies.ssh-key]
+
version = "0.5.1"
+
default-features = false
+
features = ["std", "encryption", "rand_core", "getrandom"]
+
optional = true
+

[dependencies.quickcheck]
version = "1"
default-features = false
@@ -47,9 +53,7 @@ optional = true
version = "0.13"
optional = true

-

[dev-dependencies]
-
fastrand = { version = "1.8.0" }
quickcheck_macros = { version = "1", default-features = false }
quickcheck = { version = "1", default-features = false }
-
zeroize = { version = "1.5.7" }
+
tempfile = { version = "3.3.0" }
modified radicle-crypto/src/ssh.rs
@@ -1,4 +1,5 @@
pub mod agent;
+
pub mod keystore;

use std::io;

added radicle-crypto/src/ssh/keystore.rs
@@ -0,0 +1,108 @@
+
use std::path::{Path, PathBuf};
+

+
use thiserror::Error;
+

+
use crate::{KeyPair, PublicKey, SecretKey};
+

+
#[derive(Debug, Error)]
+
pub enum Error {
+
    #[error("ssh keygen: {0}")]
+
    Ssh(#[from] ssh_key::Error),
+
    #[error("invalid key type, expected ed25519 key")]
+
    InvalidKeyType,
+
    #[error("keystore already initialized")]
+
    AlreadyInitialized,
+
}
+

+
/// Stores keys on disk, in OpenSSH format.
+
#[derive(Debug)]
+
pub struct Keystore {
+
    path: PathBuf,
+
}
+

+
impl Keystore {
+
    /// Create a new keystore pointing to the given path. Use [`Keystore::init`] to initialize.
+
    pub fn new<P: AsRef<Path>>(path: &P) -> Self {
+
        Self {
+
            path: path.as_ref().to_path_buf(),
+
        }
+
    }
+

+
    /// Initialize a keystore by generate a key pair and storing the secret and public keys
+
    /// at the given path.
+
    ///
+
    /// The comment is associated with the private key.
+
    /// The passphrase is used to encrypt the private key.
+
    ///
+
    pub fn init(self, comment: &str, passphrase: &str) -> Result<Self, Error> {
+
        let pair = KeyPair::generate();
+
        let pair = ssh_key::private::Ed25519Keypair::from_bytes(&*pair)?;
+
        let pair = ssh_key::private::KeypairData::Ed25519(pair);
+
        let secret = ssh_key::PrivateKey::new(pair, comment)?;
+
        let secret = secret.encrypt(ssh_key::rand_core::OsRng, passphrase)?;
+
        let public = secret.public_key();
+
        let path = self.path.join("radicle");
+

+
        if path.exists() {
+
            return Err(Error::AlreadyInitialized);
+
        }
+

+
        secret.write_openssh_file(&path, ssh_key::LineEnding::default())?;
+
        public.write_openssh_file(&path.with_extension("pub"))?;
+

+
        Ok(self)
+
    }
+

+
    /// Load the public key from the store. Returns `None` if it wasn't found.
+
    pub fn public_key(&self) -> Result<Option<PublicKey>, Error> {
+
        let path = self.path.join("radicle.pub");
+
        if !path.exists() {
+
            return Ok(None);
+
        }
+

+
        let public = ssh_key::PublicKey::read_openssh_file(&path)?;
+
        match public.key_data() {
+
            ssh_key::public::KeyData::Ed25519(ssh_key::public::Ed25519PublicKey(data)) => {
+
                Ok(Some(PublicKey::from(*data)))
+
            }
+
            _ => Err(Error::InvalidKeyType),
+
        }
+
    }
+

+
    /// Load the secret key from the store, decrypting it with the given passphrase.
+
    /// Returns `None` if it wasn't found.
+
    pub fn secret_key(&self, passphrase: &str) -> Result<Option<SecretKey>, Error> {
+
        let path = self.path.join("radicle");
+
        if !path.exists() {
+
            return Ok(None);
+
        }
+

+
        let encrypted = ssh_key::PrivateKey::read_openssh_file(&path)?;
+
        let secret = encrypted.decrypt(passphrase)?;
+

+
        match secret.key_data() {
+
            ssh_key::private::KeypairData::Ed25519(pair) => {
+
                Ok(Some(SecretKey::new(pair.to_bytes())))
+
            }
+
            _ => Err(Error::InvalidKeyType),
+
        }
+
    }
+
}
+

+
#[cfg(test)]
+
mod tests {
+
    use super::*;
+

+
    #[test]
+
    fn test_init() {
+
        let tmp = tempfile::tempdir().unwrap();
+
        let store = Keystore::new(&tmp.path());
+

+
        let store = store.init("test", "hunter").unwrap();
+
        let public = store.public_key().unwrap().unwrap();
+
        let secret = store.secret_key("hunter").unwrap().unwrap();
+

+
        assert_eq!(PublicKey::from(secret.public_key()), public);
+
        store.secret_key("blunder").unwrap_err(); // Wrong passphrase.
+
    }
+
}