From 0f6a48c480208473ddeafeb380cc0dfde3673798 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Thu, 8 Jul 2021 11:42:33 +0200 Subject: [PATCH 1/4] Implement AWS KMS cryptography This implements an abstracted signing crypto module that uses an Amazon Web Services Key Management Service key to sign data. It supports either KMS or local keys for verification. Fixes: #5 Signed-off-by: Patrick Uiterwijk --- Cargo.toml | 4 + src/crypto/kms.rs | 237 ++++++++++++++++++++++++++++++++++++++++++++++ src/crypto/mod.rs | 2 + src/error.rs | 15 +++ src/sign.rs | 147 +++++++++++++++++++++++++++- 5 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 src/crypto/kms.rs diff --git a/Cargo.toml b/Cargo.toml index 3e0ac98..2b42d66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ serde_bytes = { version = "0.11", features = ["std"] } serde_with = { version = "1.5", default_features = false } openssl = { version = "0.10", optional = true } tss-esapi = { version = "6.1", optional = true } +aws-sdk-kms = { version = "0.16", optional = true } +tokio = { version = "1.20", features = ["rt"], optional = true } [dependencies.serde] version = "1.0" @@ -23,8 +25,10 @@ features = ["derive"] [dev-dependencies] hex = "0.4" +tokio = { version = "1.20", features = ["macros"] } [features] default = ["key_openssl_pkey"] key_openssl_pkey = ["openssl"] key_tpm = ["tss-esapi", "openssl"] +key_kms = ["aws-sdk-kms", "tokio"] diff --git a/src/crypto/kms.rs b/src/crypto/kms.rs new file mode 100644 index 0000000..84ca645 --- /dev/null +++ b/src/crypto/kms.rs @@ -0,0 +1,237 @@ +//! KMS implementation for cryptography + +use openssl::{ + bn::BigNum, + ecdsa::EcdsaSig, + hash::MessageDigest, + pkey::{PKey, Public}, +}; +use tokio::runtime::Runtime; + +use aws_sdk_kms::{ + error::{VerifyError, VerifyErrorKind}, + model::{MessageType, SigningAlgorithmSpec}, + Blob, Client, SdkError, +}; + +use crate::{ + crypto::{ec_curve_to_parameters, SigningPrivateKey, SigningPublicKey}, + error::CoseError, + sign::SignatureAlgorithm, +}; + +/// A reference to an AWS KMS key and client +pub struct KmsKey { + client: Client, + key_id: String, + + sig_alg: SignatureAlgorithm, + + public_key: Option>, + + runtime: Runtime, +} + +impl KmsKey { + fn new_runtime() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("Error creating tokio runtime") + } + + /// Create a new KmsKey, using the specified client and key_id. + /// + /// The sig_alg needs to be valid for the specified key. + /// This version will use the KMS Verify call to verify signatures. + /// + /// AWS Permissions required on the specified key: + /// - Sign (for creating new signatures) + /// - Verify (for verifying existing signatures) + pub fn new( + client: Client, + key_id: String, + sig_alg: SignatureAlgorithm, + ) -> Result { + Ok(KmsKey { + client, + key_id, + sig_alg, + + public_key: None, + + runtime: Self::new_runtime(), + }) + } + + /// Create a new KmsKey, using the specified client and key_id. + /// + /// The sig_alg needs to be valid for the specified key. + /// This version will use local signature verification. + /// If no public key is passed in, the key will be retrieved with GetPublicKey. + /// + /// AWS Permissions required on the specified key: + /// - Sign (for creating new signatures) + /// - GetPublicKey (to get the public key if it wasn't passed in) + #[cfg(feature = "key_openssl_pkey")] + pub fn new_with_public_key( + client: Client, + key_id: String, + public_key: Option>, + ) -> Result { + let runtime = Self::new_runtime(); + let public_key = match public_key { + Some(key) => key, + None => { + // Retrieve public key from AWS + let request = client.get_public_key().key_id(key_id.clone()).send(); + + let public_key = runtime + .block_on(request) + .map_err(CoseError::AwsGetPublicKeyError)? + .public_key + .ok_or_else(|| { + CoseError::UnsupportedError("No public key returned".to_string()) + })?; + + PKey::public_key_from_der(public_key.as_ref()).map_err(CoseError::SignatureError)? + } + }; + + let curve_name = public_key + .ec_key() + .map_err(|_| CoseError::UnsupportedError("Non-EC keys are not supported".to_string()))? + .group() + .curve_name() + .ok_or_else(|| { + CoseError::UnsupportedError("Anonymous EC keys are not supported".to_string()) + })?; + let sig_alg = ec_curve_to_parameters(curve_name)?.0; + + Ok(KmsKey { + client, + key_id, + + sig_alg, + public_key: Some(public_key), + + runtime, + }) + } + + fn get_sig_alg_spec(&self) -> SigningAlgorithmSpec { + match self.sig_alg { + SignatureAlgorithm::ES256 => SigningAlgorithmSpec::EcdsaSha256, + SignatureAlgorithm::ES384 => SigningAlgorithmSpec::EcdsaSha384, + SignatureAlgorithm::ES512 => SigningAlgorithmSpec::EcdsaSha512, + } + } + + #[cfg(feature = "key_openssl_pkey")] + fn verify_with_public_key(&self, data: &[u8], signature: &[u8]) -> Result { + self.public_key.as_ref().unwrap().verify(data, signature) + } +} + +impl SigningPublicKey for KmsKey { + fn get_parameters(&self) -> Result<(SignatureAlgorithm, MessageDigest), CoseError> { + Ok((self.sig_alg, self.sig_alg.suggested_message_digest())) + } + + fn verify(&self, data: &[u8], signature: &[u8]) -> Result { + if self.public_key.is_some() { + #[cfg(feature = "key_openssl_pkey")] + return self.verify_with_public_key(data, signature); + + #[cfg(not(feature = "key_openssl_pkey"))] + panic!("Would have been impossible to get public_key set"); + } else { + // Call KMS to verify + + // Recover the R and S factors from the signature contained in the object + let (bytes_r, bytes_s) = signature.split_at(self.sig_alg.key_length()); + + let r = BigNum::from_slice(&bytes_r).map_err(CoseError::SignatureError)?; + let s = BigNum::from_slice(&bytes_s).map_err(CoseError::SignatureError)?; + + let sig = EcdsaSig::from_private_components(r, s).map_err(CoseError::SignatureError)?; + let sig = sig.to_der().map_err(CoseError::SignatureError)?; + + let request = self + .client + .verify() + .key_id(self.key_id.clone()) + .message(Blob::new(data.to_vec())) + .message_type(MessageType::Digest) + .signing_algorithm(self.get_sig_alg_spec()) + .signature(Blob::new(sig)) + .send(); + + let reply = self.runtime.block_on(request); + + match reply { + Ok(v) => Ok(v.signature_valid), + Err(SdkError::ServiceError { + err: + VerifyError { + kind: VerifyErrorKind::KmsInvalidSignatureException(_), + .. + }, + .. + }) => Ok(false), + Err(e) => Err(CoseError::AwsVerifyError(e)), + } + } + } +} + +impl SigningPrivateKey for KmsKey { + fn sign(&self, data: &[u8]) -> Result, CoseError> { + let request = self + .client + .sign() + .key_id(self.key_id.clone()) + .message(Blob::new(data.to_vec())) + .message_type(MessageType::Digest) + .signing_algorithm(self.get_sig_alg_spec()) + .send(); + + let signature = self + .runtime + .block_on(request) + .map_err(CoseError::AwsSignError)? + .signature + .ok_or_else(|| CoseError::UnsupportedError("No signature returned".to_string()))?; + + let signature = + EcdsaSig::from_der(signature.as_ref()).map_err(CoseError::SignatureError)?; + + let key_length = self.sig_alg.key_length(); + + // The spec defines the signature as: + // Signature = I2OSP(R, n) | I2OSP(S, n), where n = ceiling(key_length / 8) + // The Signer interface doesn't provide this, so this will use EcdsaSig interface instead + // and concatenate R and S. + // See https://tools.ietf.org/html/rfc8017#section-4.1 for details. + let bytes_r = signature.r().to_vec(); + let bytes_s = signature.s().to_vec(); + + // These should *never* exceed ceiling(key_length / 8) + assert!(bytes_r.len() <= key_length); + assert!(bytes_s.len() <= key_length); + + let mut signature_bytes = vec![0u8; key_length * 2]; + + // This is big-endian encoding so padding might be added at the start if the factor is + // too short. + let offset_copy = key_length - bytes_r.len(); + signature_bytes[offset_copy..offset_copy + bytes_r.len()].copy_from_slice(&bytes_r); + + // This is big-endian encoding so padding might be added at the start if the factor is + // too short. + let offset_copy = key_length - bytes_s.len() + key_length; + signature_bytes[offset_copy..offset_copy + bytes_s.len()].copy_from_slice(&bytes_s); + + Ok(signature_bytes) + } +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index e680e0c..6155bba 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -14,6 +14,8 @@ mod openssl; #[cfg(feature = "openssl")] pub use self::openssl::Openssl; +#[cfg(feature = "key_kms")] +pub mod kms; #[cfg(feature = "key_openssl_pkey")] mod openssl_pkey; #[cfg(feature = "key_tpm")] diff --git a/src/error.rs b/src/error.rs index 7b23cc1..6f4df79 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,15 @@ pub enum CoseError { /// TPM error occured #[cfg(feature = "key_tpm")] TpmError(tss_esapi::Error), + /// AWS sign error occured + #[cfg(feature = "key_kms")] + AwsSignError(aws_sdk_kms::SdkError), + /// AWS verify error occured + #[cfg(feature = "key_kms")] + AwsVerifyError(aws_sdk_kms::SdkError), + /// AWS GetPublicKey error occured + #[cfg(all(feature = "key_kms", feature = "key_openssl_pkey"))] + AwsGetPublicKeyError(aws_sdk_kms::SdkError), } impl fmt::Display for CoseError { @@ -51,6 +60,12 @@ impl fmt::Display for CoseError { CoseError::EncryptionError(e) => write!(f, "Encryption error: {}", e), #[cfg(feature = "key_tpm")] CoseError::TpmError(e) => write!(f, "TPM error: {}", e), + #[cfg(feature = "key_kms")] + CoseError::AwsSignError(e) => write!(f, "AWS sign error: {}", e), + #[cfg(feature = "key_kms")] + CoseError::AwsVerifyError(e) => write!(f, "AWS verify error: {}", e), + #[cfg(all(feature = "key_kms", feature = "key_openssl_pkey"))] + CoseError::AwsGetPublicKeyError(e) => write!(f, "AWS GetPublicKey error: {}", e), } } } diff --git a/src/sign.rs b/src/sign.rs index b5533ad..182a04b 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -460,7 +460,7 @@ mod tests { fn map_serialization() { // Empty map let map: HeaderMap = HeaderMap::new(); - assert_eq!(map_to_empty_or_serialized(&map).unwrap(), []); + assert_eq!(map_to_empty_or_serialized(&map).unwrap(), [] as [u8; 0]); // Checks that the body_protected field will be serialized correctly let map: HeaderMap = SignatureAlgorithm::ES256.into(); @@ -1288,4 +1288,149 @@ mod tests { } } } + + #[cfg(feature = "key_kms")] + mod kms { + use std::str::FromStr; + + use super::TEXT; + use crate::{crypto::kms::KmsKey, sign::*}; + + use std::env; + + #[tokio::test] + async fn cose_sign_kms() { + let config = aws_config::from_env().load().await; + let kms_client = aws_sdk_kms::Client::new(&config); + + tokio::task::spawn_blocking(|| { + let key_id = + env::var("AWS_KMS_TEST_KEY_ARN").expect("Please set AWS_KMS_TEST_KEY_ARN"); + + let sig_alg = env::var("TEST_KEY_SIG_ALG").expect("Please set TEST_KEY_SIG_ALG"); + let sig_alg = + SignatureAlgorithm::from_str(&sig_alg).expect("Invalid TEST_KEY_SIG_ALG"); + + let kms_key = + KmsKey::new(kms_client, key_id, sig_alg).expect("Error building kms_key"); + + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + let cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); + + // Tag 6.18 should be present + assert_eq!(tagged_bytes[0], 6 << 5 | 18); + let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); + + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&kms_key)).unwrap() + ); + }) + .await + .unwrap(); + } + + #[tokio::test] + async fn cose_sign_kms_invalid_signature() { + let config = aws_config::from_env().load().await; + let kms_client = aws_sdk_kms::Client::new(&config); + + tokio::task::spawn_blocking(|| { + let key_id = + env::var("AWS_KMS_TEST_KEY_ARN").expect("Please set AWS_KMS_TEST_KEY_ARN"); + + let sig_alg = env::var("TEST_KEY_SIG_ALG").expect("Please set TEST_KEY_SIG_ALG"); + let sig_alg = + SignatureAlgorithm::from_str(&sig_alg).expect("Invalid TEST_KEY_SIG_ALG"); + let kms_key = + KmsKey::new(kms_client, key_id, sig_alg).expect("Error building kms_key"); + + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + let mut cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + + // Mangle the signature + cose_doc1.signature[0] ^= 0xff; + + let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); + + match cose_doc2.get_payload(Some(&kms_key)) { + Ok(_) => panic!("Did not fail"), + Err(CoseError::UnverifiedSignature) => {} + Err(e) => { + panic!("Unexpected error: {:?}", e) + } + } + }) + .await + .unwrap(); + } + + #[cfg(feature = "key_openssl_pkey")] + #[tokio::test] + async fn cose_sign_kms_public_key() { + let config = aws_config::from_env().load().await; + let kms_client = aws_sdk_kms::Client::new(&config); + + let key_id = env::var("AWS_KMS_TEST_KEY_ARN").expect("Please set AWS_KMS_TEST_KEY_ARN"); + + tokio::task::spawn_blocking(|| { + let kms_key = KmsKey::new_with_public_key(kms_client, key_id, None) + .expect("Error building kms_key"); + + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + let cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); + + // Tag 6.18 should be present + assert_eq!(tagged_bytes[0], 6 << 5 | 18); + let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); + + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&kms_key)).unwrap() + ); + }) + .await + .unwrap(); + } + + #[cfg(feature = "key_openssl_pkey")] + #[tokio::test] + async fn cose_sign_kms_public_key_invalid_signature() { + let config = aws_config::from_env().load().await; + let kms_client = aws_sdk_kms::Client::new(&config); + + let key_id = env::var("AWS_KMS_TEST_KEY_ARN").expect("Please set AWS_KMS_TEST_KEY_ARN"); + + tokio::task::spawn_blocking(|| { + let kms_key = KmsKey::new_with_public_key(kms_client, key_id, None) + .expect("Error building kms_key"); + + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + let mut cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + + // Mangle the signature + cose_doc1.signature[0] ^= 0xff; + + let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); + + match cose_doc2.get_payload(Some(&kms_key)) { + Ok(_) => panic!("Did not fail"), + Err(CoseError::UnverifiedSignature) => {} + Err(e) => { + panic!("Unexpected error: {:?}", e) + } + } + }) + .await + .unwrap(); + } + } } From 7b9ba24a921b42cee3f9fefa4cc05a9cc0742092 Mon Sep 17 00:00:00 2001 From: Petre Eftime Date: Fri, 5 Aug 2022 15:08:53 +0000 Subject: [PATCH 2/4] rust: set MSRV to 1.58.1 This bumps MSRV to 1.58 to allow adding some new dependencies. These dependencies could be kept back to some degree but I am unsure whether this would bring value. At this point, more and more libraries require version 2021, which was introduced with rust 1.56. Signed-off-by: Petre Eftime --- .github/workflows/ci.yml | 2 +- Cargo.toml | 1 + README.md | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95f835a..be15700 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [1.46.0, stable, nightly] + rust: [1.58.1, stable, nightly] key_feature_set: - key_openssl_pkey steps: diff --git a/Cargo.toml b/Cargo.toml index 2b42d66..0694f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ keywords = ["COSE"] categories = ["cryptography"] repository = "https://github.com/awslabs/aws-nitro-enclaves-cose" description = "This library aims to provide a safe Rust implementation of COSE, with COSE Sign1 currently implemented." +rust-version = "1.58" [dependencies] serde_cbor = { version="0.11", features = ["tags"] } diff --git a/README.md b/README.md index dd8425e..ba3236d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![status]][actions] [![version]][crates.io] [![docs]][docs.rs] +[![status]][actions] [![version]][crates.io] [![docs]][docs.rs] ![msrv] [status]: https://img.shields.io/github/workflow/status/awslabs/aws-nitro-enclaves-cose/CI/master [actions]: https://github.com/awslabs/aws-nitro-enclaves-cose/actions?query=branch%3Amain @@ -6,6 +6,7 @@ [crates.io]: https://crates.io/crates/aws-nitro-enclaves-cose [docs]: https://img.shields.io/docsrs/aws-nitro-enclaves-cose [docs.rs]: https://docs.rs/aws-nitro-enclaves-cose +[msrv]: https://img.shields.io/badge/MSRV-1.58.1-blue ## COSE for AWS Nitro Enclaves From 40e7908f6f82ef99b01682f8ae7b7dd1cd6b33f5 Mon Sep 17 00:00:00 2001 From: Petre Eftime Date: Wed, 3 Aug 2022 07:36:31 +0000 Subject: [PATCH 3/4] Rebase and update SDK latest version This fixes the build issues that cropped up from integrating other patches into the repo. Signed-off-by: Petre Eftime --- Cargo.toml | 5 +++-- src/crypto/kms.rs | 29 ++++++++++++++++++----------- src/error.rs | 6 +++--- src/sign.rs | 25 ++++++++++++++----------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0694f6f..c31f2fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,9 @@ serde_bytes = { version = "0.11", features = ["std"] } serde_with = { version = "1.5", default_features = false } openssl = { version = "0.10", optional = true } tss-esapi = { version = "6.1", optional = true } +aws-config = { version = "0.46", optional = true } aws-sdk-kms = { version = "0.16", optional = true } -tokio = { version = "1.20", features = ["rt"], optional = true } +tokio = { version = "1.20", features = ["rt", "macros"], optional = true } [dependencies.serde] version = "1.0" @@ -32,4 +33,4 @@ tokio = { version = "1.20", features = ["macros"] } default = ["key_openssl_pkey"] key_openssl_pkey = ["openssl"] key_tpm = ["tss-esapi", "openssl"] -key_kms = ["aws-sdk-kms", "tokio"] +key_kms = ["aws-config", "aws-sdk-kms", "tokio", "key_openssl_pkey"] diff --git a/src/crypto/kms.rs b/src/crypto/kms.rs index 84ca645..e074f44 100644 --- a/src/crypto/kms.rs +++ b/src/crypto/kms.rs @@ -3,7 +3,6 @@ use openssl::{ bn::BigNum, ecdsa::EcdsaSig, - hash::MessageDigest, pkey::{PKey, Public}, }; use tokio::runtime::Runtime; @@ -11,13 +10,15 @@ use tokio::runtime::Runtime; use aws_sdk_kms::{ error::{VerifyError, VerifyErrorKind}, model::{MessageType, SigningAlgorithmSpec}, - Blob, Client, SdkError, + types::Blob, + types::SdkError, + Client, }; use crate::{ - crypto::{ec_curve_to_parameters, SigningPrivateKey, SigningPublicKey}, + crypto::openssl_pkey::ec_curve_to_parameters, + crypto::{MessageDigest, SignatureAlgorithm, SigningPrivateKey, SigningPublicKey}, error::CoseError, - sign::SignatureAlgorithm, }; /// A reference to an AWS KMS key and client @@ -94,7 +95,8 @@ impl KmsKey { CoseError::UnsupportedError("No public key returned".to_string()) })?; - PKey::public_key_from_der(public_key.as_ref()).map_err(CoseError::SignatureError)? + PKey::public_key_from_der(public_key.as_ref()) + .map_err(|e| CoseError::SignatureError(Box::new(e)))? } }; @@ -151,11 +153,16 @@ impl SigningPublicKey for KmsKey { // Recover the R and S factors from the signature contained in the object let (bytes_r, bytes_s) = signature.split_at(self.sig_alg.key_length()); - let r = BigNum::from_slice(&bytes_r).map_err(CoseError::SignatureError)?; - let s = BigNum::from_slice(&bytes_s).map_err(CoseError::SignatureError)?; + let r = + BigNum::from_slice(&bytes_r).map_err(|e| CoseError::SignatureError(Box::new(e)))?; + let s = + BigNum::from_slice(&bytes_s).map_err(|e| CoseError::SignatureError(Box::new(e)))?; - let sig = EcdsaSig::from_private_components(r, s).map_err(CoseError::SignatureError)?; - let sig = sig.to_der().map_err(CoseError::SignatureError)?; + let sig = EcdsaSig::from_private_components(r, s) + .map_err(|e| CoseError::SignatureError(Box::new(e)))?; + let sig = sig + .to_der() + .map_err(|e| CoseError::SignatureError(Box::new(e)))?; let request = self .client @@ -203,8 +210,8 @@ impl SigningPrivateKey for KmsKey { .signature .ok_or_else(|| CoseError::UnsupportedError("No signature returned".to_string()))?; - let signature = - EcdsaSig::from_der(signature.as_ref()).map_err(CoseError::SignatureError)?; + let signature = EcdsaSig::from_der(signature.as_ref()) + .map_err(|e| CoseError::SignatureError(Box::new(e)))?; let key_length = self.sig_alg.key_length(); diff --git a/src/error.rs b/src/error.rs index 6f4df79..b3911d9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,13 +35,13 @@ pub enum CoseError { TpmError(tss_esapi::Error), /// AWS sign error occured #[cfg(feature = "key_kms")] - AwsSignError(aws_sdk_kms::SdkError), + AwsSignError(aws_sdk_kms::types::SdkError), /// AWS verify error occured #[cfg(feature = "key_kms")] - AwsVerifyError(aws_sdk_kms::SdkError), + AwsVerifyError(aws_sdk_kms::types::SdkError), /// AWS GetPublicKey error occured #[cfg(all(feature = "key_kms", feature = "key_openssl_pkey"))] - AwsGetPublicKeyError(aws_sdk_kms::SdkError), + AwsGetPublicKeyError(aws_sdk_kms::types::SdkError), } impl fmt::Display for CoseError { diff --git a/src/sign.rs b/src/sign.rs index 182a04b..b63c87c 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -1294,7 +1294,10 @@ mod tests { use std::str::FromStr; use super::TEXT; - use crate::{crypto::kms::KmsKey, sign::*}; + use crate::{ + crypto::{kms::KmsKey, Openssl, SignatureAlgorithm}, + sign::*, + }; use std::env; @@ -1316,7 +1319,7 @@ mod tests { let mut map = HeaderMap::new(); map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - let cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let cose_doc1 = CoseSign1::new::(TEXT, &map, &kms_key).unwrap(); let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); // Tag 6.18 should be present @@ -1324,8 +1327,8 @@ mod tests { let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&kms_key)).unwrap() + cose_doc1.get_payload::(None).unwrap(), + cose_doc2.get_payload::(Some(&kms_key)).unwrap() ); }) .await @@ -1349,7 +1352,7 @@ mod tests { let mut map = HeaderMap::new(); map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - let mut cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let mut cose_doc1 = CoseSign1::new::(TEXT, &map, &kms_key).unwrap(); // Mangle the signature cose_doc1.signature[0] ^= 0xff; @@ -1357,7 +1360,7 @@ mod tests { let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); - match cose_doc2.get_payload(Some(&kms_key)) { + match cose_doc2.get_payload::(Some(&kms_key)) { Ok(_) => panic!("Did not fail"), Err(CoseError::UnverifiedSignature) => {} Err(e) => { @@ -1383,7 +1386,7 @@ mod tests { let mut map = HeaderMap::new(); map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - let cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let cose_doc1 = CoseSign1::new::(TEXT, &map, &kms_key).unwrap(); let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); // Tag 6.18 should be present @@ -1391,8 +1394,8 @@ mod tests { let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&kms_key)).unwrap() + cose_doc1.get_payload::(None).unwrap(), + cose_doc2.get_payload::(Some(&kms_key)).unwrap() ); }) .await @@ -1413,7 +1416,7 @@ mod tests { let mut map = HeaderMap::new(); map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - let mut cose_doc1 = CoseSign1::new(TEXT, &map, &kms_key).unwrap(); + let mut cose_doc1 = CoseSign1::new::(TEXT, &map, &kms_key).unwrap(); // Mangle the signature cose_doc1.signature[0] ^= 0xff; @@ -1421,7 +1424,7 @@ mod tests { let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); - match cose_doc2.get_payload(Some(&kms_key)) { + match cose_doc2.get_payload::(Some(&kms_key)) { Ok(_) => panic!("Did not fail"), Err(CoseError::UnverifiedSignature) => {} Err(e) => { From 6c9ca848ef4d720e57794bbf129474871541f11c Mon Sep 17 00:00:00 2001 From: Petre Eftime Date: Mon, 8 Aug 2022 11:35:09 +0000 Subject: [PATCH 4/4] kms: do not create a new runtime, use a handle Signed-off-by: Petre Eftime --- src/crypto/kms.rs | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/crypto/kms.rs b/src/crypto/kms.rs index e074f44..1e09c36 100644 --- a/src/crypto/kms.rs +++ b/src/crypto/kms.rs @@ -5,7 +5,6 @@ use openssl::{ ecdsa::EcdsaSig, pkey::{PKey, Public}, }; -use tokio::runtime::Runtime; use aws_sdk_kms::{ error::{VerifyError, VerifyErrorKind}, @@ -21,6 +20,8 @@ use crate::{ error::CoseError, }; +use tokio::runtime::Handle; + /// A reference to an AWS KMS key and client pub struct KmsKey { client: Client, @@ -29,18 +30,9 @@ pub struct KmsKey { sig_alg: SignatureAlgorithm, public_key: Option>, - - runtime: Runtime, } impl KmsKey { - fn new_runtime() -> Runtime { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("Error creating tokio runtime") - } - /// Create a new KmsKey, using the specified client and key_id. /// /// The sig_alg needs to be valid for the specified key. @@ -58,10 +50,7 @@ impl KmsKey { client, key_id, sig_alg, - public_key: None, - - runtime: Self::new_runtime(), }) } @@ -80,14 +69,14 @@ impl KmsKey { key_id: String, public_key: Option>, ) -> Result { - let runtime = Self::new_runtime(); + let handle = Handle::current(); let public_key = match public_key { Some(key) => key, None => { // Retrieve public key from AWS let request = client.get_public_key().key_id(key_id.clone()).send(); - let public_key = runtime + let public_key = handle .block_on(request) .map_err(CoseError::AwsGetPublicKeyError)? .public_key @@ -116,8 +105,6 @@ impl KmsKey { sig_alg, public_key: Some(public_key), - - runtime, }) } @@ -174,7 +161,8 @@ impl SigningPublicKey for KmsKey { .signature(Blob::new(sig)) .send(); - let reply = self.runtime.block_on(request); + let handle = Handle::current(); + let reply = handle.block_on(request); match reply { Ok(v) => Ok(v.signature_valid), @@ -203,8 +191,8 @@ impl SigningPrivateKey for KmsKey { .signing_algorithm(self.get_sig_alg_spec()) .send(); - let signature = self - .runtime + let handle = Handle::current(); + let signature = handle .block_on(request) .map_err(CoseError::AwsSignError)? .signature