crypto: implement an OpenSSH keystore

This will become the primary keystore.

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-11-02 14:11:07 +01:00
parent f5212405c5
commit 5494490c40
No known key found for this signature in database
4 changed files with 597 additions and 9 deletions

485
Cargo.lock generated
View File

@ -8,6 +8,17 @@ version = "1.0.2"
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"
@ -129,12 +140,35 @@ version = "0.2.11"
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"
@ -197,6 +231,16 @@ dependencies = [
"siphasher",
]
[[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"
@ -266,6 +310,12 @@ dependencies = [
"winapi",
]
[[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"
@ -315,6 +365,18 @@ dependencies = [
"cfg-if",
]
[[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"
@ -331,6 +393,28 @@ version = "1.1.1"
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"
@ -357,6 +441,17 @@ dependencies = [
"syn",
]
[[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"
@ -373,10 +468,32 @@ 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"
@ -384,7 +501,40 @@ 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]]
@ -396,6 +546,16 @@ dependencies = [
"instant",
]
[[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"
@ -470,6 +630,17 @@ dependencies = [
"version_check",
]
[[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"
@ -526,6 +697,17 @@ dependencies = [
"url",
]
[[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"
@ -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"
@ -744,6 +929,12 @@ dependencies = [
"pkg-config",
]
[[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"
@ -882,6 +1073,23 @@ dependencies = [
"winapi",
]
[[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"
@ -892,6 +1100,17 @@ dependencies = [
"num-traits",
]
[[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"
@ -899,6 +1118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@ -949,6 +1169,28 @@ version = "0.1.1"
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"
@ -958,6 +1200,15 @@ dependencies = [
"digest 0.10.5",
]
[[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"
@ -996,6 +1247,28 @@ version = "0.1.0"
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"
@ -1011,6 +1284,12 @@ dependencies = [
"libc",
]
[[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"
@ -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]]
@ -1220,13 +1500,56 @@ dependencies = [
"radicle",
]
[[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]]
@ -1280,6 +1612,38 @@ dependencies = [
"winapi",
]
[[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"
@ -1307,6 +1671,20 @@ dependencies = [
"sha2 0.10.6",
]
[[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"
@ -1397,6 +1775,16 @@ dependencies = [
"lazy_static",
]
[[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"
@ -1428,6 +1816,22 @@ dependencies = [
"winapi",
]
[[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"
@ -1458,6 +1862,38 @@ dependencies = [
"sqlite3-src",
]
[[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"
@ -1481,6 +1917,18 @@ version = "0.1.1"
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"
@ -1760,6 +2208,12 @@ dependencies = [
"tinyvec",
]
[[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"
@ -1805,6 +2259,12 @@ dependencies = [
"try-lock",
]
[[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"
@ -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",
]

View File

@ -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" }

View File

@ -1,4 +1,5 @@
pub mod agent;
pub mod keystore;
use std::io;

View File

@ -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.
}
}