diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7cc10d97..93f2756d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,5 +60,6 @@ repos: - id: check-json - id: check-added-large-files - id: detect-private-key + exclude: __testkey - id: check-executables-have-shebangs - id: check-toml diff --git a/Cargo.lock b/Cargo.lock index 7c91e46f..b058caf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,21 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -323,6 +338,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + [[package]] name = "async-io" version = "1.13.0" @@ -485,6 +506,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -563,7 +590,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -846,6 +873,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "winapi", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -1379,6 +1419,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.11.1" @@ -1452,8 +1502,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core 0.20.1", + "darling_macro 0.20.1", ] [[package]] @@ -1470,17 +1530,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.16", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core 0.20.1", + "quote", + "syn 2.0.16", +] + [[package]] name = "data-encoding" version = "2.3.3" @@ -1514,7 +1599,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", - "pem-rfc7468", + "pem-rfc7468 0.6.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -1561,7 +1657,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -1632,9 +1728,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -1729,7 +1825,7 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", + "der 0.6.1", "elliptic-curve", "rfc6979", "signature 1.6.4", @@ -1770,18 +1866,18 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", + "base16ct 0.1.1", "crypto-bigint", - "der", - "digest 0.10.6", + "der 0.6.1", + "digest 0.10.7", "ff", "generic-array", "group", "hkdf", - "pem-rfc7468", - "pkcs8", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", "subtle", "zeroize", ] @@ -2372,13 +2468,23 @@ dependencies = [ "hmac 0.12.1", ] +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac", + "crypto-mac 0.11.1", "digest 0.9.0", ] @@ -2388,7 +2494,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", ] [[package]] @@ -2461,10 +2578,13 @@ dependencies = [ "libsqlite3-sys", "openssl", "proptest", + "rand 0.8.5", "reqwest", + "sec1 0.7.2", "semver", "serde", "serde_ipld_dagcbor", + "serde_with", "thiserror", "tokio", "tokio-tungstenite 0.19.0", @@ -2627,6 +2747,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.48.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -2686,7 +2829,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows", + "windows 0.34.0", ] [[package]] @@ -3210,7 +3353,7 @@ dependencies = [ "quick-protobuf-codec", "rand 0.8.5", "regex", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "unsigned-varint", @@ -3246,14 +3389,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" dependencies = [ + "asn1_der", "bs58", "ed25519-dalek", + "libsecp256k1", "log", "multiaddr", "multihash 0.17.0", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "zeroize", ] @@ -3278,7 +3423,7 @@ dependencies = [ "log", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "uint", @@ -3354,7 +3499,7 @@ dependencies = [ "once_cell", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "snow", "static_assertions", "thiserror", @@ -3530,6 +3675,54 @@ dependencies = [ "yamux", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "libsqlite3-sys" version = "0.26.0" @@ -3652,7 +3845,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3818,9 +4011,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", - "sha2 0.10.6", + "sha2 0.10.7", "unsigned-varint", ] @@ -3834,11 +4027,11 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", "serde", "serde-big-array", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "unsigned-varint", ] @@ -4180,7 +4373,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4191,7 +4384,7 @@ checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4288,6 +4481,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -4335,7 +4537,7 @@ checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4382,8 +4584,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.7", + "spki 0.7.2", ] [[package]] @@ -5341,14 +5553,27 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array", - "pkcs8", + "pkcs8 0.9.0", "subtle", "zeroize", ] +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array", + "pkcs8 0.10.2", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.1" @@ -5469,6 +5694,34 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +dependencies = [ + "base64 0.21.0", + "chrono", + "hex", + "indexmap 1.9.3", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +dependencies = [ + "darling 0.20.1", + "proc-macro2", + "quote", + "syn 2.0.16", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -5490,7 +5743,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -5508,13 +5761,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -5523,7 +5776,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -5560,7 +5813,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -5638,7 +5891,7 @@ dependencies = [ "rand_core 0.6.4", "ring", "rustc_version", - "sha2 0.10.6", + "sha2 0.10.7", "subtle", ] @@ -5690,7 +5943,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.7", ] [[package]] @@ -6899,7 +7162,7 @@ dependencies = [ "log", "rustix 0.36.13", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "toml 0.5.11", "windows-sys 0.45.0", "zstd", @@ -7186,7 +7449,7 @@ dependencies = [ "sdp", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "stun", "thiserror", "time", @@ -7246,10 +7509,10 @@ dependencies = [ "rcgen 0.9.3", "ring", "rustls 0.19.1", - "sec1", + "sec1 0.3.0", "serde", "sha1", - "sha2 0.10.6", + "sha2 0.10.7", "signature 1.6.4", "subtle", "thiserror", @@ -7470,6 +7733,15 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.42.0" diff --git a/homestar-runtime/Cargo.toml b/homestar-runtime/Cargo.toml index a6676532..2465f29f 100644 --- a/homestar-runtime/Cargo.toml +++ b/homestar-runtime/Cargo.toml @@ -53,14 +53,17 @@ ipfs-api = { version = "0.17", optional = true } ipfs-api-backend-hyper = { version = "0.6", features = ["with-builder", "with-send-sync"], optional = true } itertools = "0.10" libipld = { workspace = true } -libp2p = { version = "0.51", default-features = false, features = ["kad", "request-response", "macros", "identify", "mdns", "gossipsub", "tokio", "dns", "mplex", "tcp", "noise", "yamux", "websocket"] } +libp2p = { version = "0.51", default-features = false, features = ["kad", "request-response", "macros", "identify", "mdns", "gossipsub", "tokio", "dns", "mplex", "tcp", "noise", "yamux", "websocket", "ed25519", "secp256k1"] } libsqlite3-sys = { version = "0.26", features = ["bundled"] } openssl = { version = "0.10", features = ["vendored"] } proptest = { version = "1.2", optional = true } +rand = "0.8" reqwest = { version = "0.11", features = ["json"] } +sec1 = { version = "0.7", features = ["pem", "der"] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_ipld_dagcbor = { workspace = true } +serde_with = { version = "3.0", features = ["base64"] } thiserror = "1.0" tokio = { workspace = true } tracing = { workspace = true } diff --git a/homestar-runtime/fixtures/__testkey_ed25519.pem b/homestar-runtime/fixtures/__testkey_ed25519.pem new file mode 100644 index 00000000..fb6cc573 --- /dev/null +++ b/homestar-runtime/fixtures/__testkey_ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= +-----END PRIVATE KEY----- diff --git a/homestar-runtime/fixtures/__testkey_secp256k1.der b/homestar-runtime/fixtures/__testkey_secp256k1.der new file mode 100644 index 00000000..1d95e910 Binary files /dev/null and b/homestar-runtime/fixtures/__testkey_secp256k1.der differ diff --git a/homestar-runtime/fixtures/settings-import-ed25519.toml b/homestar-runtime/fixtures/settings-import-ed25519.toml new file mode 100644 index 00000000..366a0650 --- /dev/null +++ b/homestar-runtime/fixtures/settings-import-ed25519.toml @@ -0,0 +1,10 @@ +[monitoring] +process_collector_interval = 10 + +[node] + +[node.network.keypair_config] +# path is relative to execution +# generate a new key with openssl `openssl genpkey -algorithm ed25519 > ed25519_key.pem` +# homestar supports ed25519 and secp256k1 keys +existing = { key_type = "ed25519", path = "./fixtures/__testkey_ed25519.pem"} diff --git a/homestar-runtime/fixtures/settings-import-secp256k1.toml b/homestar-runtime/fixtures/settings-import-secp256k1.toml new file mode 100644 index 00000000..3b36c591 --- /dev/null +++ b/homestar-runtime/fixtures/settings-import-secp256k1.toml @@ -0,0 +1,8 @@ +[monitoring] +process_collector_interval = 10 + +[node] + +[node.network.keypair_config] +# `openssl ecparam -genkey -name secp256k1 -outform DER -o secp256k1_key.der` +existing = { key_type = "secp256k1", path = "./fixtures/__testkey_secp256k1.der"} diff --git a/homestar-runtime/fixtures/settings-random-secp256k1.toml b/homestar-runtime/fixtures/settings-random-secp256k1.toml new file mode 100644 index 00000000..987ba150 --- /dev/null +++ b/homestar-runtime/fixtures/settings-random-secp256k1.toml @@ -0,0 +1,8 @@ +[monitoring] +process_collector_interval = 10 + +[node] + +[node.network.keypair_config] +# generate key using a seed +random_seed = { key_type = "secp256k1", seed = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="} diff --git a/homestar-runtime/src/network/swarm.rs b/homestar-runtime/src/network/swarm.rs index 6aec969f..b3617fee 100644 --- a/homestar-runtime/src/network/swarm.rs +++ b/homestar-runtime/src/network/swarm.rs @@ -5,11 +5,10 @@ use crate::{ network::{eventloop::RECEIPTS_TOPIC, pubsub}, settings, Receipt, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use libp2p::{ core::upgrade, gossipsub::{self, MessageId, SubscriptionError, TopicHash}, - identity::Keypair, kad::{record::store::MemoryStore, Kademlia, KademliaEvent}, mdns, noise, swarm::{NetworkBehaviour, Swarm, SwarmBuilder}, @@ -19,7 +18,11 @@ use std::{fmt, time::Duration}; /// Build a new [Swarm] with a given transport and a tokio executor. pub async fn new(settings: &settings::Node) -> Result> { - let keypair = Keypair::generate_ed25519(); + let keypair = settings + .network + .keypair_config + .keypair() + .with_context(|| "Failed to generate/import keypair for libp2p".to_string())?; let peer_id = keypair.public().to_peer_id(); let transport = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true)) diff --git a/homestar-runtime/src/settings.rs b/homestar-runtime/src/settings.rs index 5f3854e7..718259f7 100644 --- a/homestar-runtime/src/settings.rs +++ b/homestar-runtime/src/settings.rs @@ -1,9 +1,18 @@ //! Settings / Configuration. +use anyhow::{anyhow, Context}; use config::{Config, ConfigError, Environment, File}; use http::Uri; +use libp2p::{identity, identity::secp256k1}; +use rand::{Rng, SeedableRng}; +use sec1::der::Decode; use serde::Deserialize; -use std::path::PathBuf; +use serde_with::{base64::Base64, serde_as}; +use std::{ + io::Read, + path::{Path, PathBuf}, +}; +use tracing::info; /// Server settings. #[derive(Clone, Debug, Deserialize)] @@ -22,6 +31,126 @@ pub struct Monitoring { process_collector_interval: u64, } +#[derive(Clone, Debug, Deserialize)] +/// Configure how the Network keypair is generated or using an existing one +pub(crate) enum PubkeyConfig { + #[serde(rename = "random")] + Random, + /// Seed string should be a base64 encoded 32 bytes. This is used as the RNG seed to generate a ed25519 key. + #[serde(rename = "random_seed")] + GenerateFromSeed(PupkeyRNGSeed), + /// File path to a PEM encoded ed25519 key + #[serde(rename = "existing")] + Existing(ExistingKeyPath), +} + +/// Supported key types of homestar +#[derive(Clone, Debug, Default, Deserialize)] +pub(crate) enum KeyType { + #[default] + #[serde(rename = "ed25519")] + Ed25519, + #[serde(rename = "secp256k1")] + Secp256k1, +} + +/// Seed material for RNG generated keys +#[serde_as] +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct PupkeyRNGSeed { + #[serde(default)] + key_type: KeyType, + #[serde_as(as = "Base64")] + seed: [u8; 32], +} + +/// Info on where and what the Key file is +#[derive(Clone, Debug, Deserialize)] +pub(crate) struct ExistingKeyPath { + #[serde(default)] + key_type: KeyType, + path: String, +} + +impl PubkeyConfig { + /// Produce a Keypair using the given configuration. + /// Calling this function will access the filesystem if configured to import a key. + pub(crate) fn keypair(&self) -> anyhow::Result { + match self { + PubkeyConfig::Random => { + info!("generating random ed25519 key"); + Ok(identity::Keypair::generate_ed25519()) + } + PubkeyConfig::GenerateFromSeed(PupkeyRNGSeed { key_type, seed }) => { + // seed RNG with supplied seed + let mut r = rand::prelude::StdRng::from_seed(*seed); + let mut new_key: [u8; 32] = r.gen(); + + match key_type { + KeyType::Ed25519 => { + info!("generating radom ed25519 key from seed"); + + identity::Keypair::ed25519_from_bytes(new_key).map_err(|e| { + anyhow!("failed to generate ed25519 key from random: {:?}", e) + }) + } + KeyType::Secp256k1 => { + info!("generating radom secp256k1 key from seed"); + + let sk = + secp256k1::SecretKey::try_from_bytes(&mut new_key).map_err(|e| { + anyhow!("failed to generate secp256k1 key from random: {:?}", e) + })?; + let kp = secp256k1::Keypair::from(sk); + Ok(identity::Keypair::from(kp)) + } + } + } + PubkeyConfig::Existing(ExistingKeyPath { key_type, path }) => { + let path = Path::new(&path); + let mut file = std::fs::File::open(path).context("unable to read key file")?; + + let mut buf = Vec::new(); + file.read_to_end(&mut buf) + .context("unable to read bytes from file, is the file corrupted?")?; + + match key_type { + KeyType::Ed25519 => { + const PEM_HEADER: &str = "PRIVATE KEY"; + + info!("importing ed25519 key from: {}", path.display()); + + let (tag, mut key) = sec1::der::pem::decode_vec(&buf) + .map_err(|e| anyhow!("key file must be PEM formatted: {:#?}", e))?; + if tag != PEM_HEADER { + return Err(anyhow!("imported key file had a header of '{tag}', expected '{PEM_HEADER}' for ed25519")); + } + + // raw bytes of ed25519 secret key from PEM file + identity::Keypair::ed25519_from_bytes(&mut key) + .with_context(|| "imported key material was invalid for ed25519") + } + KeyType::Secp256k1 => { + info!("importing secp256k1 key from: {}", path.display()); + + let sk = match path.extension().and_then(|ext| ext.to_str()) { + Some("der") => sec1::EcPrivateKey::from_der(buf.as_slice()).map_err(|e| anyhow!("failed to parse DER encoded secp256k1 key: {e:#?}")), + Some("pem") => { + Err(anyhow!("PEM encoded secp256k1 keys are unsupported at the moment. Please file an issue if you require this.")) + }, + _ => Err(anyhow!("please disambiguate file from either PEM or DER with a file extension.")) + }?; + let kp = secp256k1::SecretKey::try_from_bytes(sk.private_key.to_vec()) + .map(secp256k1::Keypair::from) + .map_err(|e| anyhow!("failed to import secp256k1 key: {:#?}", e))?; + Ok(identity::Keypair::from(kp)) + } + } + } + } + } +} + #[derive(Debug, Deserialize)] /// Application settings. pub struct Settings { @@ -43,6 +172,7 @@ impl Settings { /// Network-related settings for a homestar node. #[derive(Clone, Debug, Deserialize)] +#[serde(default)] pub(crate) struct Network { /// pub(crate) events_buffer_len: usize, @@ -73,6 +203,8 @@ pub(crate) struct Network { /// /// [workflow::Info]: crate::workflow::Info pub(crate) workflow_quorum: usize, + /// Pubkey setup configuration + pub(crate) keypair_config: PubkeyConfig, } /// Database-related settings for a homestar node. @@ -98,6 +230,7 @@ impl Default for Network { websocket_port: 1337, websocket_capacity: 100, workflow_quorum: 3, + keypair_config: PubkeyConfig::Random, } } } @@ -133,3 +266,55 @@ impl Settings { s.try_deserialize() } } + +#[cfg(test)] +mod test { + use crate::Settings; + + #[test] + fn import_existing_key() { + let settings = Settings::build("fixtures/settings-import-ed25519.toml".into()) + .expect("setting file in test fixtures"); + + let msg = b"foo bar"; + let signature = libp2p::identity::Keypair::ed25519_from_bytes([0; 32]) + .unwrap() + .sign(msg) + .unwrap(); + // round-about way of testing since there is no Eq derive for keypairs + assert!(settings + .node + .network + .keypair_config + .keypair() + .expect("import ed25519 key") + .public() + .verify(msg, &signature)); + } + + #[test] + fn import_secp256k1_key() { + let settings = Settings::build("fixtures/settings-import-secp256k1.toml".into()) + .expect("setting file in test fixtures"); + + settings + .node + .network + .keypair_config + .keypair() + .expect("import secp256k1 key"); + } + + #[test] + fn seeded_secp256k1_key() { + let settings = Settings::build("fixtures/settings-random-secp256k1.toml".into()) + .expect("setting file in test fixtures"); + + settings + .node + .network + .keypair_config + .keypair() + .expect("generate a seeded secp256k1 key"); + } +}