diff --git a/Cargo.lock b/Cargo.lock index 45b246616d..1bcf053edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.61", "time", ] @@ -140,10 +140,26 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.61", "time", ] +[[package]] +name = "asn1-rs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607495ec7113b178fbba7a6166a27f99e774359ef4823adbefd756b5b81d7970" +dependencies = [ + "asn1-rs-derive 0.6.0", + "asn1-rs-impl 0.2.0", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", + "thiserror 2.0.11", +] + [[package]] name = "asn1-rs-derive" version = "0.4.0" @@ -164,7 +180,19 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", + "synstructure 0.13.1", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -187,7 +215,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -242,7 +270,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -311,7 +339,7 @@ dependencies = [ "tedge_config", "tedge_mqtt_ext", "tedge_utils", - "thiserror", + "thiserror 1.0.61", "time", "tokio", ] @@ -379,7 +407,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -445,7 +473,7 @@ dependencies = [ "tedge_config", "tedge_mqtt_ext", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", ] @@ -500,6 +528,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "batcher" version = "1.4.2" @@ -626,7 +660,7 @@ dependencies = [ "tedge_config", "tedge_utils", "tempfile", - "thiserror", + "thiserror 1.0.61", "tokio", "toml 0.8.8", "url", @@ -656,7 +690,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "toml 0.8.8", @@ -712,7 +746,7 @@ dependencies = [ "tedge_mqtt_ext", "tedge_test_utils", "tedge_utils", - "thiserror", + "thiserror 1.0.61", "tokio", ] @@ -739,7 +773,7 @@ dependencies = [ "tedge_http_ext", "tedge_test_utils", "tedge_utils", - "thiserror", + "thiserror 1.0.61", "time", "tokio", ] @@ -777,7 +811,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "toml 0.8.8", @@ -814,9 +848,11 @@ name = "certificate" version = "1.4.2" dependencies = [ "anyhow", + "asn1-rs 0.7.0", "assert_matches", - "base64 0.13.1", + "base64 0.22.1", "camino", + "cryptoki", "rcgen", "reqwest", "rustls 0.21.11", @@ -827,7 +863,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "sha-1", "tempfile", - "thiserror", + "thiserror 1.0.61", "time", "tracing", "x509-parser 0.16.0", @@ -883,7 +919,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -913,7 +949,7 @@ dependencies = [ "tedge_actors", "tedge_api", "tedge_mqtt_ext", - "thiserror", + "thiserror 1.0.61", "time", "tokio", ] @@ -1012,6 +1048,29 @@ dependencies = [ "typenum", ] +[[package]] +name = "cryptoki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e45b5f1ddb06ae54aea3f6d60e88c493356c51f43b8e741c9d5ac661f4d02d3" +dependencies = [ + "bitflags 1.3.2", + "cryptoki-sys", + "libloading", + "log", + "paste", + "secrecy", +] + +[[package]] +name = "cryptoki-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062316aeb324b15a0b10c9475a9e67d613b7409056c33fbe33b7c12027408cfb" +dependencies = [ + "libloading", +] + [[package]] name = "csv" version = "1.3.0" @@ -1078,7 +1137,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -1100,7 +1159,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -1177,7 +1236,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -1240,7 +1299,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "tokio", "url", ] @@ -1367,7 +1426,7 @@ dependencies = [ "assert_matches", "nix", "tempfile", - "thiserror", + "thiserror 1.0.61", "tracing", ] @@ -1410,7 +1469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" dependencies = [ "nom", - "thiserror", + "thiserror 1.0.61", ] [[package]] @@ -1478,7 +1537,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -1932,7 +1991,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2088,7 +2147,7 @@ version = "1.4.2" dependencies = [ "anyhow", "serde_json", - "thiserror", + "thiserror 1.0.61", ] [[package]] @@ -2134,6 +2193,16 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libm" version = "0.2.8" @@ -2243,7 +2312,7 @@ dependencies = [ "metrics", "metrics-util", "quanta", - "thiserror", + "thiserror 1.0.61", "tokio", "tracing", ] @@ -2256,7 +2325,7 @@ checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2291,7 +2360,7 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror", + "thiserror 1.0.61", "unicode-width", ] @@ -2303,7 +2372,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2414,7 +2483,7 @@ dependencies = [ "rumqttc", "serde", "serde_json", - "thiserror", + "thiserror 1.0.61", "tokio", "zeroize", ] @@ -2668,6 +2737,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "path-clean" version = "0.1.0" @@ -2700,7 +2775,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2735,7 +2810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.61", "ucd-trie", ] @@ -2759,7 +2834,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2800,7 +2875,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2835,7 +2910,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "tracing", @@ -2918,7 +2993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -2947,9 +3022,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2962,7 +3037,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "version_check", "yansi", ] @@ -3249,7 +3324,7 @@ dependencies = [ "rustls-native-certs 0.7.3", "rustls-pemfile 2.2.0", "rustls-webpki 0.102.8", - "thiserror", + "thiserror 1.0.61", "tokio", "tokio-rustls 0.25.0", ] @@ -3276,7 +3351,7 @@ dependencies = [ "serde", "serde_json", "slab", - "thiserror", + "thiserror 1.0.61", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -3404,9 +3479,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" @@ -3487,6 +3562,15 @@ dependencies = [ "untrusted", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -3533,7 +3617,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -3850,9 +3934,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -3885,7 +3969,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -3956,7 +4040,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "tokio", "toml 0.8.8", "tracing", @@ -4011,7 +4095,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "tokio-util", @@ -4034,7 +4118,7 @@ dependencies = [ "tedge_config", "tedge_utils", "test-case", - "thiserror", + "thiserror 1.0.61", "tracing", ] @@ -4088,7 +4172,7 @@ dependencies = [ "tedge_api", "tedge_config", "tedge_utils", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "tracing", @@ -4119,7 +4203,7 @@ dependencies = [ "env_logger", "futures", "log", - "thiserror", + "thiserror 1.0.61", "tokio", ] @@ -4147,7 +4231,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "toml 0.8.8", @@ -4182,7 +4266,7 @@ dependencies = [ "tedge_utils", "tempfile", "test-case", - "thiserror", + "thiserror 1.0.61", "toml 0.8.8", "tracing", "tracing-subscriber", @@ -4204,7 +4288,7 @@ dependencies = [ "serde", "serde_json", "tedge_config_macros-macro", - "thiserror", + "thiserror 1.0.61", "toml 0.8.8", "tracing", "url", @@ -4222,7 +4306,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.96", "test-case", ] @@ -4232,7 +4316,7 @@ version = "1.4.2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "tedge_config_macros-impl", ] @@ -4258,7 +4342,7 @@ dependencies = [ "tedge_uploader_ext", "tedge_utils", "tempfile", - "thiserror", + "thiserror 1.0.61", "tokio", "toml 0.8.8", "uzers", @@ -4308,7 +4392,7 @@ dependencies = [ "tedge_api", "tedge_config", "tedge_mqtt_ext", - "thiserror", + "thiserror 1.0.61", "tokio", ] @@ -4326,7 +4410,7 @@ dependencies = [ "serde", "serde_json", "tedge_actors", - "thiserror", + "thiserror 1.0.61", "tokio", ] @@ -4354,7 +4438,7 @@ dependencies = [ "tedge_test_utils", "tedge_uploader_ext", "tedge_utils", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "toml 0.8.8", @@ -4380,7 +4464,7 @@ dependencies = [ "tedge_actors", "tedge_config", "tedge_test_utils", - "thiserror", + "thiserror 1.0.61", "tokio", "tracing", ] @@ -4478,7 +4562,7 @@ dependencies = [ "strum", "tedge_test_utils", "tempfile", - "thiserror", + "thiserror 1.0.61", "time", "tokio", "tracing", @@ -4542,7 +4626,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -4553,7 +4637,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "test-case-core", ] @@ -4574,7 +4658,16 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.61", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -4585,7 +4678,18 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -4668,7 +4772,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -4812,7 +4916,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -4882,7 +4986,7 @@ dependencies = [ "rand", "rustls 0.21.11", "sha1", - "thiserror", + "thiserror 1.0.61", "url", "utf-8", ] @@ -4971,7 +5075,7 @@ dependencies = [ "reqwest", "tedge_test_utils", "tempfile", - "thiserror", + "thiserror 1.0.61", "tokio", "tokio-util", ] @@ -5094,7 +5198,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "wasm-bindgen-shared", ] @@ -5128,7 +5232,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5428,7 +5532,7 @@ dependencies = [ "nom", "oid-registry 0.6.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.61", "time", ] @@ -5445,7 +5549,7 @@ dependencies = [ "nom", "oid-registry 0.7.1", "rusticata-macros", - "thiserror", + "thiserror 1.0.61", "time", ] @@ -5493,7 +5597,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -5514,7 +5618,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] [[package]] @@ -5534,7 +5638,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", "synstructure 0.13.1", ] @@ -5563,5 +5667,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.96", ] diff --git a/Cargo.toml b/Cargo.toml index bf46492cd6..d33f3f9719 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ upload = { path = "crates/common/upload" } anstyle = "1.0" anyhow = "1.0" +asn1-rs = { version = "0.7.0", features = ["bigint"] } assert-json-diff = "2.0" assert_cmd = "2.0" assert_matches = "1.5" @@ -97,6 +98,7 @@ clap = { version = "4.5", features = [ "unstable-styles", ] } clap_complete = { version = "4.5", features = ["unstable-dynamic"] } +cryptoki = "0.8.0" csv = "1.1" darling = "0.20" doku = "0.21" diff --git a/crates/common/certificate/Cargo.toml b/crates/common/certificate/Cargo.toml index 587f195818..343ee30bc4 100644 --- a/crates/common/certificate/Cargo.toml +++ b/crates/common/certificate/Cargo.toml @@ -15,7 +15,10 @@ reqwest = ["dep:reqwest"] [dependencies] anyhow = { workspace = true } +asn1-rs = { workspace = true } +base64 = "0.22" camino = { workspace = true } +cryptoki = { workspace = true } rcgen = { workspace = true } reqwest = { workspace = true, optional = true } @@ -37,7 +40,6 @@ zeroize = { workspace = true } [dev-dependencies] assert_matches = { workspace = true } -base64 = { workspace = true } tempfile = { workspace = true } time = { workspace = true, features = ["macros"] } diff --git a/crates/common/certificate/src/rustls022/parse_root_certificate.rs b/crates/common/certificate/src/rustls022/parse_root_certificate.rs index 9027c7dbd8..fa882095d5 100644 --- a/crates/common/certificate/src/rustls022/parse_root_certificate.rs +++ b/crates/common/certificate/src/rustls022/parse_root_certificate.rs @@ -9,9 +9,13 @@ use std::fs; use std::fs::File; use std::io::BufReader; use std::path::Path; +use std::sync::Arc; use super::CertificateError; +mod pkcs11; +use pkcs11::Pkcs11Resolver; + pub fn create_tls_config( root_certificates: impl AsRef, client_private_key: impl AsRef, @@ -26,6 +30,21 @@ pub fn create_tls_config( .with_client_auth_cert(cert_chain, pvt_key)?) } +pub fn create_tls_config_piv( + root_certificates: impl AsRef, + piv_serial: Arc, +) -> Result { + let root_cert_store = new_root_store(root_certificates.as_ref())?; + + let resolver = Pkcs11Resolver::from_piv_serial(&piv_serial).expect("failed to create resolver"); + + let config = ClientConfig::builder() + .with_root_certificates(root_cert_store) + .with_client_cert_resolver(resolver); + + Ok(config) +} + pub fn client_config_for_ca_certificates

( root_certificates: impl IntoIterator, ) -> Result diff --git a/crates/common/certificate/src/rustls022/parse_root_certificate/pkcs11.rs b/crates/common/certificate/src/rustls022/parse_root_certificate/pkcs11.rs new file mode 100644 index 0000000000..1196172b36 --- /dev/null +++ b/crates/common/certificate/src/rustls022/parse_root_certificate/pkcs11.rs @@ -0,0 +1,423 @@ +//! Example of how to configure rumqttd to connect to a server using TLS and authentication. +//! Source https://github.com/leonardodepaula/Cryptoki-TLS +//! https://github.com/rustls/rustls-cng/blob/dev/src/signer.rs +use std::error::Error; + +use asn1_rs::ToDer; +use base64::Engine; +use rustls_022 as rustls; +use rustls_native_certs_022 as rustls_native_certs; +use rustls_pemfile_022 as rustls_pemfile; + +use rustls::ClientConfig; + +// Only used when loading certs from file +use rustls::pki_types::pem::PemObject; + +use cryptoki::{ + context::{CInitializeArgs, Pkcs11}, + mechanism::{ + rsa::{PkcsMgfType, PkcsPssParams}, + Mechanism, MechanismType, + }, + object::{Attribute, AttributeType, CertificateType, ObjectClass}, + session::{Session, UserType}, + types::AuthPin, +}; +use rustls::{ + client::ResolvesClientCert, + pki_types::CertificateDer, + sign::{CertifiedKey, Signer, SigningKey}, + Error as RusTLSError, SignatureAlgorithm, SignatureScheme, +}; +use std::sync::{Arc, Mutex}; +use x509_parser::{ + der_parser::der, + prelude::{FromDer, X509Certificate}, +}; + +const PCKS11_MODULE_PATH: &str = "/usr/lib/x86_64-linux-gnu/libykcs11.so"; +const PKCS11_PIN: &str = "123456"; +const PKCS11_TOKENLABEL: &str = "YubiKey PIV #30590875"; + +#[derive(Debug, Clone)] +struct PKCS11 { + session: Arc>, +} + +#[derive(Debug)] +struct MySigner { + pkcs11: PKCS11, + scheme: SignatureScheme, +} + +impl MySigner { + fn get_mechanism(&self) -> anyhow::Result { + match self.scheme { + SignatureScheme::ED25519 => Ok(Mechanism::Eddsa), + SignatureScheme::ECDSA_NISTP256_SHA256 => Ok(Mechanism::EcdsaSha256), + SignatureScheme::ECDSA_NISTP384_SHA384 => Ok(Mechanism::EcdsaSha384), + SignatureScheme::ECDSA_NISTP521_SHA512 => Ok(Mechanism::EcdsaSha512), + SignatureScheme::RSA_PKCS1_SHA256 => Ok(Mechanism::Sha256RsaPkcs), + SignatureScheme::RSA_PKCS1_SHA384 => Ok(Mechanism::Sha384RsaPkcs), + SignatureScheme::RSA_PKCS1_SHA512 => Ok(Mechanism::Sha512RsaPkcs), + SignatureScheme::RSA_PSS_SHA256 => { + let params = PkcsPssParams { + hash_alg: MechanismType::SHA256_RSA_PKCS, + mgf: PkcsMgfType::MGF1_SHA256, + s_len: 32.into(), + }; + Ok(Mechanism::Sha256RsaPkcsPss(params)) + } + SignatureScheme::RSA_PSS_SHA384 => { + let params = PkcsPssParams { + hash_alg: MechanismType::SHA384_RSA_PKCS, + mgf: PkcsMgfType::MGF1_SHA384, + s_len: 48.into(), + }; + Ok(Mechanism::Sha384RsaPkcsPss(params)) + } + SignatureScheme::RSA_PSS_SHA512 => { + let params = PkcsPssParams { + hash_alg: MechanismType::SHA512_RSA_PKCS, + mgf: PkcsMgfType::MGF1_SHA512, + s_len: 64.into(), + }; + Ok(Mechanism::Sha512RsaPkcsPss(params)) + } + _ => Err(RusTLSError::General( + "Unsupported signature scheme".to_owned(), + )), + } + } +} + +impl Signer for MySigner { + fn sign(&self, message: &[u8]) -> anyhow::Result, RusTLSError> { + let session = self.pkcs11.session.lock().unwrap(); + + let key_template = vec![ + Attribute::Token(true), + Attribute::Private(true), + Attribute::Sign(true), + // Attribute::KeyType(KeyType::EC), + ]; + + let key = session + .find_objects(&key_template) + .unwrap() + .into_iter() + .nth(0) + .unwrap(); + + let info = session.get_attributes( + key, + &[ + AttributeType::Id, + AttributeType::Class, + AttributeType::AcIssuer, + AttributeType::Application, + AttributeType::Label, + AttributeType::Coefficient, + AttributeType::KeyType, + AttributeType::Issuer, + AttributeType::Url, + ], + ); + println!(""); + match info { + Ok(value) => { + for v in &value[0..value.len() - 1] { + match v { + Attribute::Application(raw_value) => println!( + "Private Key Application: {:?}", + String::from_utf8_lossy(raw_value) + ), + Attribute::Label(raw_value) => println!( + "Private Key Label: {:?}", + String::from_utf8_lossy(raw_value) + ), + Attribute::Id(raw_value) => { + println!("Private Key Id: {:?}", String::from_utf8_lossy(raw_value)) + } + Attribute::Issuer(raw_value) => println!( + "Private Key Issuer: {:?}", + String::from_utf8_lossy(raw_value) + ), + Attribute::Url(raw_value) => { + println!("Private Key URL: {:?}", String::from_utf8_lossy(raw_value)) + } + Attribute::Class(raw_value) => { + println!("Private Key Class: {:?}", raw_value.to_string()) + } + _ => { + println!("Could not read attribute value: {:?}", v); + } + } + } + } + Err(err) => println!("Could not read value: {err:?}"), + }; + // info + let mechanism = self.get_mechanism().unwrap(); + println!( + "Input message ({:?}): {:?}", + mechanism, + String::from_utf8_lossy(&message) + ); + + // Optional + let direct_sign = true; + + let signature_raw = if direct_sign { + let signature_raw = match session.sign(&mechanism, key, &message) { + Ok(result) => result, + Err(err) => { + println!("Failed to sign: {err:?}"); + "".into() + } + }; + signature_raw + } else { + let digest = session.digest(&Mechanism::Sha256, &message).unwrap(); + session.sign(&mechanism, key, &digest).unwrap() + }; + + // Split raw signature into r and s values (assuming 32 bytes each) + println!("Signature (raw) len={:?}", signature_raw.len()); + let r_bytes = signature_raw[0..32].to_vec(); + let s_bytes = signature_raw[32..].to_vec(); + let signature_asn1 = format_asn1_ecdsa_signature(&r_bytes, &s_bytes).unwrap(); + println!( + "Encoded ASN.1 Signature: len={:?} {:?}", + signature_asn1.len(), + signature_asn1 + ); + Ok(signature_asn1) + } + + fn scheme(&self) -> SignatureScheme { + println!("Using scheme: {:?}", self.scheme.as_str()); + self.scheme + } +} + +#[derive(Debug)] +struct MySigningKey { + pkcs11: PKCS11, +} + +impl MySigningKey { + fn supported_schemes(&self) -> &[SignatureScheme] { + &[ + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP521_SHA512, + ] + } +} + +impl SigningKey for MySigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + let supported = self.supported_schemes(); + for scheme in offered { + if supported.contains(scheme) { + println!("Matching scheme: {:?}", scheme.as_str()); + return Some(Box::new(MySigner { + pkcs11: self.pkcs11.clone(), + scheme: *scheme, + })); + } + } + None + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::ECDSA + // SignatureAlgorithm::RSA + } +} + +#[derive(Debug)] +pub struct Pkcs11Resolver { + chain: Vec>, + signing_key: Arc, +} + +impl ResolvesClientCert for Pkcs11Resolver { + fn resolve( + &self, + _acceptable_issuers: &[&[u8]], + _sigschemes: &[SignatureScheme], + ) -> Option> { + Some(Arc::new(CertifiedKey { + cert: self.chain.clone(), + key: self.signing_key.clone(), + ocsp: None, + })) + } + + fn has_certs(&self) -> bool { + true + } +} + +fn get_certificate_der( + pkcs11: PKCS11, +) -> anyhow::Result>, anyhow::Error> { + let session = pkcs11.session.lock().unwrap(); + let search_template = vec![ + Attribute::Class(ObjectClass::CERTIFICATE), + Attribute::CertificateType(CertificateType::X_509), + ]; + let handle = session.find_objects(&search_template)?.remove(0); + let value = session + .get_attributes(handle, &[AttributeType::Value])? + .remove(0); + + // Print out info about the certificate + match value { + Attribute::Value(cert) => { + println!("Certificate: {:?}", CertificateDer::from_slice(&cert)); + + let res = X509Certificate::from_der(&cert); + match res { + Ok((_rem, cert)) => { + println!( + "Public Key: {:?}", + cert.public_key().algorithm.algorithm.to_string() + ); + println!("Subject: {:?}", cert.subject().to_string()); + println!("Issuer: {:?}", cert.issuer().to_string()); + println!("Serial: {:?}", cert.raw_serial_as_string().replace(":", "")); + } + _ => panic!("x509 parsing failed: {:?}", res), + } + + let certificate_der = CertificateDer::from_slice(&cert).into_owned(); + Ok(vec![certificate_der]) + } + _ => { + anyhow::bail!("Couldn't find X509 certificate.") + } + } +} + +impl Pkcs11Resolver { + pub fn from_piv_serial(piv_serial: &str) -> anyhow::Result> { + // Alternative module: /opt/homebrew/lib/pkcs11/opensc-pkcs11.so + let pkcs11module = std::env::var("PKCS11_MODULE"); + let pkcs11module = pkcs11module.as_deref().unwrap_or(PCKS11_MODULE_PATH); + let pkcs11client = Pkcs11::new(pkcs11module)?; + pkcs11client.initialize(CInitializeArgs::OsThreads)?; + + let slot = pkcs11client.get_slots_with_token()?.remove(0); + let session = pkcs11client.open_ro_session(slot)?; + session.login(UserType::User, Some(&AuthPin::new("123456".into())))?; + + // Debug + let search = vec![ + Attribute::Class(ObjectClass::CERTIFICATE), + Attribute::CertificateType(CertificateType::X_509), + ]; + for handle in session.find_objects(&search)? { + // each cert: get the "value" which will be the raw certificate data + for value in session.get_attributes(handle, &[AttributeType::SerialNumber])? { + if let Attribute::Value(value) = value { + match String::from_utf8(value) { + Ok(path) => println!("Certificate value: {:?}", path), + Err(e) => println!("Invalid UTF-8 sequence: {}", e), + }; + } + } + } + + let pkcs11 = PKCS11 { + session: Arc::new(Mutex::new(session)), + }; + let chain = get_certificate_der(pkcs11.clone())?; + let my_signing_key = Arc::new(MySigningKey { pkcs11 }); + + let mut root_cert_store = rustls::RootCertStore::empty(); + root_cert_store.add_parsable_certificates( + rustls_native_certs::load_native_certs().expect("could not load platform certs"), + ); + + // Alternative: Create client using file based certs (without using HSM) + // let client_cert = rumqttc::tokio_rustls::rustls::pki_types::CertificateDer::from_pem_file("/opt/homebrew/etc/tedge/device-certs/tedge-certificate.pem")?; + // let client_key = rumqttc::tokio_rustls::rustls::pki_types::PrivateKeyDer::from_pem_file("/opt/homebrew/etc/tedge/device-certs/tedge-private-key.pem")?; + // let client_config = ClientConfig::builder() + // .with_root_certificates(root_cert_store) + // .with_client_auth_cert(chain, client_key)?; + + // Create client using custom client cert resolver + let resolver = Arc::new(Pkcs11Resolver { + chain, + signing_key: my_signing_key, + }); + + // let mqtt_client_id = std::env::var("DEVICE_ID"); + // let mqtt_client_id = mqtt_client_id.as_deref().unwrap_or("rmi_macos01"); + + // let c8y_domain = std::env::var("C8Y_DOMAIN"); + // let c8y_domain = c8y_domain + // .as_deref() + // .unwrap_or("thin-edge-io.eu-latest.cumulocity.com"); + + // let mut mqttoptions = MqttOptions::new(mqtt_client_id, c8y_domain, 8883); + // mqttoptions.set_keep_alive(std::time::Duration::from_secs(60)); + + // mqttoptions.set_transport(rumqttc::Transport::tls_with_config(client_config.into())); + + // let (_client, mut eventloop) = AsyncClient::new(mqttoptions, 10); + + // _client.subscribe("s/ds", rumqttc::QoS::AtLeastOnce).await?; + // _client + // .publish("s/us", rumqttc::QoS::AtLeastOnce, false, "500") + // .await?; + + // loop { + // match eventloop.poll().await { + // Ok(v) => { + // println!("Event = {v:?}"); + // } + // Err(e) => { + // println!("Error = {e:?}"); + // break; + // } + // } + // } + + Ok(resolver) + } +} + +fn format_asn1_ecdsa_signature(r_bytes: &[u8], s_bytes: &[u8]) -> Result, anyhow::Error> { + use base64::prelude::BASE64_STANDARD_NO_PAD; + let mut writer = Vec::new(); + + write_asn1_integer(&mut writer, r_bytes); + + write_asn1_integer(&mut writer, s_bytes); + + let seq = asn1_rs::Sequence::new(writer.into()); + let b = seq.to_der_vec().unwrap(); + println!("Encoded ASN.1 Der: {:?}", BASE64_STANDARD_NO_PAD.encode(&b)); + Ok(b) +} + +fn write_asn1_integer(writer: &mut dyn std::io::Write, b: &[u8]) { + let mut i = asn1_rs::BigInt::from_signed_bytes_be(&b); + if i.sign() == asn1_rs::Sign::Minus { + // Prepend a most significant zero byte if value < 0 + let mut positive = b.to_vec(); + positive.insert(0, 0); + + i = asn1_rs::BigInt::from_signed_bytes_be(&positive); + } + let i = i.to_signed_bytes_be(); + let i = asn1_rs::Integer::new(&i); + let _ = i.write_der(writer); +} diff --git a/crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs b/crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs index d04398cf5a..e0ee819f39 100644 --- a/crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs +++ b/crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs @@ -181,6 +181,11 @@ define_tedge_config! { #[doku(as = "PathBuf")] csr_path: Utf8PathBuf, + /// Use a certificate/key from a Personal Identity Verification (PIV) device of a given serial number. Overrides + /// `key_path`/`cert_path`/`csr_path` options. + #[tedge_config(example = "123456789")] + use_piv_serial: Arc, + /// The default device type #[tedge_config(example = "thin-edge.io", default(value = "thin-edge.io"))] #[tedge_config(rename = "type")] diff --git a/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs b/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs index 90135e4f9c..68bfbb2e0d 100644 --- a/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs +++ b/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs @@ -1,10 +1,13 @@ +use std::sync::Arc; + use super::ConnectError; use crate::bridge::BridgeConfig; use crate::cli::connect::CONNECTION_TIMEOUT; use anyhow::bail; use anyhow::Context as _; -use certificate::rustls022::parse_root_certificate::create_tls_config; -use certificate::rustls022::parse_root_certificate::create_tls_config_without_client_cert; +use certificate::parse_root_certificate::create_tls_config; +use certificate::parse_root_certificate::create_tls_config_without_client_cert; +use certificate::rustls022 as certificate; use rumqttc::tokio_rustls::rustls::AlertDescription; use rumqttc::tokio_rustls::rustls::CertificateError; use rumqttc::tokio_rustls::rustls::Error; @@ -26,6 +29,8 @@ pub fn create_device_with_direct_connection( use_basic_auth: bool, bridge_config: &BridgeConfig, device_type: &str, + // TODO: put into general authentication struct + use_piv_serial: Option>, ) -> anyhow::Result<()> { const DEVICE_ALREADY_EXISTS: &[u8] = b"41,100,Device already existing"; const DEVICE_CREATE_ERROR_TOPIC: &str = "s/e"; @@ -51,6 +56,11 @@ pub fn create_device_with_direct_connection( .expect("password must be set to use basic auth"), ); create_tls_config_without_client_cert(&bridge_config.bridge_root_cert_path)? + } else if let Some(use_piv_serial) = use_piv_serial { + dbg!(certificate::parse_root_certificate::create_tls_config_piv( + &bridge_config.bridge_root_cert_path, + use_piv_serial, + )?) } else { create_tls_config( &bridge_config.bridge_root_cert_path, @@ -58,6 +68,7 @@ pub fn create_device_with_direct_connection( &bridge_config.bridge_certfile, )? }; + dbg!(&tls_config); mqtt_options.set_transport(Transport::tls_with_config(tls_config.into())); let (mut client, mut connection) = Client::new(mqtt_options, 10); diff --git a/crates/core/tedge/src/cli/connect/command.rs b/crates/core/tedge/src/cli/connect/command.rs index 11554c4041..38ba54697e 100644 --- a/crates/core/tedge/src/cli/connect/command.rs +++ b/crates/core/tedge/src/cli/connect/command.rs @@ -253,6 +253,7 @@ impl ConnectCommand { } fn check_connection(&self, config: &TEdgeConfig) -> Result> { let spinner = Spinner::start("Verifying device is connected to cloud"); + dbg!("'ere m8"); let res = match &self.cloud { Cloud::Azure(profile) => check_device_status_azure(config, profile.as_deref()), Cloud::Aws(profile) => check_device_status_aws(config, profile.as_deref()), @@ -561,6 +562,16 @@ fn check_device_status_c8y( mqtt_options.set_keep_alive(RESPONSE_TIMEOUT); + if let Ok(piv_serial) = tedge_config.device.use_piv_serial.or_config_not_set() { + let tls_config = certificate::rustls022::parse_root_certificate::create_tls_config_piv( + &c8y_config.root_cert_path, + piv_serial.clone(), + )?; + dbg!("weeee"); + dbg!(&tls_config); + mqtt_options.set_transport(rumqttc::Transport::tls_with_config(tls_config.into())); + } + let (client, mut connection) = rumqttc::Client::new(mqtt_options, 10); connection .eventloop @@ -870,6 +881,7 @@ fn check_device_status_aws( } } +// TODO: too many args fn new_bridge( bridge_config: &BridgeConfig, common_mosquitto_config: &CommonMosquittoConfig, @@ -894,6 +906,9 @@ fn new_bridge( bridge_config.validate(use_basic_auth)?; + // TODO: put in general auth config struct + let use_piv_serial = tedge_config.device.use_piv_serial.or_none().cloned(); + if bridge_config.cloud_name.eq("c8y") { if offline_mode { println!("Offline mode. Skipping device creation in Cumulocity cloud.") @@ -903,6 +918,7 @@ fn new_bridge( use_basic_auth, bridge_config, device_type, + use_piv_serial, ); spinner.finish(res)?; }