diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 33feea1c2..af1286e3e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,6 +13,7 @@ jobs: - run: cargo build --locked -p olpc-cjson - run: cargo build --locked -p tough - run: cargo build --locked -p tough-ssm + - run: cargo build --locked -p tough-kms - run: cargo build --locked -p tuftool - run: cargo test --locked - run: cd tough && cargo test --all-features --locked diff --git a/Cargo.lock b/Cargo.lock index db3b55333..0b0bb9a1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,11 +178,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "loom 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "cc" @@ -503,18 +500,6 @@ dependencies = [ "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "generator" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "generic-array" version = "0.12.3" @@ -555,7 +540,7 @@ name = "h2" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -620,7 +605,7 @@ name = "http" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -630,7 +615,7 @@ name = "http-body" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -644,7 +629,7 @@ name = "hyper" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -667,7 +652,7 @@ name = "hyper-rustls" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -684,7 +669,7 @@ name = "hyper-tls" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -757,16 +742,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "loom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "generator 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "maplit" version = "1.0.2" @@ -1236,7 +1211,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1284,7 +1259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1325,13 +1300,40 @@ dependencies = [ "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rusoto_kms" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rusoto_mock" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rusoto_signature" version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1355,7 +1357,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1431,11 +1433,6 @@ dependencies = [ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scoped-tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1808,7 +1805,7 @@ name = "tokio" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1861,7 +1858,7 @@ name = "tokio-util" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1893,6 +1890,23 @@ dependencies = [ "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tough-kms" +version = "0.1.0" +dependencies = [ + "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_credential 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_kms 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_mock 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tough 0.8.0", +] + [[package]] name = "tough-ssm" version = "0.3.0" @@ -1939,6 +1953,7 @@ dependencies = [ "ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_credential 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusoto_kms 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_ssm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1948,6 +1963,7 @@ dependencies = [ "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "tough 0.8.0", + "tough-kms 0.1.0", "tough-ssm 0.3.0", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2228,7 +2244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +"checksum bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" "checksum cc 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "0fde55d2a2bfaa4c9668bbc63f531fbdeee3ffe188f4662511ce2c22b3eedebe" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" @@ -2268,7 +2284,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" "checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" "checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" -"checksum generator 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" @@ -2295,7 +2310,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum loom 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" "checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" @@ -2354,6 +2368,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)" = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" "checksum rusoto_core 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "841ca8f73e7498ba39146ab43acea906bbbb807d92ec0b7ea4b6293d2621f80d" "checksum rusoto_credential 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60669ddc1bdbb83ce225593649d36b4c5f6bf9db47cc1ab3e81281abffc853f4" +"checksum rusoto_kms 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c5a083f44d08db76d4deedd7527bb215dd008fa08f4b1d8ca40071522bdbcb7" +"checksum rusoto_mock 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb65b30ea51d8f975eba8e03e6c10ed247f11a6f1b8fa225c2b844e98113366" "checksum rusoto_signature 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9eddff187ac18c5a91d9ccda9353f30cf531620dce437c4db661dfe2e23b2029" "checksum rusoto_ssm 0.44.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e9224ad97be05dae1a0f6745252f3fa1430d6bea97c93f59e99edaeb7d70f5d" "checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" @@ -2364,7 +2380,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" "checksum schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" "checksum security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" diff --git a/tough-kms/Cargo.toml b/tough-kms/Cargo.toml index 2c51d47ae..09bcd3937 100644 --- a/tough-kms/Cargo.toml +++ b/tough-kms/Cargo.toml @@ -8,7 +8,6 @@ repository = "https://github.com/awslabs/tough" keywords = ["TUF", "KMS"] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["rusoto"] rusoto = ["rusoto-rustls"] diff --git a/tough-kms/src/client.rs b/tough-kms/src/client.rs index 6314be095..5e9832492 100644 --- a/tough-kms/src/client.rs +++ b/tough-kms/src/client.rs @@ -8,7 +8,7 @@ use rusoto_kms::KmsClient; use snafu::ResultExt; use std::str::FromStr; -/// Builds an KMS client for a given profile name. +/// Builds a KMS client for a given profile name. pub(crate) fn build_client_kms(profile: Option<&str>) -> Result { Ok(if let Some(profile) = profile { let mut provider = ProfileProvider::new().context(error::RusotoCreds)?; diff --git a/tough-kms/src/error.rs b/tough-kms/src/error.rs index 97c287eb7..35a69ed8c 100644 --- a/tough-kms/src/error.rs +++ b/tough-kms/src/error.rs @@ -1,50 +1,29 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT OR Apache-2.0 +//! Contains the error type for this library. + +#![allow(clippy::default_trait_access)] + use snafu::{Backtrace, Snafu}; +/// Alias for `Result`. pub type Result = std::result::Result; /// The error type for this library. #[derive(Debug, Snafu)] #[snafu(visibility = "pub(crate)")] +#[non_exhaustive] +#[allow(missing_docs)] pub enum Error { - #[snafu(display( - "Failed to create customer managed key for aws-kms://{}/{}: {}", - profile.as_deref().unwrap_or(""), - key_id, - source, - ))] - KmsCreateKey { - profile: Option, - key_id: String, - source: rusoto_core::RusotoError, - backtrace: Backtrace, - }, - - #[snafu(display( - "Failed to assign alias {} for created customer managed key_id {} for aws-kms://{}/{}: {}", - alias, - target_key_id, - profile.as_deref().unwrap_or(""), - key_id, - source, - ))] - KmsCreateAlias { - alias: String, - target_key_id: String, - profile: Option, - key_id: String, - source: rusoto_core::RusotoError, - backtrace: Backtrace, - }, - + /// The library failed to authenticate Aws account. #[snafu(display("Error creating AWS credentials provider: {}", source))] RusotoCreds { source: rusoto_credential::CredentialsError, backtrace: Backtrace, }, + /// The library failed to get the region for the given profile. #[snafu(display("Unable to determine region from profile '{}': {}", profile, source))] RusotoRegionFromProfile { profile: String, @@ -52,25 +31,29 @@ pub enum Error { backtrace: Backtrace, }, - #[snafu(display("Unknown AWS region \"{}\": {}", region, source))] + /// The library failed to identify the region obtained from the given profile. + #[snafu(display("Unknown AWS region '{}': {}", region, source))] RusotoRegion { region: String, source: rusoto_core::region::ParseRegionError, backtrace: Backtrace, }, + /// The library failed to instantiate 'HttpClient'. #[snafu(display("Error creating AWS request dispatcher: {}", source))] RusotoTls { source: rusoto_core::request::TlsError, backtrace: Backtrace, }, + /// The library failed to instantiate 'tokio Runtime'. #[snafu(display("Unable to create tokio runtime: {}", source))] RuntimeCreation { source: std::io::Error, backtrace: Backtrace, }, + /// The library failed to get public key from Awm kms #[snafu(display( "Failed to get public key for aws-kms://{}/{} : {}", profile.as_deref().unwrap_or(""), @@ -84,38 +67,20 @@ pub enum Error { backtrace: Backtrace, }, - #[snafu(display( - "Alias {} exist for aws-kms:://{}/{}", - key_id, - profile.as_deref().unwrap_or(""), - key_id - ))] - KmsAliasCheck { - profile: Option, - key_id: String, - }, - - #[snafu(display("Public key is none"))] + /// Empty public key was returned by AWS KMS + #[snafu(display("Public key does not exist"))] PublicKeyNone, - #[snafu(display( - "Write in Not defined for aws-kms://{}/{}", - profile.as_deref().unwrap_or(""), - key_id - ))] - WriteNotDefined { - profile: Option, + /// The library failed to get the message signature from AWS KMS + #[snafu(display("Error while signing message for aws-kms://{}/{} : {}", profile.as_deref().unwrap_or(""), key_id, source))] + KmsSignMessage { key_id: String, - }, - - #[snafu(display( - "Key metatada is None while creating key for aws-kms://{}/{}", - profile.as_deref().unwrap_or(""), - key_id - ))] - KeyMatadata { profile: Option, - key_id: String, + source: rusoto_core::RusotoError, backtrace: Backtrace, }, + + /// Empty signature was returned by AWS KMS + #[snafu(display("Empty signature returned by AWS KMS"))] + SignatureNotFound, } diff --git a/tough-kms/src/lib.rs b/tough-kms/src/lib.rs index 53462dd16..8691049ff 100644 --- a/tough-kms/src/lib.rs +++ b/tough-kms/src/lib.rs @@ -1,20 +1,51 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT OR Apache-2.0 +//! tough-kms implements the `KeySource` trait found in [tough, a Rust TUF client](https://github.com/awslabs/tough). +//! +//! By implementing this trait, AWS KMS can become a source of keys used to sign a [TUF repository](https://theupdateframework.github.io/). +//! +//! # Testing +//! +//! Unit tests are run in the usual manner: `cargo test`. + +#![forbid(missing_debug_implementations, missing_copy_implementations)] +#![deny(rust_2018_idioms)] +// missing_docs is on its own line to make it easy to comment out when making changes. +#![deny(missing_docs)] +#![warn(clippy::pedantic)] +#![allow( + clippy::module_name_repetitions, + clippy::must_use_candidate, + clippy::missing_errors_doc +)] + mod client; pub mod error; -use rusoto_kms::{Kms, KmsClient}; +use ring::rand::SecureRandom; +use rusoto_kms::{Kms, KmsClient, SignRequest}; use snafu::{OptionExt, ResultExt}; +use std::collections::HashMap; use std::fmt; use tough::key_source::KeySource; use tough::schema::decoded::{Decoded, RsaPem}; -use tough::schema::key::KmsSigningAlgorithms; -use tough::sign::{KmsRsaKey, Sign}; +use tough::schema::key::{Key, RsaKey, RsaScheme}; +use tough::sign::Sign; + +/// Represents a Signing Algorithms for AWS KMS. +#[derive(Debug, Clone, PartialEq)] +pub enum KmsSigningAlgorithms { + /// The key type + Rsa(String), +} -/// Implements the KeySource trait for keys that live in AWS KMS +/// Implements the `KeySource` trait for keys that live in AWS KMS pub struct KmsKeySource { + /// Identifies AWS account named profile, if not provided default AWS profile is used. pub profile: Option, + /// Identifies an asymmetric CMK in AWS KMS. pub key_id: String, + /// KmsClient Object to query AWS KMS pub client: Option, } @@ -27,13 +58,13 @@ impl fmt::Debug for KmsKeySource { } } -/// Implements the KeySource trait. +/// Implement the `KeySource` trait. impl KeySource for KmsKeySource { fn as_sign( &self, ) -> std::result::Result, Box> { - let kms_client = match self.client.clone() { + let kms_client = match self.client.to_owned() { Some(value) => value, None => client::build_client_kms(self.profile.as_deref())?, }; @@ -55,7 +86,8 @@ impl KeySource for KmsKeySource { .to_vec() .into(); Ok(Box::new(KmsRsaKey { - kms_client: kms_client.clone(), + profile: self.profile.clone(), + client: Some(kms_client.clone()), key_id: self.key_id.clone(), public_key: pb_key, signing_algorithm: KmsSigningAlgorithms::Rsa(String::from("RSASSA_PSS_SHA_256")), @@ -64,76 +96,76 @@ impl KeySource for KmsKeySource { fn write( &self, - value: &str, - ) -> std::result::Result<(), Box> { - let kms_client = match self.client.clone() { - Some(value) => value, - None => client::build_client_kms(self.profile.as_deref())?, - }; - // Assign an alias to the Key - let fut = kms_client.create_alias(rusoto_kms::CreateAliasRequest { - alias_name: self.key_id.clone(), - target_key_id: value.to_string(), - }); - let _response = tokio::runtime::Runtime::new() - .context(error::RuntimeCreation)? - .block_on(fut) - .context(error::KmsCreateAlias { - alias: self.key_id.clone(), - target_key_id: value.to_string(), - profile: self.profile.clone(), - key_id: self.key_id.clone(), - })?; + _value: &str, + _key_id_hex: &str, + ) -> Result<(), Box> { Ok(()) } +} + +/// Implements the Sign trait for KMS rsa Key +pub struct KmsRsaKey { + /// Key Id of Customer Managed Key in KMS used to sign the message + pub key_id: String, + /// Aws account profile + pub profile: Option, + /// KmsClient Object to query AWS KMS + pub client: Option, + /// Public Key corresponding to Customer Managed Key + pub public_key: Decoded, + /// Signing Algorithm to be used for the Customer Managed Key + pub signing_algorithm: KmsSigningAlgorithms, +} - fn create( +impl fmt::Debug for KmsRsaKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("KmsRsaKey") + .field("key_id", &self.key_id) + .field("signing_algorithm", &self.signing_algorithm) + .field("public_key", &self.public_key) + .finish() + } +} + +impl Sign for KmsRsaKey { + fn tuf_key(&self) -> Key { + // Create a Key struct for the public key + Key::Rsa { + keyval: RsaKey { + public: self.public_key.to_owned(), + _extra: HashMap::new(), + }, + scheme: RsaScheme::RsassaPssSha256, + _extra: HashMap::new(), + } + } + + fn sign( &self, - _bits: u16, - _exponent: u32, - ) -> Result<(), Box> { - let kms_client = match self.client.clone() { + msg: &[u8], + _rng: &dyn SecureRandom, + ) -> Result, Box> { + let kms_client = match self.client.to_owned() { Some(value) => value, None => client::build_client_kms(self.profile.as_deref())?, }; - // Kms currently does not have an API to validate if the alias exist. As a Hack, - // or temporary resolution, we are using the get_public_key API, if it succeeds than - // Alias exist and alarm the user. - // TODO: replace with correct API when available - let fut = kms_client.get_public_key(rusoto_kms::GetPublicKeyRequest { + let sign_fut = kms_client.sign(SignRequest { key_id: self.key_id.clone(), - ..rusoto_kms::GetPublicKeyRequest::default() + message: bytes::Bytes::from(msg.to_owned()), + message_type: Some(String::from("RAW")), + signing_algorithm: match self.signing_algorithm.clone() { + KmsSigningAlgorithms::Rsa(algorithm) => algorithm, + }, + ..rusoto_kms::SignRequest::default() }); let response = tokio::runtime::Runtime::new() .context(error::RuntimeCreation)? - .block_on(fut); - if response.is_ok() { - error::KmsAliasCheck { - profile: self.profile.clone(), - key_id: self.key_id.clone(), - } - .fail()?; - } - // Create a new Customer managed Key in KMS - let fut = kms_client.create_key(rusoto_kms::CreateKeyRequest { - customer_master_key_spec: Some(String::from("RSA_2048")), - description: Some(String::from("Tuf repository signing key")), - key_usage: Some(String::from("SIGN_VERIFY")), - ..rusoto_kms::CreateKeyRequest::default() - }); - let key_id = tokio::runtime::Runtime::new() - .context(error::RuntimeCreation)? - .block_on(fut) - .context(error::KmsCreateKey { + .block_on(sign_fut) + .context(error::KmsSignMessage { profile: self.profile.clone(), key_id: self.key_id.clone(), - })? - .key_metadata - .context(error::KeyMatadata { - profile: self.profile.clone(), - key_id: self.key_id.clone(), - })? - .key_id; - self.write(&key_id) + })?; + let signature = response.signature.context(error::SignatureNotFound)?; + Ok(signature.to_vec()) } } diff --git a/tough-kms/tests/all_test.rs b/tough-kms/tests/all_test.rs index 213906975..596fbd7e1 100644 --- a/tough-kms/tests/all_test.rs +++ b/tough-kms/tests/all_test.rs @@ -46,6 +46,7 @@ struct CreateKeyResp { } #[test] +// Ensure public key is returned on calling tuf_key fn check_tuf_key_success() { let input = "response_public_key.json"; let key_id = String::from("alias/some_alias"); @@ -85,6 +86,7 @@ fn check_tuf_key_success() { } #[test] +// Ensure message signature is returned on calling sign fn check_sign_success() { let resp_public_key = "response_public_key.json"; let resp_signature = "response_signature.json"; @@ -145,9 +147,9 @@ fn check_sign_success() { } #[test] +// Ensure call to tuf_key fails when public key is not available fn check_public_key_failure() { - let error_msg = - String::from("The request was rejected because the specified CMK is not enabled."); + let error_msg = String::from("Some error message from KMS"); let mock = MockRequestDispatcher::with_dispatch_error(HttpDispatchError::new(error_msg.clone())); let client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); @@ -171,9 +173,9 @@ fn check_public_key_failure() { } #[test] +// Ensure sign error when Kms fails to sign message. fn check_sign_request_failure() { - let error_msg = - String::from("The request was rejected because the specified CMK is not enabled."); + let error_msg = String::from("Some error message from KMS"); let resp_public_key = "response_public_key.json"; let key_id = String::from("alias/some_alias"); let mock = MultipleMockRequestDispatcher::new(vec![ @@ -214,7 +216,7 @@ fn check_sign_request_failure() { let err = result.err().unwrap(); assert_eq!( format!( - "Not able to Sign message for key-id {} : {}", + "Error while signing message for aws-kms:///{} : {}", key_id.clone(), error_msg.clone() ), @@ -223,6 +225,7 @@ fn check_sign_request_failure() { } #[test] +// Ensure sign error when Kms returns empty signature. fn check_signature_failure() { let resp_public_key = "response_public_key.json"; let resp_signature = "response_signature_empty.json"; @@ -270,182 +273,8 @@ fn check_signature_failure() { let result = kms_sign.sign("Some message to sign".as_bytes(), &rng); assert!(result.is_err()); let err = result.err().unwrap(); - assert_eq!(format!("Signature is None"), err.to_string()); -} - -#[test] -fn check_write_success() { - let mock = - MockRequestDispatcher::with_status(200).with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.CreateAlias"))); - }); - let mock_client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); - let kms_key = tough_kms::KmsKeySource { - profile: None, - key_id: String::from("alias/some_alias"), - client: Some(mock_client), - }; - let result = kms_key.write("key_id"); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()) -} - -#[test] -fn check_write_failure() { - let error_msg = String::from("Some Error message"); - let key_alias = String::from("alias/some_alias"); - let profile = Some(String::from("Some profile")); - let mock = - MockRequestDispatcher::with_dispatch_error(HttpDispatchError::new(error_msg.clone())) - .with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.CreateAlias"))); - }); - let mock_client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); - let kms_key = tough_kms::KmsKeySource { - profile: profile.clone(), - key_id: key_alias.clone(), - client: Some(mock_client), - }; - let result = kms_key.write("Some Key Id"); - assert!(result.is_err()); - let err = result.err().unwrap(); - assert_eq!( - format!("Failed to assign alias {} for created customer managed key_id Some Key Id for aws-kms://{}/{}: {}",key_alias.clone(), profile.as_deref().unwrap_or(""), key_alias.clone(), error_msg.clone()), - err.to_string() - ); -} - -#[test] -fn check_create_success() { - let public_key_error = String::from("Key id is not available"); - let resp_create_key = "response_create_key.json"; - let mock = MultipleMockRequestDispatcher::new(vec![ - MockRequestDispatcher::with_dispatch_error(HttpDispatchError::new( - public_key_error.clone(), - )) - .with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.GetPublicKey"))); - }), - MockRequestDispatcher::with_status(200) - .with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.CreateKey"))); - }) - .with_body( - MockResponseReader::read_response( - test_utils::test_data().to_str().unwrap(), - resp_create_key, - ) - .as_ref(), - ), - MockRequestDispatcher::with_status(200).with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.CreateAlias"))); - }), - ]); - let mock_client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); - let kms_key = tough_kms::KmsKeySource { - profile: None, - key_id: String::from("alias/some_alias"), - client: Some(mock_client), - }; - let result = kms_key.create(3072, 2048); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()) -} - -#[test] -fn check_create_failure() { - let key_alias = String::from("alias/some_alias"); - let profile = Some(String::from("Some profile")); - let public_key_error = String::from("Key id is not available"); - let create_error = String::from("Some error message"); - let mock = MultipleMockRequestDispatcher::new(vec![ - MockRequestDispatcher::with_dispatch_error(HttpDispatchError::new( - public_key_error.clone(), - )) - .with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.GetPublicKey"))); - }), - MockRequestDispatcher::with_dispatch_error(HttpDispatchError::new(create_error.clone())) - .with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.CreateKey"))); - }), - ]); - let mock_client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); - let kms_key = tough_kms::KmsKeySource { - profile: profile.clone(), - key_id: key_alias.clone(), - client: Some(mock_client), - }; - let result = kms_key.create(3072, 2048); - assert!(result.is_err()); - let err = result.err().unwrap(); - assert_eq!( - format!( - "Failed to create customer managed key for aws-kms://{}/{}: {}", - profile.as_deref().unwrap_or(""), - key_alias.clone(), - create_error.clone() - ), - err.to_string() - ); -} - -#[test] -fn check_create_failure_alias_exist() { - let key_alias = String::from("alias/some_alias"); - let profile = Some(String::from("Some profile")); - let mock = - MockRequestDispatcher::with_status(200).with_request_checker(|request: &SignedRequest| { - assert!(request - .headers - .get("x-amz-target") - .unwrap() - .contains(&Vec::from("TrentService.GetPublicKey"))); - }); - let mock_client = KmsClient::new_with(mock, MockCredentialsProvider, Region::UsEast1); - let kms_key = tough_kms::KmsKeySource { - profile: profile.clone(), - key_id: key_alias.clone(), - client: Some(mock_client), - }; - let result = kms_key.create(3072, 2048); - assert!(result.is_err()); - let err = result.err().unwrap(); assert_eq!( - format!( - "Alias {} exist for aws-kms:://{}/{}", - key_alias.clone(), - profile.as_deref().unwrap_or(""), - key_alias.clone() - ), + format!("Empty signature returned by AWS KMS"), err.to_string() ); } diff --git a/tough-ssm/src/lib.rs b/tough-ssm/src/lib.rs index c95ae2df4..5b4259496 100644 --- a/tough-ssm/src/lib.rs +++ b/tough-ssm/src/lib.rs @@ -55,11 +55,12 @@ impl KeySource for SsmKeySource { fn write( &self, value: &str, + key_id_hex: &str, ) -> std::result::Result<(), Box> { let ssm_client = client::build_client(self.profile.as_deref())?; let fut = ssm_client.put_parameter(rusoto_ssm::PutParameterRequest { name: self.parameter_name.to_owned(), - description: Some(String::from("Tough Key")), + description: Some(key_id_hex.to_owned()), key_id: self.key_id.as_ref().cloned(), overwrite: Some(true), type_: Some("SecureString".to_owned()), diff --git a/tough/Cargo.toml b/tough/Cargo.toml index 39fe01946..566186b8d 100644 --- a/tough/Cargo.toml +++ b/tough/Cargo.toml @@ -9,7 +9,6 @@ keywords = ["tuf", "update", "repository"] edition = "2018" [dependencies] -bytes = "0.5.6" chrono = { version = "0.4.11", features = ["serde"] } globset = { version = "0.4.5" } hex = "0.4.2" @@ -18,13 +17,10 @@ olpc-cjson = { version = "0.1.0", path = "../olpc-cjson" } pem = "0.8.1" reqwest = { version = "0.10.4", optional = true, default-features = false, features = ["blocking"] } ring = { version = "0.16.13", features = ["std"] } -rusoto_core = { version = "0.44", optional = true, default-features = false } -rusoto_kms = { version = "0.44", optional = true, default-features = false } serde = { version = "1.0.110", features = ["derive"] } serde_json = "1.0.53" serde_plain = "0.3.0" snafu = "0.6.8" -tokio = "0.2.13" untrusted = "0.7.0" url = "2.1.0" walkdir = "2.2.9" @@ -36,10 +32,6 @@ tempfile = "3.1.0" [features] http = ["reqwest"] -default = ["rusoto"] -rusoto = ["rusoto-rustls"] -rusoto-native-tls = [ "rusoto_core/native-tls", "rusoto_kms/native-tls"] -rusoto-rustls = ["rusoto_core/rustls", "rusoto_kms/rustls"] # The `integ` feature enables integration tests. These tests require docker to be running on the host. integ = [] diff --git a/tough/src/editor/signed.rs b/tough/src/editor/signed.rs index 00e96dcc9..71230550c 100644 --- a/tough/src/editor/signed.rs +++ b/tough/src/editor/signed.rs @@ -76,7 +76,7 @@ where role: T::TYPE.to_string(), })?; for (signing_key_id, signing_key) in valid_keys { - let sig = signing_key.sign(&data, rng)?; + let sig = signing_key.sign(&data, rng).context(error::SignMessage)?; // Add the signatures to the `Signed` struct for this role role.signatures.push(Signature { diff --git a/tough/src/error.rs b/tough/src/error.rs index 3f0a3f709..ad2ce7d0b 100644 --- a/tough/src/error.rs +++ b/tough/src/error.rs @@ -286,6 +286,12 @@ pub enum Error { backtrace: Backtrace, }, + #[snafu(display("Failed to sign message: {}", source))] + SignMessage { + source: Box, + backtrace: Backtrace, + }, + #[snafu(display("Unable to find signing keys for role '{}'", role))] SigningKeysNotFound { role: String }, @@ -491,46 +497,6 @@ pub enum Error { actual: usize, threshold: u64, }, - - #[snafu(display("Not able to Sign message for key-id {} : {}", key_id, source))] - KmsSign { - key_id: String, - source: rusoto_core::RusotoError, - backtrace: Backtrace, - }, - - #[snafu(display("Unable to create tokio runtime: {}", source))] - RuntimeCreation { - source: std::io::Error, - backtrace: Backtrace, - }, - - #[snafu(display("Signature is None"))] - SignatureNotFound, - - #[snafu(display("Key Id: {} is not sign and verify key", key_id))] - NotSignVerifyKeyType { key_id: String }, - - #[snafu(display("Failed to run {}: {}", command_str, source))] - CommandExec { - command_str: String, - source: std::io::Error, - backtrace: Backtrace, - }, - - #[snafu(display("Command {} failed with {}", command_str, status))] - CommandStatus { - command_str: String, - status: std::process::ExitStatus, - backtrace: Backtrace, - }, - - #[snafu(display("Command {} output is not valid UTF-8: {}", command_str, source))] - CommandUtf8 { - command_str: String, - source: std::string::FromUtf8Error, - backtrace: Backtrace, - }, } // used in `std::io::Read` implementations diff --git a/tough/src/key_source.rs b/tough/src/key_source.rs index e15d28589..9bee6d0cb 100644 --- a/tough/src/key_source.rs +++ b/tough/src/key_source.rs @@ -5,7 +5,7 @@ //! obtained, for example, from local files or from cloud provider key stores. use crate::error; use crate::sign::{parse_keypair, Sign}; -use snafu::{ensure, ResultExt}; +use snafu::ResultExt; use std::fmt::Debug; use std::path::PathBuf; use std::result::Result; @@ -17,38 +17,11 @@ pub trait KeySource: Debug + Send + Sync { fn as_sign(&self) -> Result, Box>; /// Writes a key back to the `KeySource` - fn write(&self, value: &str) -> Result<(), Box>; - /// Creates a key for key source, default implementation creates rsa key pair which can - /// be override by individual `KeySource` implementation - fn create( + fn write( &self, - bits: u16, - exponent: u32, - ) -> Result<(), Box> { - // ring doesn't support RSA key generation yet - // https://github.com/briansmith/ring/issues/219 - let mut command = std::process::Command::new("openssl"); - command.args(&["genpkey", "-algorithm", "RSA", "-pkeyopt"]); - command.arg(format!("rsa_keygen_bits:{}", bits)); - command.arg("-pkeyopt"); - command.arg(format!("rsa_keygen_pubexp:{}", exponent)); - - let command_str = format!("{:?}", command); - let output = command.output().context(error::CommandExec { - command_str: &command_str, - })?; - ensure!( - output.status.success(), - error::CommandStatus { - command_str: &command_str, - status: output.status - } - ); - let stdout = - String::from_utf8(output.stdout).context(error::CommandUtf8 { command_str })?; - self.write(&stdout)?; - Ok(()) - } + value: &str, + key_id_hex: &str, + ) -> Result<(), Box>; } /// Points to a local key using a filesystem path. @@ -65,7 +38,11 @@ impl KeySource for LocalKeySource { Ok(Box::new(parse_keypair(&data)?)) } - fn write(&self, value: &str) -> Result<(), Box> { + fn write( + &self, + value: &str, + _key_id_hex: &str, + ) -> Result<(), Box> { Ok(std::fs::write(&self.path, value.as_bytes()) .context(error::FileWrite { path: &self.path })?) } diff --git a/tough/src/schema/key.rs b/tough/src/schema/key.rs index adec7751b..76a139493 100644 --- a/tough/src/schema/key.rs +++ b/tough/src/schema/key.rs @@ -127,13 +127,6 @@ pub struct EcdsaKey { pub _extra: HashMap, } -/// Represents a Signing Algorithms for AWS KMS. -#[derive(Debug, Clone, PartialEq)] -pub enum KmsSigningAlgorithms { - /// The key type - Rsa(String), -} - impl Key { /// Calculate the key ID for this key. pub fn key_id(&self) -> Result> { diff --git a/tough/src/sign.rs b/tough/src/sign.rs index 8210eea64..184efc306 100644 --- a/tough/src/sign.rs +++ b/tough/src/sign.rs @@ -4,14 +4,11 @@ //! Provides the `Sign` trait which abstracts over the method of signing with different key types. use crate::error::{self, Result}; -use crate::schema::decoded::{Decoded, RsaPem}; -use crate::schema::key::{Key, KmsSigningAlgorithms}; +use crate::schema::key::Key; use ring::rand::SecureRandom; use ring::signature::{KeyPair, RsaKeyPair}; -use rusoto_kms::{Kms, KmsClient, SignRequest}; -use snafu::{OptionExt, ResultExt}; +use snafu::ResultExt; use std::collections::HashMap; -use std::fmt; /// This trait must be implemented for each type of key with which you will /// sign things. @@ -20,7 +17,11 @@ pub trait Sign: Sync + Send { fn tuf_key(&self) -> crate::schema::key::Key; /// Signs the supplied message - fn sign(&self, msg: &[u8], rng: &dyn SecureRandom) -> Result>; + fn sign( + &self, + msg: &[u8], + rng: &dyn SecureRandom, + ) -> std::result::Result, Box>; } /// Implements the Sign trait for RSA keypairs @@ -38,7 +39,11 @@ impl Sign for RsaKeyPair { } } - fn sign(&self, msg: &[u8], rng: &dyn SecureRandom) -> Result> { + fn sign( + &self, + msg: &[u8], + rng: &dyn SecureRandom, + ) -> std::result::Result, Box> { let mut signature = vec![0; self.public_modulus_len()]; self.sign(&ring::signature::RSA_PSS_SHA256, rng, msg, &mut signature) .context(error::Sign)?; @@ -67,60 +72,3 @@ pub fn parse_keypair(key: &[u8]) -> Result { error::KeyUnrecognized.fail() } } - -/// Implements the Sign trait for KMS rsa Key -pub struct KmsRsaKey { - /// Key Id of Customer Managed Key in KMS used to sign the message - pub key_id: String, - /// KmsClient Object to query AWS KMS - pub kms_client: KmsClient, - /// Public Key corresponding to Customer Managed Key - pub public_key: Decoded, - /// Signing Algorithm to be used for the Customer Managed Key - pub signing_algorithm: KmsSigningAlgorithms, -} - -impl fmt::Debug for KmsRsaKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("KmsRsaKey") - .field("key_id", &self.key_id) - .field("signing_algorithm", &self.signing_algorithm) - .field("public_key", &self.public_key) - .finish() - } -} - -impl Sign for KmsRsaKey { - fn tuf_key(&self) -> Key { - use crate::schema::key::{RsaKey, RsaScheme}; - // Create a Key struct for the public key - Key::Rsa { - keyval: RsaKey { - public: self.public_key.to_owned(), - _extra: HashMap::new(), - }, - scheme: RsaScheme::RsassaPssSha256, - _extra: HashMap::new(), - } - } - - fn sign(&self, msg: &[u8], _rng: &dyn SecureRandom) -> Result> { - let sign_fut = self.kms_client.sign(SignRequest { - key_id: self.key_id.clone(), - message: bytes::Bytes::from(msg.to_owned()), - message_type: Some(String::from("RAW")), - signing_algorithm: match self.signing_algorithm.clone() { - KmsSigningAlgorithms::Rsa(algorithm) => algorithm, - }, - ..rusoto_kms::SignRequest::default() - }); - let response = tokio::runtime::Runtime::new() - .context(error::RuntimeCreation)? - .block_on(sign_fut) - .context(error::KmsSign { - key_id: self.key_id.clone(), - })?; - let signature = response.signature.context(error::SignatureNotFound)?; - Ok(signature.to_vec()) - } -} diff --git a/tuftool/src/error.rs b/tuftool/src/error.rs index 092029bac..9b5d61d8a 100644 --- a/tuftool/src/error.rs +++ b/tuftool/src/error.rs @@ -13,11 +13,27 @@ pub(crate) type Result = std::result::Result; #[derive(Debug, Snafu)] #[snafu(visibility = "pub(crate)")] pub(crate) enum Error { - #[snafu(display("Failed write: {}", source))] - CreateKeySource { - source: Box, + #[snafu(display("Failed to run {}: {}", command_str, source))] + CommandExec { + command_str: String, + source: std::io::Error, + backtrace: Backtrace, + }, + + #[snafu(display("Command {} failed with {}", command_str, status))] + CommandStatus { + command_str: String, + status: std::process::ExitStatus, backtrace: Backtrace, }, + + #[snafu(display("Command {} output is not valid UTF-8: {}", command_str, source))] + CommandUtf8 { + command_str: String, + source: std::string::FromUtf8Error, + backtrace: Backtrace, + }, + #[snafu(display("Cannot determine current directory: {}", source))] CurrentDir { source: std::io::Error, diff --git a/tuftool/src/root.rs b/tuftool/src/root.rs index 862be69b7..ce9e06446 100644 --- a/tuftool/src/root.rs +++ b/tuftool/src/root.rs @@ -19,6 +19,7 @@ use tough::editor::signed::SignedRole; use tough::key_source::KeySource; use tough::schema::decoded::{Decoded, Hex}; use tough::schema::{key::Key, RoleKeys, RoleType, Root, Signed}; +use tough::sign::{parse_keypair, Sign}; #[derive(Debug, StructOpt)] pub(crate) enum Command { @@ -241,11 +242,34 @@ impl Command { exponent: u32, ) -> Result<()> { let mut root: Signed = load_file(path)?; + + // ring doesn't support RSA key generation yet + // https://github.com/briansmith/ring/issues/219 + let mut command = std::process::Command::new("openssl"); + command.args(&["genpkey", "-algorithm", "RSA", "-pkeyopt"]); + command.arg(format!("rsa_keygen_bits:{}", bits)); + command.arg("-pkeyopt"); + command.arg(format!("rsa_keygen_pubexp:{}", exponent)); + + let command_str = format!("{:?}", command); + let output = command.output().context(error::CommandExec { + command_str: &command_str, + })?; + ensure!( + output.status.success(), + error::CommandStatus { + command_str: &command_str, + status: output.status + } + ); + let stdout = + String::from_utf8(output.stdout).context(error::CommandUtf8 { command_str })?; + + let key_pair = parse_keypair(stdout.as_bytes()).context(error::KeyPairParse)?; + let key_id = hex::encode(add_key(&mut root.signed, roles, key_pair.tuf_key())?); key_source - .create(bits.to_owned(), exponent.to_owned()) - .context(error::CreateKeySource)?; - let key_sign = key_source.as_sign().context(error::KeyPairFromKeySource)?; - let key_id = hex::encode(add_key(&mut root.signed, roles, key_sign.tuf_key())?); + .write(&stdout, &key_id) + .context(error::WriteKeySource)?; clear_sigs(&mut root); println!("{}", key_id); write_file(path, &root) diff --git a/tuftool/src/source.rs b/tuftool/src/source.rs index 43c803eee..0736ea78e 100644 --- a/tuftool/src/source.rs +++ b/tuftool/src/source.rs @@ -56,8 +56,6 @@ pub(crate) fn parse_key_source(input: &str) -> Result> { .base_url(Some(&pwd_url)) .parse(input) .context(error::UrlParse { url: input })?; - println!("{}", url.scheme()); - println!("{:?}", PathBuf::from(url.path())); match url.scheme() { "file" => Ok(Box::new(LocalKeySource { path: PathBuf::from(url.path()), @@ -90,7 +88,7 @@ pub(crate) fn parse_key_source(input: &str) -> Result> { Some(s.to_owned()) } }), - key_id: format!("{}{}", "alias", url.path().to_owned()), + key_id: url.path().to_owned(), client: None, })), _ => error::UnrecognizedScheme { diff --git a/tuftool/tests/create_repository_integration.rs b/tuftool/tests/create_repository_integration.rs index 83522bc57..d00fc808c 100644 --- a/tuftool/tests/create_repository_integration.rs +++ b/tuftool/tests/create_repository_integration.rs @@ -12,6 +12,7 @@ use tough::{ExpirationEnforcement, Limits, Repository, Settings}; // Since the tests are run using the actual "AWS Ssm and AWS Kms", you would have to configure // AWS account profile "thar-signing-root-pdx" with root permission. // Refer https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html to configure named profile. +// Additionally, tough-kms key generation is not supported (issue #211), so you would have to manually create kms CMK key. // To run test include feature flag 'integration-tests' like : "cargo test --features=integration-tests" fn initialie_root_json(root_json: &str) { @@ -63,7 +64,20 @@ fn gen_key(key: &str, root_json: &str) { .assert() .success(); } - +fn add_root_key(key: &str, root_json: &str) { + Command::cargo_bin("tuftool") + .unwrap() + .args(&[ + "root", + "add-key", + root_json.clone(), + key.clone(), + "--role", + "root", + ]) + .assert() + .success(); +} fn add_key_all_role(key: &str, root_json: &str) { Command::cargo_bin("tuftool") .unwrap() @@ -111,12 +125,16 @@ fn sign_root_json(key: &str, root_json: &str) { .success(); } -fn create_repository(root_key: &str) { +fn create_repository(root_key: &str, auto_generate: bool) { // create a root.json file to create TUF repository metadata let root_json_dir = TempDir::new().unwrap(); let root_json = root_json_dir.path().join("root.json"); initialie_root_json(root_json.to_str().unwrap()); - gen_key(root_key.clone(), root_json.to_str().unwrap()); + if auto_generate == true { + gen_key(root_key.clone(), root_json.to_str().unwrap()); + } else { + add_root_key(root_key, root_json.to_str().unwrap()); + } add_key_all_role(root_key.clone(), root_json.to_str().unwrap()); sign_root_json(root_key.clone(), root_json.to_str().unwrap()); // Use root.json file to generate metadata using create command. @@ -228,7 +246,7 @@ fn create_repository_local_key() { let root_key_dir = TempDir::new().unwrap(); let root_key_path = root_key_dir.path().join("local_key.pem"); let root_key = &format!("file://{}", root_key_path.to_str().unwrap()); - create_repository(root_key); + create_repository(root_key, true); } #[test] @@ -236,13 +254,13 @@ fn create_repository_local_key() { // Ensure we can use ssm key to create and sign a repo created by the `tuftool` binary using the `tough` library fn create_repository_ssm_key() { let root_key = "aws-ssm://thar-signing-root-pdx/thar/root-key-new"; - create_repository(root_key); + create_repository(root_key, true); } #[test] #[cfg_attr(not(feature = "integration-tests"), ignore)] -// Ensure we can use ssm key to create and sign a repo created by the `tuftool` binary using the `tough` library +// Ensure we can use kms key to create and sign a repo created by the `tuftool` binary using the `tough` library fn create_repository_kms_key() { - let root_key = "aws-kms://thar-signing-root-pdx/test_key"; - create_repository(root_key); + let root_key = "aws-kms://thar-signing-root-pdx/alias/test_key"; + create_repository(root_key, false); }