diff --git a/edgelet/edgelet-core/src/crypto.rs b/edgelet/edgelet-core/src/crypto.rs index b00e42c378a..16809e9078c 100644 --- a/edgelet/edgelet-core/src/crypto.rs +++ b/edgelet/edgelet-core/src/crypto.rs @@ -115,6 +115,8 @@ pub trait CreateCertificate { &self, properties: &CertificateProperties, ) -> Result; + + fn destroy_certificate(&self, alias: String) -> Result<(), Error>; } pub trait Certificate { diff --git a/edgelet/edgelet-hsm/src/crypto.rs b/edgelet/edgelet-hsm/src/crypto.rs index ed4d3bdcfe0..b39a8b86940 100644 --- a/edgelet/edgelet-hsm/src/crypto.rs +++ b/edgelet/edgelet-hsm/src/crypto.rs @@ -88,6 +88,16 @@ impl CoreCreateCertificate for Crypto { .map_err(CoreError::from)?; Ok(Certificate(cert)) } + + fn destroy_certificate(&self, alias: String) -> Result<(), CoreError> { + self.crypto + .read() + .expect("Shared read lock on crypto structure failed") + .destroy_certificate(alias) + .map_err(Error::from) + .map_err(CoreError::from)?; + Ok(()) + } } impl CoreEncrypt for Crypto { diff --git a/edgelet/edgelet-hsm/tests/crypto_create_cert_input_fail.rs b/edgelet/edgelet-hsm/tests/crypto_create_cert_input_fail.rs index 9ebc7396552..918911c21f1 100644 --- a/edgelet/edgelet-hsm/tests/crypto_create_cert_input_fail.rs +++ b/edgelet/edgelet-hsm/tests/crypto_create_cert_input_fail.rs @@ -52,4 +52,8 @@ fn crypto_create_cert_input_fail() { Ok(_) => panic!("Expected an error from bad alias"), Err(_) => (), } + match crypto.destroy_certificate("unknown_cert_alias".to_string()) { + Ok(_) => (), + Err(_) => panic!("Expected no error when destroying a certificate that does not exist"), + } } diff --git a/edgelet/edgelet-hsm/tests/crypto_create_cert_success.rs b/edgelet/edgelet-hsm/tests/crypto_create_cert_success.rs index 6d613a82388..f49e36b3ef2 100644 --- a/edgelet/edgelet-hsm/tests/crypto_create_cert_success.rs +++ b/edgelet/edgelet-hsm/tests/crypto_create_cert_success.rs @@ -36,4 +36,6 @@ fn crypto_create_cert_success() { PrivateKey::Ref(_) => panic!("did not expect reference private key"), PrivateKey::Key(KeyBytes::Pem(k)) => assert!(k.as_bytes().len() > 0), } + + crypto.destroy_certificate("Alias".to_string()).unwrap(); } diff --git a/edgelet/edgelet-http-workload/src/server/cert/server.rs b/edgelet/edgelet-http-workload/src/server/cert/server.rs index 1b068b1614f..b55fd2ae43b 100644 --- a/edgelet/edgelet-http-workload/src/server/cert/server.rs +++ b/edgelet/edgelet-http-workload/src/server/cert/server.rs @@ -62,6 +62,7 @@ where .map(|expiration| (cert_req, expiration)) }) .and_then(move |(cert_req, expiration)| { + hsm.destroy_certificate(alias.clone()).map_err(Error::from)?; let props = CertificateProperties::new( ensure_range!(expiration, 0, i64::max_value()) as u64, ensure_not_empty!(cert_req.common_name().to_string()), @@ -164,6 +165,10 @@ mod tests { let callback = self.on_create.as_ref().unwrap(); callback(properties) } + + fn destroy_certificate(&self, _alias: String) -> StdResult<(), CoreError> { + Ok(()) + } } fn parse_error_response(response: Response) -> ErrorResponse { diff --git a/edgelet/hsm-rs/src/crypto.rs b/edgelet/hsm-rs/src/crypto.rs index d56b30f2b54..2689b094b3c 100644 --- a/edgelet/hsm-rs/src/crypto.rs +++ b/edgelet/hsm-rs/src/crypto.rs @@ -212,6 +212,21 @@ impl CreateCertificate for Crypto { Ok(HsmCertificate { cert_info_handle }) } } + + fn destroy_certificate(&self, alias: String) -> Result<(), Error> { + let if_fn = self.interface + .hsm_client_destroy_certificate + .ok_or(ErrorKind::NoneFn)?; + + CString::new(alias.clone()) + .ok() + .and_then(|c_alias| { + unsafe { if_fn(self.handle, c_alias.as_ptr()) }; + Some(()) + }) + .ok_or_else(|| ErrorKind::ToCStr)?; + Ok(()) + } } impl GetTrustBundle for Crypto { diff --git a/edgelet/hsm-rs/src/error.rs b/edgelet/hsm-rs/src/error.rs index 986a99f2d12..a2354c932d4 100644 --- a/edgelet/hsm-rs/src/error.rs +++ b/edgelet/hsm-rs/src/error.rs @@ -25,6 +25,8 @@ pub enum ErrorKind { CertProps, #[fail(display = "HSM API returned an invalid null response")] NullResponse, + #[fail(display = "Could not convert parameter to c string")] + ToCStr, #[fail(display = "Could not parse bytes as utf-8")] Utf8, #[fail(display = "Invalid private key type: {}", _0)] diff --git a/edgelet/hsm-rs/src/lib.rs b/edgelet/hsm-rs/src/lib.rs index 6d0f25d9a58..82606936cd1 100644 --- a/edgelet/hsm-rs/src/lib.rs +++ b/edgelet/hsm-rs/src/lib.rs @@ -57,6 +57,8 @@ pub trait CreateCertificate { &self, properties: &CertificateProperties, ) -> Result; + + fn destroy_certificate(&self, alias: String) -> Result<(), Error>; } pub trait Encrypt { diff --git a/edgelet/hsm-sys/azure-iot-hsm-c/tests/edge_hsm_crypto_int/edge_hsm_crypto_int.c b/edgelet/hsm-sys/azure-iot-hsm-c/tests/edge_hsm_crypto_int/edge_hsm_crypto_int.c index 80407c70b38..c78d927f2e1 100644 --- a/edgelet/hsm-sys/azure-iot-hsm-c/tests/edge_hsm_crypto_int/edge_hsm_crypto_int.c +++ b/edgelet/hsm-sys/azure-iot-hsm-c/tests/edge_hsm_crypto_int/edge_hsm_crypto_int.c @@ -550,6 +550,28 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) test_helper_crypto_deinit(hsm_handle); } + TEST_FUNCTION(hsm_client_mulitple_destroy_create_destroy_certificate_smoke) + { + //arrange + HSM_CLIENT_HANDLE hsm_handle = test_helper_crypto_init(); + const HSM_CLIENT_CRYPTO_INTERFACE* interface = hsm_client_crypto_interface(); + CERT_PROPS_HANDLE certificate_props = test_helper_create_ca_cert_properties(); + + // act + interface->hsm_client_destroy_certificate(hsm_handle, TEST_CA_ALIAS); + interface->hsm_client_destroy_certificate(hsm_handle, TEST_CA_ALIAS); + CERT_INFO_HANDLE result = interface->hsm_client_create_certificate(hsm_handle, certificate_props); + + // assert + ASSERT_IS_NOT_NULL_WITH_MSG(result, "Line:" TOSTRING(__LINE__)); + + // cleanup + interface->hsm_client_destroy_certificate(hsm_handle, TEST_CA_ALIAS); + certificate_info_destroy(result); + cert_properties_destroy(certificate_props); + test_helper_crypto_deinit(hsm_handle); + } + TEST_FUNCTION(hsm_client_create_server_certificate_with_larger_expiration_time_will_use_issuers_expiration) { //arrange @@ -659,7 +681,6 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) // destroy the certificate in the HSM and create a new one and test if different from prior call certificate_info_destroy(result_second); interface->hsm_client_destroy_certificate(hsm_handle, TEST_CLIENT_ALIAS); - result_second = NULL; result_second = interface->hsm_client_create_certificate(hsm_handle, certificate_props); ASSERT_IS_NOT_NULL_WITH_MSG(result_second, "Line:" TOSTRING(__LINE__)); second_certificate = certificate_info_get_certificate(result_second); @@ -883,6 +904,9 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) free(expected_trust_bundle); certificate_info_destroy(result); test_helper_crypto_deinit(hsm_handle); + test_helper_unsetenv(ENV_DEVICE_CA_PATH); + test_helper_unsetenv(ENV_DEVICE_PK_PATH); + test_helper_unsetenv(ENV_TRUSTED_CA_CERTS_PATH); } TEST_FUNCTION(hsm_client_transparent_gateway_ca_cert_create_smoke) @@ -914,6 +938,10 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) certificate_info_destroy(result); cert_properties_destroy(ca_certificate_props); test_helper_crypto_deinit(hsm_handle); + test_helper_unsetenv(ENV_DEVICE_CA_PATH); + test_helper_unsetenv(ENV_DEVICE_PK_PATH); + test_helper_unsetenv(ENV_TRUSTED_CA_CERTS_PATH); + } TEST_FUNCTION(hsm_client_transparent_gateway_server_cert_create_smoke) @@ -946,6 +974,9 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) certificate_info_destroy(result); cert_properties_destroy(certificate_props); test_helper_crypto_deinit(hsm_handle); + test_helper_unsetenv(ENV_DEVICE_CA_PATH); + test_helper_unsetenv(ENV_DEVICE_PK_PATH); + test_helper_unsetenv(ENV_TRUSTED_CA_CERTS_PATH); } TEST_FUNCTION(hsm_client_transparent_gateway_erroneous_config) @@ -1004,6 +1035,9 @@ BEGIN_TEST_SUITE(edge_hsm_crypto_int_tests) ASSERT_ARE_NOT_EQUAL_WITH_MSG(int, 0, status, "Line:" TOSTRING(__LINE__)); // cleanup + test_helper_unsetenv(ENV_DEVICE_CA_PATH); + test_helper_unsetenv(ENV_DEVICE_PK_PATH); + test_helper_unsetenv(ENV_TRUSTED_CA_CERTS_PATH); } END_TEST_SUITE(edge_hsm_crypto_int_tests)