From 9f1ddd9d71bced380c930e5ed2ae896eb7d7337f Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 29 Jan 2024 18:07:42 +0100 Subject: [PATCH 1/8] algorithms: Make errors more specific Also add unit tests for error cases Signed-off-by: Anderson Toshiyuki Sasaki --- keylime/src/algorithms.rs | 63 +++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/keylime/src/algorithms.rs b/keylime/src/algorithms.rs index c0774667..9c461954 100644 --- a/keylime/src/algorithms.rs +++ b/keylime/src/algorithms.rs @@ -15,12 +15,14 @@ use tss_esapi::{ // This error needs to be public because we implement TryFrom for public types #[derive(Error, Debug)] pub enum AlgorithmError { - #[error("{0}")] - Hash(String), - #[error("{0}")] - Encrypt(String), - #[error("{0}")] - Sign(String), + #[error("Hashing Algorithm {0} not supported")] + UnsupportedHashingAlgorithm(String), + + #[error("Encryption Algorithm {0} not supported")] + UnsupportedEncryptionAlgorithm(String), + + #[error("Signing algorithm {0} not supported")] + UnsupportedSigningAlgorithm(String), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -42,9 +44,9 @@ impl TryFrom<&str> for HashAlgorithm { "sha384" => Ok(HashAlgorithm::Sha384), "sha512" => Ok(HashAlgorithm::Sha512), "sm3_256" => Ok(HashAlgorithm::Sm3_256), - _ => Err(AlgorithmError::Hash(format!( - "Hash algorithm {value} is not supported by Keylime" - ))), + _ => { + Err(AlgorithmError::UnsupportedHashingAlgorithm(value.into())) + } } } } @@ -107,9 +109,9 @@ impl TryFrom<&str> for EncryptionAlgorithm { match value { "rsa" => Ok(EncryptionAlgorithm::Rsa), "ecc" => Ok(EncryptionAlgorithm::Ecc), - _ => Err(AlgorithmError::Encrypt(format!( - "Encryption algorithm {value} not supported by Keylime" - ))), + _ => Err(AlgorithmError::UnsupportedEncryptionAlgorithm( + value.into(), + )), } } } @@ -173,9 +175,9 @@ impl TryFrom<&str> for SignAlgorithm { "ecdsa" => Ok(SignAlgorithm::EcDsa), // "ecdaa" => Ok(SignAlgorithm::EcDaa), "ecschnorr" => Ok(SignAlgorithm::EcSchnorr), - _ => Err(AlgorithmError::Sign(format!( - "Signing algorithm {value} not supported by Keylime" - ))), + _ => { + Err(AlgorithmError::UnsupportedSigningAlgorithm(value.into())) + } } } } @@ -200,15 +202,46 @@ mod tests { fn test_hash_tryfrom() { let result = HashAlgorithm::try_from("sha1"); assert!(result.is_ok()); + let result = HashAlgorithm::try_from("sha256"); + assert!(result.is_ok()); + let result = HashAlgorithm::try_from("sha384"); + assert!(result.is_ok()); + let result = HashAlgorithm::try_from("sha512"); + assert!(result.is_ok()); + let result = HashAlgorithm::try_from("sm3_256"); + assert!(result.is_ok()); + } + #[test] + fn test_unsupported_hash_tryfrom() { + let result = HashAlgorithm::try_from("unsupported"); + assert!(result.is_err()); } #[test] fn test_encrypt_try_from() { let result = EncryptionAlgorithm::try_from("rsa"); assert!(result.is_ok()); + let result = EncryptionAlgorithm::try_from("ecc"); + assert!(result.is_ok()); + } + #[test] + fn test_unsupported_encrypt_try_from() { + let result = EncryptionAlgorithm::try_from("unsupported"); + assert!(result.is_err()); } #[test] fn test_sign_tryfrom() { let result = SignAlgorithm::try_from("rsassa"); assert!(result.is_ok()); + let result = SignAlgorithm::try_from("rsapss"); + assert!(result.is_ok()); + let result = SignAlgorithm::try_from("ecdsa"); + assert!(result.is_ok()); + let result = SignAlgorithm::try_from("ecschnorr"); + assert!(result.is_ok()); + } + #[test] + fn test_unsupported_sign_tryfrom() { + let result = SignAlgorithm::try_from("unsupported"); + assert!(result.is_err()); } } From e8d3ee46dfa68e98f096764b65d0fec302ccb9f3 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Fri, 2 Feb 2024 17:56:16 +0100 Subject: [PATCH 2/8] list_parser: Add source for error for backtrace Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent/src/error.rs | 4 ++-- keylime/src/list_parser.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/keylime-agent/src/error.rs b/keylime-agent/src/error.rs index 41392c05..8b0f481e 100644 --- a/keylime-agent/src/error.rs +++ b/keylime-agent/src/error.rs @@ -87,8 +87,8 @@ pub(crate) enum Error { Sender(String), #[error("Error receiving internal message: {0}")] Receiver(String), - #[error("List parser error: {0}")] - ListParser(#[from] keylime::list_parser::Error), + #[error("List parser error")] + ListParser(#[from] keylime::list_parser::ListParsingError), #[error("Zip error: {0}")] Zip(#[from] zip::result::ZipError), #[error("{0}")] diff --git a/keylime/src/list_parser.rs b/keylime/src/list_parser.rs index 1753ba0f..f6ded7c5 100644 --- a/keylime/src/list_parser.rs +++ b/keylime/src/list_parser.rs @@ -10,9 +10,9 @@ use thiserror::Error; pub struct ListParser; #[derive(Error, Debug)] -pub enum Error { - #[error("Parsing error: {0}")] - ParseError(Box>), +pub enum ListParsingError { + #[error("failed to parse list")] + ParseError(#[source] Box>), } fn get_inner_str(pair: Pair) -> Vec<&str> { @@ -81,9 +81,9 @@ fn get_inner_str(pair: Pair) -> Vec<&str> { /// * `[a b c]` => `["a", "b", "c"]` /// * `['a', "b", c]` => `["'a'", "\"b\"", "c"]` /// -pub fn parse_list(list: &str) -> Result, Error> { +pub fn parse_list(list: &str) -> Result, ListParsingError> { if let Some(pair) = ListParser::parse(Rule::list, list) - .map_err(|e| Error::ParseError(Box::new(e)))? + .map_err(|e| ListParsingError::ParseError(Box::new(e)))? .next() { return Ok(get_inner_str(pair)); From dc8464f1bb4a506ef79a3f880d6ec23abd30b076 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 6 Feb 2024 14:32:22 +0100 Subject: [PATCH 3/8] tpm: Rename origin of error as source in structures This takes advantage of thiserror crate to create a backtrace for the errors. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime/src/tpm.rs | 460 ++++++++++++++++++++++++++------------------- 1 file changed, 265 insertions(+), 195 deletions(-) diff --git a/keylime/src/tpm.rs b/keylime/src/tpm.rs index 66440acc..2a93f0df 100644 --- a/keylime/src/tpm.rs +++ b/keylime/src/tpm.rs @@ -114,118 +114,118 @@ pub enum TpmError { UnsupportedHashingAlgorithm { alg: HashingAlgorithm }, /// Error creating EK object - #[error("Error creating EK object: {e}")] - TSSCreateEKError { e: tss_esapi::Error }, + #[error("Error creating EK object")] + TSSCreateEKError { source: tss_esapi::Error }, /// Error creating AK object - #[error("Error creating AK object: {e}")] - TSSCreateAKError { e: tss_esapi::Error }, + #[error("Error creating AK object")] + TSSCreateAKError { source: tss_esapi::Error }, /// Error loading AK object - #[error("Error loading AK object: {e}")] - TSSLoadAKError { e: tss_esapi::Error }, + #[error("Error loading AK object")] + TSSLoadAKError { source: tss_esapi::Error }, /// Error creating new persistent TPM handle - #[error( - "Error creating handle for persistent TPM object in {handle}: {e}" - )] - TSSNewPersistentHandleError { handle: String, e: tss_esapi::Error }, + #[error("Error creating handle for persistent TPM object in {handle}")] + TSSNewPersistentHandleError { + handle: String, + source: tss_esapi::Error, + }, /// Error creating handle from persistent TPM handle - #[error( - "Error creating handle from persistent TPM handle {handle}: {e}" - )] - TSSHandleFromPersistentHandleError { handle: String, e: tss_esapi::Error }, + #[error("Error creating handle from persistent TPM handle {handle}")] + TSSHandleFromPersistentHandleError { + handle: String, + source: tss_esapi::Error, + }, /// Error returned in case of error creating new Primary Key - #[error("Error creating primary key: {e}")] - TSSCreatePrimaryError { e: tss_esapi::Error }, + #[error("Error creating primary key")] + TSSCreatePrimaryError { source: tss_esapi::Error }, /// Error building PCR Selection list - #[error("Error building PCR Selection list: {e}")] - TSSPCRSelectionBuildError { e: tss_esapi::Error }, + #[error("Error building PCR Selection list")] + TSSPCRSelectionBuildError { source: tss_esapi::Error }, /// Error obtaining digest value from an authorization policy - #[error( - "Error obtaining digest value from an authorization policy: {e}" - )] - TSSDigestFromAuthPolicyError { e: tss_esapi::Error }, + #[error("Error obtaining digest value from an authorization policy")] + TSSDigestFromAuthPolicyError { source: tss_esapi::Error }, /// Error obtaining RSA public key from IDevID - #[error("Error obtaining RSA public key from IDevID: {e}")] - TSSPublicKeyFromIDevID { e: tss_esapi::Error }, + #[error("Error obtaining RSA public key from IDevID")] + TSSPublicKeyFromIDevID { source: tss_esapi::Error }, /// Error obtaining RSA public key from IAK - #[error("Error obtaining RSA public key from IAK: {e}")] - TSSPublicKeyFromIAK { e: tss_esapi::Error }, + #[error("Error obtaining RSA public key from IAK")] + TSSPublicKeyFromIAK { source: tss_esapi::Error }, /// Error building Object Attributes - #[error("Error building Object Attributes: {e}")] - TSSObjectAttributesBuildError { e: tss_esapi::Error }, + #[error("Error building Object Attributes: {source}")] + TSSObjectAttributesBuildError { source: tss_esapi::Error }, /// Error building public RSA parameters - #[error("Error building public RSA parameters: {e}")] - TSSPublicRSAParametersBuildError { e: tss_esapi::Error }, + #[error("Error building public RSA parameters")] + TSSPublicRSAParametersBuildError { source: tss_esapi::Error }, /// Error building public ECC parameters - #[error("Error building public ECC parameters: {e}")] - TSSPublicECCParametersBuildError { e: tss_esapi::Error }, + #[error("Error building public ECC parameters")] + TSSPublicECCParametersBuildError { source: tss_esapi::Error }, /// Error building IDevID key - #[error("Error building IDevID key: {e}")] - TSSIDevIDKeyBuildError { e: tss_esapi::Error }, + #[error("Error building IDevID key")] + TSSIDevIDKeyBuildError { source: tss_esapi::Error }, /// Error building IAK key - #[error("Error building IAK key: {e}")] - TSSIAKKeyBuildError { e: tss_esapi::Error }, + #[error("Error building IAK key")] + TSSIAKKeyBuildError { source: tss_esapi::Error }, /// Error obtaining ECC parameter from IDevID - #[error("Error obtaining ECC parameter from IDevID: {e}")] - TSSECCParameterFromIDevIDError { e: tss_esapi::Error }, + #[error("Error obtaining ECC parameter from IDevID")] + TSSECCParameterFromIDevIDError { source: tss_esapi::Error }, /// Error obtaining ECC parameter from IAK - #[error("Error obtaining ECC parameter from IAK: {e}")] - TSSECCParameterFromIAKError { e: tss_esapi::Error }, + #[error("Error obtaining ECC parameter from IAK")] + TSSECCParameterFromIAKError { source: tss_esapi::Error }, /// Error returned in case of failure reading EK public information - #[error("Error reading EK public info: {e}")] - TSSReadPublicError { e: tss_esapi::Error }, + #[error("Error reading EK public info")] + TSSReadPublicError { source: tss_esapi::Error }, /// Error returned in case of failure obtaining SymmetricDefinition from Cipher - #[error("Error converting Cipher to SymmetricDefinition: {e}")] - TSSSymmetricDefinitionFromCipher { e: tss_esapi::Error }, + #[error("Error converting Cipher to SymmetricDefinition")] + TSSSymmetricDefinitionFromCipher { source: tss_esapi::Error }, /// Error returned in case of failure starting authentication session - #[error("Error starting authentication session: {e}")] - TSSStartAuthenticationSessionError { e: tss_esapi::Error }, + #[error("Error starting authentication session")] + TSSStartAuthenticationSessionError { source: tss_esapi::Error }, /// Error setting authentication session attributes - #[error("Error setting authentication session attributes: {e}")] - TSSSessionSetAttributesError { e: tss_esapi::Error }, + #[error("Error setting authentication session attributes")] + TSSSessionSetAttributesError { source: tss_esapi::Error }, /// Error converting to TSS Digest from digest value - #[error("Error converting to TSS Digest from digest value: {e}")] - TSSDigestFromValue { e: tss_esapi::Error }, + #[error("Error converting to TSS Digest from digest value")] + TSSDigestFromValue { source: tss_esapi::Error }, /// Error creating a TCTI context - #[error("Error creating TCTI context: {error:?}")] - TSSTctiContextError { error: tss_esapi::Error }, + #[error("Error creating TCTI context")] + TSSTctiContextError { source: tss_esapi::Error }, /// Error marshalling TPMS_ATTEST structure - #[error("Error marshalling TPMS_ATTEST structure: {e:?}")] - TSSMarshallAttestError { e: tss_esapi::Error }, + #[error("Error marshalling TPMS_ATTEST structure")] + TSSMarshallAttestError { source: tss_esapi::Error }, /// Error marshalling TPMT_SIGNATURE structure - #[error("Error marshalling TPMT_SIGNATURE structure: {e:?}")] - TSSMarshallSignatureError { e: tss_esapi::Error }, + #[error("Error marshalling TPMT_SIGNATURE structure")] + TSSMarshallSignatureError { source: tss_esapi::Error }, /// Error getting PCR data - #[error("Error getting PCR data from TPM: {e:?}")] - TSSPCRListError { e: tss_esapi::Error }, + #[error("Error getting PCR data from TPM")] + TSSPCRListError { source: tss_esapi::Error }, /// Error generating quote - #[error("Error generating quote: {e:?}")] - TSSQuoteError { e: tss_esapi::Error }, + #[error("Error generating quote")] + TSSQuoteError { source: tss_esapi::Error }, /// Unexpected attested type in quote #[error("Unexpected attested type in quote: expected {expected:?} got {got:?}")] @@ -249,10 +249,10 @@ pub enum TpmError { EmptyAuthenticationSessionError, /// Error parsing number from string - #[error("Number parsing error from string {origin}: {e}")] + #[error("Number parsing error from string {origin}")] NumParse { origin: String, - e: std::num::ParseIntError, + source: std::num::ParseIntError, }, /// Error converting TSS_MAGIC number from MakeCredential keyblob header to u32 @@ -296,38 +296,38 @@ pub enum TpmError { InvalidKeyblobVersion { expected: u32, got: u32 }, /// Error parsing the value in TCTI env var - #[error("Error parsing TCTI configuration from env var 'TCTI' {path}: {error:?}")] + #[error("Error parsing TCTI configuration from env var 'TCTI' {path}")] TctiNameError { path: String, - error: tss_esapi::Error, + source: tss_esapi::Error, }, /// Error getting RSA public key from PKey - #[error("Error getting RSA public key from PKey: {e}")] - OpenSSLRSAFromPKey { e: openssl::error::ErrorStack }, + #[error("Error getting RSA public key from PKey")] + OpenSSLRSAFromPKey { source: openssl::error::ErrorStack }, /// Error PEM encoding public key - #[error("Error encoding public key in PEM format: {e}")] - OpenSSLPublicKeyToPEM { e: openssl::error::ErrorStack }, + #[error("Error encoding public key in PEM format")] + OpenSSLPublicKeyToPEM { source: openssl::error::ErrorStack }, /// Error creating Hasher - #[error("Error creating Hasher : {e}")] - OpenSSLHasherNew { e: openssl::error::ErrorStack }, + #[error("Error creating Hasher")] + OpenSSLHasherNew { source: openssl::error::ErrorStack }, /// Error updating Hasher - #[error("Error updating Hasher : {e}")] - OpenSSLHasherUpdate { e: openssl::error::ErrorStack }, + #[error("Error updating Hasher")] + OpenSSLHasherUpdate { source: openssl::error::ErrorStack }, /// Error finishing Hasher - #[error("Error finishing Hasher : {e}")] - OpenSSLHasherFinish { e: openssl::error::ErrorStack }, + #[error("Error finishing Hasher")] + OpenSSLHasherFinish { source: openssl::error::ErrorStack }, /// Number conversion error - #[error("Error converting number: {0}")] + #[error("Error converting number")] TryFromInt(#[from] std::num::TryFromIntError), /// Base64 decoding error - #[error("base64 decode error: {0}")] + #[error("base64 decode error")] Base64Decode(#[from] base64::DecodeError), /// Malformed PCR selection mask @@ -339,15 +339,18 @@ pub enum TpmError { NotImplemented(String), /// Read IO error - #[error("Error reading {what}: {e}")] - IoReadError { what: String, e: std::io::Error }, + #[error("Error reading {what}")] + IoReadError { + what: String, + source: std::io::Error, + }, /// Invalid request #[error("Invalid request: {0}")] InvalidRequest(String), /// Infallible error - #[error("Infallible: {0}")] + #[error("Infallible")] Infallible(#[from] std::convert::Infallible), /// Generic catch-all TPM device error @@ -359,7 +362,7 @@ pub enum TpmError { }, /// Generic catch-all Algorithm error - #[error("AlgorithmError: {0}")] + #[error("AlgorithmError")] AlgorithmError(#[from] AlgorithmError), /// Generic catch-all error @@ -457,17 +460,28 @@ impl Context { let tcti = TctiNameConf::from_str(&tcti_path).map_err(|error| { TpmError::TctiNameError { path: tcti_path.to_string(), - error, + source: error, } })?; Ok(Self { - inner: tss_esapi::Context::new(tcti) - .map_err(|error| TpmError::TSSTctiContextError { error })?, + inner: tss_esapi::Context::new(tcti).map_err(|error| { + TpmError::TSSTctiContextError { source: error } + })?, }) } /// Creates an EK, returns the key handle and public certificate /// in `EKResult`. + /// + /// # Arguments + /// + /// `alg`: The EK algorithm + /// `handle`: Optional; if provided, the EK in the provided handle is used instead of creating + /// a new EK. + /// + /// # Returns + /// + /// An `EKResult` structure if successful, a TPMError otherwise pub fn create_ek( &mut self, alg: EncryptionAlgorithm, @@ -482,27 +496,29 @@ impl Context { alg.into(), DefaultKey, ) - .map_err(|e| TpmError::TSSCreateEKError { e })? + .map_err(|source| TpmError::TSSCreateEKError { source })? } else { let handle = u32::from_str_radix(v.trim_start_matches("0x"), 16) - .map_err(|e| TpmError::NumParse { + .map_err(|source| TpmError::NumParse { origin: v.to_string(), - e, + source, })?; self.inner .tr_from_tpm_public(TpmHandle::Persistent( PersistentTpmHandle::new(handle).map_err( - |e| TpmError::TSSNewPersistentHandleError { - handle: v.to_string(), - e, + |source| { + TpmError::TSSNewPersistentHandleError { + handle: v.to_string(), + source, + } }, )?, )) - .map_err(|e| { + .map_err(|source| { TpmError::TSSHandleFromPersistentHandleError { handle: v.to_string(), - e, + source, } })? .into() @@ -510,7 +526,7 @@ impl Context { } None => { ek::create_ek_object(&mut self.inner, alg.into(), DefaultKey) - .map_err(|e| TpmError::TSSCreateEKError { e })? + .map_err(|source| TpmError::TSSCreateEKError { source })? } }; let cert = match ek::retrieve_ek_pubcert(&mut self.inner, alg.into()) @@ -524,7 +540,7 @@ impl Context { let (tpm_pub, _, _) = self .inner .read_public(key_handle) - .map_err(|e| TpmError::TSSReadPublicError { e })?; + .map_err(|source| TpmError::TSSReadPublicError { source })?; Ok(EKResult { key_handle, ek_cert: cert, @@ -532,7 +548,15 @@ impl Context { }) } - /// Creates an AK. + /// Creates an AK + /// + /// # Arguments + /// + /// * `handle`: The associated EK handle + /// * `hash_alg`: The digest algorithm used for signing with the created AK + /// * `sign_alg`: The created AK signing algorithm + /// + /// Returns an `AKResult` structure if successful and a `TPMError` otherwise pub fn create_ak( &mut self, handle: KeyHandle, @@ -547,7 +571,7 @@ impl Context { None, DefaultKey, ) - .map_err(|e| TpmError::TSSCreateAKError { e })?; + .map_err(|source| TpmError::TSSCreateAKError { source })?; Ok(AKResult { public: ak.out_public, private: ak.out_private, @@ -555,6 +579,15 @@ impl Context { } /// Loads an existing AK associated with `handle` and `ak`. + /// + /// # Arguments + /// + /// `handle`: The associated EK handle + /// `ak`: The `AKResult` structure containing the private and public keys to load + /// + /// # Return + /// + /// The loaded AK KeyHandle if successful, a TPMError otherwise pub fn load_ak( &mut self, handle: KeyHandle, @@ -567,7 +600,7 @@ impl Context { ak.private.clone(), ak.public.clone(), ) - .map_err(|e| TpmError::TSSLoadAKError { e })?; + .map_err(|source| TpmError::TSSLoadAKError { source })?; Ok(ak_handle) } @@ -596,7 +629,9 @@ impl Context { ], ) .build() - .map_err(|e| TpmError::TSSPCRSelectionBuildError { e })?; + .map_err(|source| TpmError::TSSPCRSelectionBuildError { + source, + })?; let primary_key = self .inner @@ -610,7 +645,7 @@ impl Context { Some(pcr_selection_list), ) }) - .map_err(|e| TpmError::TSSCreatePrimaryError { e })?; + .map_err(|source| TpmError::TSSCreatePrimaryError { source })?; Ok(IDevIDResult { public: primary_key.out_public, @@ -637,9 +672,9 @@ impl Context { // restricted=0 for DevIDs .with_restricted(false); - let obj_attrs = obj_attrs_builder - .build() - .map_err(|e| TpmError::TSSObjectAttributesBuildError { e })?; + let obj_attrs = obj_attrs_builder.build().map_err(|source| { + TpmError::TSSObjectAttributesBuildError { source } + })?; let (auth_policy, key_bits, curve_id) = match name_alg { HashingAlgorithm::Sha256 => ( @@ -675,7 +710,9 @@ impl Context { .with_name_hashing_algorithm(name_alg) .with_object_attributes(obj_attrs) .with_auth_policy(Digest::try_from(auth_policy).map_err( - |e| TpmError::TSSDigestFromAuthPolicyError { e }, + |source| TpmError::TSSDigestFromAuthPolicyError { + source, + }, )?) .with_rsa_parameters( PublicRsaParametersBuilder::new() @@ -687,13 +724,15 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.restricted()) .build() - .map_err(|e| { - TpmError::TSSPublicRSAParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicRSAParametersBuildError { + source, + } })?, ) .with_rsa_unique_identifier( PublicKeyRsa::try_from(&UNIQUE_IDEVID[0..6]).map_err( - |e| TpmError::TSSPublicKeyFromIDevID { e }, + |source| TpmError::TSSPublicKeyFromIDevID { source }, )?, ), AsymmetricAlgorithm::Ecc => PublicBuilder::new() @@ -701,7 +740,9 @@ impl Context { .with_name_hashing_algorithm(name_alg) .with_object_attributes(obj_attrs) .with_auth_policy(Digest::try_from(auth_policy).map_err( - |e| TpmError::TSSDigestFromAuthPolicyError { e }, + |source| TpmError::TSSDigestFromAuthPolicyError { + source, + }, )?) .with_ecc_parameters( PublicEccParametersBuilder::new() @@ -717,16 +758,22 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.restricted()) .build() - .map_err(|e| { - TpmError::TSSPublicECCParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicECCParametersBuildError { + source, + } })?, ) .with_ecc_unique_identifier(EccPoint::new( EccParameter::try_from(&UNIQUE_IDEVID[0..6]).map_err( - |e| TpmError::TSSECCParameterFromIDevIDError { e }, + |source| TpmError::TSSECCParameterFromIDevIDError { + source, + }, )?, EccParameter::try_from(&UNIQUE_IDEVID[0..6]).map_err( - |e| TpmError::TSSECCParameterFromIDevIDError { e }, + |source| TpmError::TSSECCParameterFromIDevIDError { + source, + }, )?, )), // Defaulting to RSA on null @@ -738,8 +785,8 @@ impl Context { Digest::try_from( IDEVID_AUTH_POLICY_SHA256[0..32].to_vec(), ) - .map_err(|e| { - TpmError::TSSDigestFromAuthPolicyError { e } + .map_err(|source| { + TpmError::TSSDigestFromAuthPolicyError { source } })?, ) .with_rsa_parameters( @@ -752,21 +799,23 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.decrypt()) .build() - .map_err(|e| { - TpmError::TSSPublicRSAParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicRSAParametersBuildError { + source, + } })?, ) .with_rsa_unique_identifier( PublicKeyRsa::try_from(&UNIQUE_IDEVID[0..6]).map_err( - |e| TpmError::TSSPublicKeyFromIDevID { e }, + |source| TpmError::TSSPublicKeyFromIDevID { source }, )?, ), }; Ok(IDevIDPublic { - public: key_builder - .build() - .map_err(|e| TpmError::TSSIDevIDKeyBuildError { e })?, + public: key_builder.build().map_err(|source| { + TpmError::TSSIDevIDKeyBuildError { source } + })?, }) } @@ -795,7 +844,9 @@ impl Context { ], ) .build() - .map_err(|e| TpmError::TSSPCRSelectionBuildError { e })?; + .map_err(|source| TpmError::TSSPCRSelectionBuildError { + source, + })?; let primary_key = self .inner @@ -809,7 +860,7 @@ impl Context { Some(pcr_selection_list), ) }) - .map_err(|e| TpmError::TSSCreatePrimaryError { e })?; + .map_err(|source| TpmError::TSSCreatePrimaryError { source })?; Ok(IAKResult { public: primary_key.out_public, @@ -836,9 +887,9 @@ impl Context { // restricted=1 for AKs .with_restricted(true); - let obj_attrs = obj_attrs_builder - .build() - .map_err(|e| TpmError::TSSObjectAttributesBuildError { e })?; + let obj_attrs = obj_attrs_builder.build().map_err(|source| { + TpmError::TSSObjectAttributesBuildError { source } + })?; let (auth_policy, key_bits, curve_id) = match name_alg { HashingAlgorithm::Sha256 => ( @@ -874,7 +925,9 @@ impl Context { .with_name_hashing_algorithm(name_alg) .with_object_attributes(obj_attrs) .with_auth_policy(Digest::try_from(auth_policy).map_err( - |e| TpmError::TSSDigestFromAuthPolicyError { e }, + |source| TpmError::TSSDigestFromAuthPolicyError { + source, + }, )?) .with_rsa_parameters( PublicRsaParametersBuilder::new() @@ -888,20 +941,25 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.restricted()) .build() - .map_err(|e| { - TpmError::TSSPublicRSAParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicRSAParametersBuildError { + source, + } })?, ) .with_rsa_unique_identifier( - PublicKeyRsa::try_from(&UNIQUE_IAK[0..3]) - .map_err(|e| TpmError::TSSPublicKeyFromIAK { e })?, + PublicKeyRsa::try_from(&UNIQUE_IAK[0..3]).map_err( + |source| TpmError::TSSPublicKeyFromIAK { source }, + )?, ), AsymmetricAlgorithm::Ecc => PublicBuilder::new() .with_public_algorithm(PublicAlgorithm::Ecc) .with_name_hashing_algorithm(name_alg) .with_object_attributes(obj_attrs) .with_auth_policy(Digest::try_from(auth_policy).map_err( - |e| TpmError::TSSDigestFromAuthPolicyError { e }, + |source| TpmError::TSSDigestFromAuthPolicyError { + source, + }, )?) .with_ecc_parameters( PublicEccParametersBuilder::new() @@ -917,16 +975,22 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.restricted()) .build() - .map_err(|e| { - TpmError::TSSPublicECCParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicECCParametersBuildError { + source, + } })?, ) .with_ecc_unique_identifier(EccPoint::new( EccParameter::try_from(&UNIQUE_IAK[0..3]).map_err( - |e| TpmError::TSSECCParameterFromIAKError { e }, + |source| TpmError::TSSECCParameterFromIAKError { + source, + }, )?, EccParameter::try_from(&UNIQUE_IAK[0..3]).map_err( - |e| TpmError::TSSECCParameterFromIAKError { e }, + |source| TpmError::TSSECCParameterFromIAKError { + source, + }, )?, )), AsymmetricAlgorithm::Null => PublicBuilder::new() @@ -935,8 +999,8 @@ impl Context { .with_object_attributes(obj_attrs) .with_auth_policy( Digest::try_from(IAK_AUTH_POLICY_SHA256[0..32].to_vec()) - .map_err(|e| { - TpmError::TSSDigestFromAuthPolicyError { e } + .map_err(|source| { + TpmError::TSSDigestFromAuthPolicyError { source } })?, ) .with_rsa_parameters( @@ -949,20 +1013,23 @@ impl Context { .with_is_decryption_key(obj_attrs.decrypt()) .with_restricted(obj_attrs.decrypt()) .build() - .map_err(|e| { - TpmError::TSSPublicRSAParametersBuildError { e } + .map_err(|source| { + TpmError::TSSPublicRSAParametersBuildError { + source, + } })?, ) .with_rsa_unique_identifier( - PublicKeyRsa::try_from(&UNIQUE_IAK[0..3]) - .map_err(|e| TpmError::TSSPublicKeyFromIAK { e })?, + PublicKeyRsa::try_from(&UNIQUE_IAK[0..3]).map_err( + |source| TpmError::TSSPublicKeyFromIAK { source }, + )?, ), }; Ok(IAKPublic { public: key_builder .build() - .map_err(|e| TpmError::TSSIAKKeyBuildError { e })?, + .map_err(|source| TpmError::TSSIAKKeyBuildError { source })?, }) } @@ -978,13 +1045,13 @@ impl Context { None, None, ses_type, - Cipher::aes_128_cfb().try_into().map_err(|e| { - TpmError::TSSSymmetricDefinitionFromCipher { e } + Cipher::aes_128_cfb().try_into().map_err(|source| { + TpmError::TSSSymmetricDefinitionFromCipher { source } })?, HashingAlgorithm::Sha256, ) - .map_err(|e| TpmError::TSSStartAuthenticationSessionError { - e, + .map_err(|source| { + TpmError::TSSStartAuthenticationSessionError { source } })? else { return Err(TpmError::EmptyAuthenticationSessionError); @@ -997,7 +1064,9 @@ impl Context { self.inner .tr_sess_set_attributes(session, ses_attrs, ses_attrs_mask) - .map_err(|e| TpmError::TSSSessionSetAttributesError { e })?; + .map_err(|source| TpmError::TSSSessionSetAttributesError { + source, + })?; Ok(session) } @@ -1266,9 +1335,9 @@ fn pubkey_to_tpm_digest( let keybytes = match pubkey.id() { Id::RSA => pubkey .rsa() - .map_err(|e| TpmError::OpenSSLRSAFromPKey { e })? + .map_err(|source| TpmError::OpenSSLRSAFromPKey { source })? .public_key_to_pem() - .map_err(|e| TpmError::OpenSSLPublicKeyToPEM { e })?, + .map_err(|source| TpmError::OpenSSLPublicKeyToPEM { source })?, other_id => { return Err(TpmError::NotImplemented(format!( "Converting to digest value for key type {other_id:?}" @@ -1277,18 +1346,19 @@ fn pubkey_to_tpm_digest( }; let hashing_algo = HashingAlgorithm::from(hash_algo); - let mut hasher = Hasher::new(hash_alg_to_message_digest(hashing_algo)?) - .map_err(|e| TpmError::OpenSSLHasherNew { e })?; + let mut hasher = + Hasher::new(hash_alg_to_message_digest(hashing_algo)?) + .map_err(|source| TpmError::OpenSSLHasherNew { source })?; hasher .update(&keybytes) - .map_err(|e| TpmError::OpenSSLHasherUpdate { e })?; + .map_err(|source| TpmError::OpenSSLHasherUpdate { source })?; let hashvec = hasher .finish() - .map_err(|e| TpmError::OpenSSLHasherFinish { e })?; + .map_err(|source| TpmError::OpenSSLHasherFinish { source })?; keydigest.set( hashing_algo, Digest::try_from(hashvec.as_ref()) - .map_err(|e| TpmError::TSSDigestFromValue { e })?, + .map_err(|source| TpmError::TSSDigestFromValue { source })?, ); Ok(keydigest) @@ -1369,10 +1439,10 @@ fn encode_quote_string( // dictated by tpm2_tools. let att_vec = att .marshall() - .map_err(|e| TpmError::TSSMarshallAttestError { e })?; + .map_err(|source| TpmError::TSSMarshallAttestError { source })?; let sig_vec = sig .marshall() - .map_err(|e| TpmError::TSSMarshallSignatureError { e })?; + .map_err(|source| TpmError::TSSMarshallSignatureError { source })?; let pcr_vec = pcrdata_to_vec(pcrs_read, pcr_data); // base64 encoding @@ -1411,7 +1481,7 @@ fn make_pcr_blob( ) -> Result<(PcrSelectionList, PcrData)> { let pcr_data = context .execute_without_session(|ctx| read_all(ctx, pcrlist.clone())) - .map_err(|e| TpmError::TSSPCRListError { e })?; + .map_err(|source| TpmError::TSSPCRListError { source })?; Ok((pcrlist, pcr_data)) } @@ -1451,18 +1521,18 @@ fn check_if_pcr_data_and_attestation_match( let attested_pcr = quote_info.pcr_digest().value(); let mut hasher = Hasher::new(hash_alg_to_message_digest(hash_algo)?) - .map_err(|e| TpmError::OpenSSLHasherNew { e })?; + .map_err(|source| TpmError::OpenSSLHasherNew { source })?; for tpml_digest in pcr_data { for i in 0..tpml_digest.count { let pcr = tpml_digest.digests[i as usize]; hasher .update(&pcr.buffer[..pcr.size as usize]) - .map_err(|e| TpmError::OpenSSLHasherUpdate { e })?; + .map_err(|source| TpmError::OpenSSLHasherUpdate { source })?; } } let pcr_digest = hasher .finish() - .map_err(|e| TpmError::OpenSSLHasherFinish { e })?; + .map_err(|source| TpmError::OpenSSLHasherFinish { source })?; log::trace!( "Attested to PCR digest: {:?}, read PCR digest: {:?}", @@ -1499,7 +1569,7 @@ fn perform_quote_and_pcr_read( // create quote let (attestation, sig) = context .quote(ak_handle, nonce.clone(), sign_scheme, pcrs_read.clone()) - .map_err(|e| TpmError::TSSQuoteError { e })?; + .map_err(|source| TpmError::TSSQuoteError { source })?; // Check whether the attestation and pcr_data match if check_if_pcr_data_and_attestation_match( @@ -1613,10 +1683,10 @@ pub mod testing { let mut reader = std::io::Cursor::new(pcrsel_vec); let mut count_vec = [0u8; 4]; - reader.read_exact(&mut count_vec).map_err(|e| { + reader.read_exact(&mut count_vec).map_err(|source| { TpmError::IoReadError { what: "PCR selection count from slice".into(), - e, + source, } })?; let count = u32::from_le_bytes(count_vec); @@ -1626,29 +1696,29 @@ pub mod testing { for selection in &mut pcr_selections { let mut hash_vec = [0u8; 2]; - reader.read_exact(&mut hash_vec).map_err(|e| { + reader.read_exact(&mut hash_vec).map_err(|source| { TpmError::IoReadError { what: "PCR selection hash from slice".into(), - e, + source, } })?; selection.hash = u16::from_le_bytes(hash_vec); let mut size_vec = [0u8; 1]; - reader.read_exact(&mut size_vec).map_err(|e| { + reader.read_exact(&mut size_vec).map_err(|source| { TpmError::IoReadError { what: "PCR selection size from slice".into(), - e, + source, } })?; selection.sizeofSelect = u8::from_le_bytes(size_vec); - reader.read_exact(&mut selection.pcrSelect).map_err(|e| { - TpmError::IoReadError { + reader.read_exact(&mut selection.pcrSelect).map_err( + |source| TpmError::IoReadError { what: "PCR selection from slice".into(), - e, - } - })?; + source, + }, + )?; } Ok(TPML_PCR_SELECTION { @@ -1671,10 +1741,10 @@ pub mod testing { let mut reader = std::io::Cursor::new(digest_vec); let mut count_vec = [0u8; 4]; - reader.read_exact(&mut count_vec).map_err(|e| { + reader.read_exact(&mut count_vec).map_err(|source| { TpmError::IoReadError { what: "Digest count from slice".into(), - e, + source, } })?; let count = u32::from_le_bytes(count_vec); @@ -1683,17 +1753,17 @@ pub mod testing { for digest in &mut digests { let mut size_vec = [0u8; 2]; - reader.read_exact(&mut size_vec).map_err(|e| { + reader.read_exact(&mut size_vec).map_err(|source| { TpmError::IoReadError { what: "Digest size from slice".into(), - e, + source, } })?; digest.size = u16::from_le_bytes(size_vec); - reader.read_exact(&mut digest.buffer).map_err(|e| { + reader.read_exact(&mut digest.buffer).map_err(|source| { TpmError::IoReadError { what: "Digest from slice".into(), - e, + source, } })?; } @@ -1704,10 +1774,10 @@ pub mod testing { fn vec_to_pcrdata(val: &[u8]) -> Result<(PcrSelectionList, PcrData)> { let mut reader = std::io::Cursor::new(val); let mut pcrsel_vec = [0u8; TPML_PCR_SELECTION_SIZE]; - reader.read_exact(&mut pcrsel_vec).map_err(|e| { + reader.read_exact(&mut pcrsel_vec).map_err(|source| { TpmError::IoReadError { what: "PCR selection size from slice".into(), - e, + source, } })?; @@ -1715,10 +1785,10 @@ pub mod testing { let pcrlist: PcrSelectionList = pcrsel.try_into()?; let mut count_vec = [0u8; 4]; - reader.read_exact(&mut count_vec).map_err(|e| { + reader.read_exact(&mut count_vec).map_err(|source| { TpmError::IoReadError { what: "PCR selection count from slice".into(), - e, + source, } })?; let count = u32::from_le_bytes(count_vec); @@ -1731,10 +1801,10 @@ pub mod testing { } let mut digest_vec = [0u8; TPML_DIGEST_SIZE]; - reader.read_exact(&mut digest_vec).map_err(|e| { + reader.read_exact(&mut digest_vec).map_err(|source| { TpmError::IoReadError { what: "Digest from slice".into(), - e, + source, } })?; let digest = deserialize_digest(&digest_vec)?; @@ -1784,11 +1854,11 @@ pub mod testing { Ok((att.try_into()?, sig, pcrsel, pcrdata)) } - // This performs the same checks as in tpm2_checkquote, namely: - // signature, nonce, and PCR digests from the quote. - // - // Reference: - // https://github.com/tpm2-software/tpm2-tools/blob/master/tools/tpm2_checkquote.c + /// This performs the same checks as in tpm2_checkquote, namely: + /// signature, nonce, and PCR digests from the quote. + /// + /// Reference: + /// https://github.com/tpm2-software/tpm2-tools/blob/master/tools/tpm2_checkquote.c pub fn check_quote( context: &mut tss_esapi::Context, ak_handle: KeyHandle, @@ -1801,13 +1871,13 @@ pub mod testing { // bother unmarshalling the AK to OpenSSL PKey, but just use // Esys_VerifySignature with loaded AK let mut hasher = Hasher::new(MessageDigest::sha256()) - .map_err(|e| TpmError::OpenSSLHasherNew { e })?; + .map_err(|source| TpmError::OpenSSLHasherNew { source })?; hasher .update(att.value()) - .map_err(|e| TpmError::OpenSSLHasherUpdate { e })?; + .map_err(|source| TpmError::OpenSSLHasherUpdate { source })?; let digest = hasher .finish() - .map_err(|e| TpmError::OpenSSLHasherFinish { e })?; + .map_err(|source| TpmError::OpenSSLHasherFinish { source })?; let digest: Digest = digest.as_ref().try_into().unwrap(); //#[allow_ci] match context.verify_signature(ak_handle, digest, sig) { Ok(ticket) if ticket.tag() == StructureTag::Verified => {} @@ -1829,19 +1899,19 @@ pub mod testing { .pcr_bank(HashingAlgorithm::Sha256) .ok_or_else(|| TpmError::Other("no SHA256 bank".to_string()))?; let mut hasher = Hasher::new(MessageDigest::sha256()) - .map_err(|e| TpmError::OpenSSLHasherNew { e })?; + .map_err(|source| TpmError::OpenSSLHasherNew { source })?; for &sel in pcrsel.get_selections() { for i in &sel.selected() { if let Some(digest) = pcrbank.get_digest(*i) { - hasher - .update(digest.value()) - .map_err(|e| TpmError::OpenSSLHasherUpdate { e })?; + hasher.update(digest.value()).map_err(|source| { + TpmError::OpenSSLHasherUpdate { source } + })?; } } } let digest = hasher .finish() - .map_err(|e| TpmError::OpenSSLHasherFinish { e })?; + .map_err(|source| TpmError::OpenSSLHasherFinish { source })?; let quote_info = match attestation.attested() { AttestInfo::Quote { info } => info, _ => { From da73919a390da56266f56902b9c5d92e0f68e186 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 6 Feb 2024 21:29:49 +0100 Subject: [PATCH 4/8] crypto: Add specific type for every possible error Also move crypto-related definitions from common.rs to crypto.rs. Adjust the code to handle the new error types. Add few auxiliary functions and tests for them. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent/src/common.rs | 20 +- keylime-agent/src/crypto.rs | 934 ++++++++++++++++++++++----- keylime-agent/src/error.rs | 2 +- keylime-agent/src/keys_handler.rs | 8 +- keylime-agent/src/main.rs | 40 +- keylime-agent/src/payloads.rs | 3 +- keylime-agent/src/registrar_agent.rs | 6 +- keylime-agent/src/revocation.rs | 13 +- 8 files changed, 821 insertions(+), 205 deletions(-) diff --git a/keylime-agent/src/common.rs b/keylime-agent/src/common.rs index 684357ff..a9ac01fe 100644 --- a/keylime-agent/src/common.rs +++ b/keylime-agent/src/common.rs @@ -1,8 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2021 Keylime Authors -use crate::error::{Error, Result}; -use crate::permissions; +use crate::{ + crypto::{AES_128_KEY_LEN, AES_256_KEY_LEN}, + error::{Error, Result}, + permissions, +}; + use keylime::algorithms::{ EncryptionAlgorithm, HashAlgorithm, SignAlgorithm, }; @@ -42,9 +46,6 @@ pub static RSA_PUBLICKEY_EXPORTABLE: &str = "rsa placeholder"; pub static KEY: &str = "secret"; pub const AGENT_UUID_LEN: usize = 36; pub const AUTH_TAG_LEN: usize = 48; -pub const AES_128_KEY_LEN: usize = 16; -pub const AES_256_KEY_LEN: usize = 32; -pub const AES_BLOCK_SIZE: usize = 16; #[derive(Serialize, Deserialize, Debug)] pub(crate) struct APIVersion { @@ -253,14 +254,9 @@ impl AgentData { /// /// This is used as the agent UUID when the configuration option 'uuid' is set as 'hash_ek' pub(crate) fn hash_ek_pubkey(ek_pub: Public) -> Result { - // Converting Public TPM key to PEM - let key = SubjectPublicKeyInfo::try_from(ek_pub)?; - let key_der = picky_asn1_der::to_vec(&key)?; - let openssl_key = PKey::public_key_from_der(&key_der)?; - let pem = openssl_key.public_key_to_pem()?; - // Calculate the SHA-256 hash of the public key in PEM format - let mut hash = hash(MessageDigest::sha256(), &pem)?; + let pem = crate::crypto::tss_pubkey_to_pem(ek_pub)?; + let hash = crate::crypto::hash(&pem, MessageDigest::sha256())?; Ok(hex::encode(hash)) } diff --git a/keylime-agent/src/crypto.rs b/keylime-agent/src/crypto.rs index ba08a687..bb8c682f 100644 --- a/keylime-agent/src/crypto.rs +++ b/keylime-agent/src/crypto.rs @@ -24,51 +24,332 @@ use std::{ io::{Read, Write}, os::unix::fs::PermissionsExt, path::Path, - string::String, + string::{FromUtf8Error, String}, }; +use thiserror::Error; + +pub const AES_128_KEY_LEN: usize = 16; +pub const AES_256_KEY_LEN: usize = 32; +pub const AES_BLOCK_SIZE: usize = 16; + +#[derive(Error, Debug)] +pub enum CryptoError { + /// Error getting ASN.1 Time from days from now + #[error("failed to get ASN.1 Time from days from now")] + ASN1TimeDaysFromNowError(#[source] openssl::error::ErrorStack), + + /// Error decoding base64 + #[error("failed to decode base64")] + Base64DecodeError(#[from] base64::DecodeError), + + /// Error decrypting AES GCM encrypted data + #[error("failed to decrypt AES GCM encrypted data")] + DecryptAEADError(#[source] openssl::error::ErrorStack), + + /// Error creating RSA decrypter object + #[error("failed to create RSA decrypter object")] + DecrypterNewError(#[source] openssl::error::ErrorStack), + + /// Error setting RSA decrypter padding + #[error("failed to set RSA decrypter padding")] + DecrypterSetPaddingError(#[source] openssl::error::ErrorStack), + + /// Error setting RSA decrypter Message Digest algorithm + #[error("failed to set RSA decrypter Message Digest algorithm")] + DecrypterSetMessageDigestError(#[source] openssl::error::ErrorStack), + + /// Error setting RSA decrypter OAEP Message Digest algorithm + #[error("failed to set RSA decrypter OAEP Message Digest algorithm")] + DecrypterSetOAEPMessageDigestError(#[source] openssl::error::ErrorStack), + + /// Error getting RSA decrypter output length + #[error("failed to get RSA decrypter output length")] + DecrypterGetOutputLenError(#[source] openssl::error::ErrorStack), + + /// Error decrypting data with RSA OAEP + #[error("failed to decrypt data with RSA OAEP")] + DecryptRSAOAEPError(#[source] openssl::error::ErrorStack), + + /// Error creating file + #[error("failed to create file {file}")] + FSCreateError { + file: String, + source: std::io::Error, + }, + + /// Error calculating hash + #[error("failed to calculate hash")] + HashError(#[source] openssl::error::ErrorStack), + + /// Error checking HMAC + #[error("HMAC check failed")] + HMACError, + + /// Infallible + #[error("Infallible")] + Infallible(#[source] std::convert::Infallible), + + /// Invalid input length + #[error("Invalid input length")] + InvalidInputLength { length: usize }, + + /// Invalid key length + #[error("Invalid key length")] + InvalidKeyLength { length: usize }, + + /// Error reading file + #[error("failed to read file")] + IOReadError(#[source] std::io::Error), + + /// Error writing file + #[error("failed to write file")] + IOWriteError(#[source] std::io::Error), + + /// Error setting file permission + #[error("failed to set file permission")] + IOSetPermissionError(#[source] std::io::Error), + + /// Error deriving key from password with PBKDF2 + #[error("failed to derive key from password with PBKDF2")] + PBKDF2Error(#[source] openssl::error::ErrorStack), + + /// Error creating PKey structure from RSA structure + #[error("failed to create PKey structure from RSA structure")] + PKeyFromRSAError(#[source] openssl::error::ErrorStack), + + /// Error creating PKey structure for HMAC key + #[error("failed to create PKey structure for HMAC key")] + PKeyHMACNewError(#[source] openssl::error::ErrorStack), + + /// Error encoding PKey structure in PKCS#8 format + #[error("failed to encode PKey structure in PKCS#8 format")] + PKeyToPKCS8(#[source] openssl::error::ErrorStack), + + /// Error decoding private key from PEM + #[error("failed to decode private key from PEM")] + PrivateKeyFromPEMError(#[source] openssl::error::ErrorStack), + + /// Error decoding public key from DER + #[error("failed to decode public key from DER")] + PublicKeyFromDERError(#[source] openssl::error::ErrorStack), + + /// Error obtaining ECC public key from structure + #[error("failed to get ECC public key from structure")] + PublicKeyGetECCError(#[source] openssl::error::ErrorStack), + + /// Error encoding public key in DER format + #[error("failed to encode public key in DER format")] + PublicKeyToDERError(#[source] openssl::error::ErrorStack), + + /// Error encoding public key in PEM format + #[error("failed to encode public key in PEM format")] + PublicKeyToPEMError(#[source] openssl::error::ErrorStack), + + /// Error composing RSA public key structure from public components + #[error( + "failed to compose RSA public key structure from public components" + )] + RSAFromComponents(#[source] openssl::error::ErrorStack), + + /// Error generating RSA key pair + #[error("failed to generate RSA key pair")] + RSAGenerateError(#[source] openssl::error::ErrorStack), + + /// Error obtaining RSA private key from structure + #[error("failed to get RSA private key from structure")] + RSAGetPrivateKeyError(#[source] openssl::error::ErrorStack), + + /// Error obtaining RSA public key from structure + #[error("failed to get RSA public key from structure")] + RSAGetPublicKeyError(#[source] openssl::error::ErrorStack), + + /// Error signing data + #[error("failed to sign data")] + SignError(#[source] openssl::error::ErrorStack), + + /// Error creating Signer object + #[error("failed to create Signer object")] + SignerNewError(#[source] openssl::error::ErrorStack), + + /// Error add input data to Signer + #[error("failed to add input data to Signer")] + SignerUpdateError(#[source] openssl::error::ErrorStack), + + /// Error setting SSL server context parameters + #[error("failed to set SSL server context parameters")] + SSLContextBuilderSetAcceptorParameters( + #[source] openssl::error::ErrorStack, + ), + + /// Error getting String from UTF-8 Vec + #[error("failed to create String from UTF-8 Vec")] + StringFromVec(#[from] FromUtf8Error), + + /// Error converting ECC public key into TSS structure + #[error("failed to convert ECC public key into TSS structure")] + SubjectPublicKeyInfoFromECCError(#[source] tss_esapi::Error), + + /// Error converting RSA public key into TSS structure + #[error("failed to convert RSA public key into TSS structure")] + SubjectPublicKeyInfoFromRSAError(#[source] tss_esapi::Error), + + /// Error converting TSS Public structure into SubjectPublicKeyInfo + #[error( + "failed to convert TSS Public structure into SubjectPublicKeyInfo" + )] + SubjectPublicKeyInfoFromTSSPublicError(#[source] tss_esapi::Error), + + /// Error encoding SubjectPublicKeyInfo in DER format + #[error("failed to encode SubjectPublicKeyInfo in DER format")] + SubjectPublicKeyInfoToDERError(#[source] picky_asn1_der::Asn1DerError), + + /// Error taking object ownership + #[error("failed to take object ownership")] + ToOwnedError(#[source] openssl::error::ErrorStack), + + /// Unsupported key algorithm + #[error("unsupported key algorithm: {id}")] + UnsupportedKeyAlgorithm { id: String }, + + /// Error creating signature verifier + #[error("failed to create signature verifier")] + VerifierNewError(#[source] openssl::error::ErrorStack), + + /// Error setting signature verifier padding algorithm + #[error("failed to set signature verifier padding algorithm")] + VerifierSetPaddingError(#[source] openssl::error::ErrorStack), + + /// Error setting signature verifier Message Digest algorithm + #[error("failed to set signature verifier Message Digest algorithm")] + VerifierSetMessageDigestError(#[source] openssl::error::ErrorStack), + + /// Error setting signature verifier RSA PSS salt length + #[error("failed to set signature verifier RSA PSS salt length")] + VerifierSetRSAPSSSaltLengthError(#[source] openssl::error::ErrorStack), + + /// Error adding input data to signature verifier + #[error("failed adding input data to signature verifier")] + VerifierUpdateError(#[source] openssl::error::ErrorStack), + + /// Error verifying signature + #[error("failed verifying signature")] + VerifySignatureError(#[source] openssl::error::ErrorStack), + + /// Error creating X509 builder + #[error("failed to create X509 builder")] + X509BuilderNewError(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate issuer name in builder + #[error("failed to set X509 certificate issuer name in builder")] + X509BuilderSetIssuerName(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate Not After in builder + #[error("failed to set X509 certificate Not After in builder")] + X509BuilderSetNotAfter(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate Not Before in builder + #[error("failed to set X509 certificate Not Before in builder")] + X509BuilderSetNotBefore(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate public key in builder + #[error("failed to set X509 certificate public key in builder")] + X509BuilderSetPubKey(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate subject name in builder + #[error("failed to set X509 subject name in builder")] + X509BuilderSetSubjectName(#[source] openssl::error::ErrorStack), + + /// Error setting X509 certificate version in builder + #[error("failed to set X509 certificate version in builder")] + X509BuilderSetVersion(#[source] openssl::error::ErrorStack), + + /// Error signing X509 certificate in builder + #[error("failed to signing X509 certificate in builder")] + X509BuilderSign(#[source] openssl::error::ErrorStack), + + /// Error loading X509 certificate chain from PEM file + #[error("failed to load X509 certificate chain from PEM file")] + X509ChainFromPEMError(#[source] openssl::error::ErrorStack), + + /// Error loading X509 certificate from DER file + #[error("failed to load X509 certificate from DER file")] + X509FromDERError(#[source] openssl::error::ErrorStack), + + /// Error loading X509 certificate from PEM file + #[error("failed to load X509 certificate from PEM file")] + X509FromPEMError(#[source] openssl::error::ErrorStack), + + /// Error obtaining certificate public key + #[error("failed to get certificate public key")] + X509GetPublicError(#[source] openssl::error::ErrorStack), + + /// Error creating X509 Name object + #[error("failed to create X509 Name object")] + X509NameNewError(#[source] openssl::error::ErrorStack), + + /// Error appending entry by NID to X509 Name object + #[error("failed to append entry by NID to X509 Name object")] + X509NameAppendError(#[source] openssl::error::ErrorStack), + + /// Error adding X509 certificates to the store builder + #[error("failed to add X509 certificates to the store builder")] + X509StoreBuilderAddCertError(#[source] openssl::error::ErrorStack), + + /// Error creating X509 certificate store builder + #[error("failed to create X509 certificate store builder")] + X509StoreBuilderNewError(#[source] openssl::error::ErrorStack), -use crate::{ - Error, Result, AES_128_KEY_LEN, AES_256_KEY_LEN, AES_BLOCK_SIZE, -}; + /// Error encoding X509 certificate in DER format + #[error("failed to encode X509 certificate in DER format")] + X509ToDERError(#[source] openssl::error::ErrorStack), -// Read a X509 cert in DER format from path -pub(crate) fn load_x509_der(input_cert_path: &Path) -> Result { - let contents = std::fs::read(input_cert_path).map_err(Error::from)?; + /// Error encoding X509 certificate in PEM format + #[error("failed to encode X509 certificate in PEM format")] + X509ToPEMError(#[source] openssl::error::ErrorStack), - X509::from_der(&contents).map_err(Error::Crypto) + /// Unsupported key algorithm in X509 certificate + #[error("unsupported key algorithm in X509 certificate: {id}")] + X509UnsupportedKeyAlgorithm { id: String }, } -pub(crate) fn load_x509_pem(input_cert_path: &Path) -> Result { - let contents = std::fs::read(input_cert_path).map_err(Error::from)?; +/// Load a X509 certificate in DER format from file +pub(crate) fn load_x509_der( + input_cert_path: &Path, +) -> Result { + let contents = + std::fs::read(input_cert_path).map_err(CryptoError::IOReadError)?; - X509::from_pem(&contents).map_err(Error::Crypto) + X509::from_der(&contents).map_err(CryptoError::X509FromDERError) } -// Read a X509 cert or cert chain and outputs the first certificate -pub(crate) fn load_x509(input_cert_path: &Path) -> Result { - let mut cert_chain = load_x509_cert_chain(input_cert_path)?; - - if cert_chain.len() != 1 { - return Err(Error::Other( - "More than one public key provided in revocation cert" - .to_string(), - )); - } - let cert = cert_chain.pop().unwrap(); //#[allow_ci] +/// Load X509 certificate in PEM format from file +pub(crate) fn load_x509_pem( + input_cert_path: &Path, +) -> Result { + let contents = + std::fs::read(input_cert_path).map_err(CryptoError::IOReadError)?; - Ok(cert) + X509::from_pem(&contents).map_err(CryptoError::X509FromPEMError) } -fn load_x509_cert_chain(input_cert_path: &Path) -> Result> { - let contents = read_to_string(input_cert_path).map_err(Error::from)?; +/// Load X509 certificate chain in PEM format from file +fn load_x509_cert_chain( + input_cert_path: &Path, +) -> Result, CryptoError> { + let contents = + read_to_string(input_cert_path).map_err(CryptoError::IOReadError)?; - X509::stack_from_pem(contents.as_bytes()).map_err(Error::Crypto) + X509::stack_from_pem(contents.as_bytes()) + .map_err(CryptoError::X509ChainFromPEMError) } +/// Load X509 certificate chains in PEM format from a list of files pub(crate) fn load_x509_cert_list( input_cert_list: Vec<&Path>, -) -> Result> { +) -> Result, CryptoError> { let mut loaded = Vec::::new(); + + // This is necessary to avoid choking on failures loading certs from a file for cert in input_cert_list { match load_x509_cert_chain(cert) { Ok(mut s) => { @@ -83,77 +364,192 @@ pub(crate) fn load_x509_cert_list( } /// Write a X509 certificate to a file in PEM format -pub(crate) fn write_x509(cert: &X509, file_path: &Path) -> Result<()> { - let mut file = std::fs::File::create(file_path)?; - _ = file.write(&cert.to_pem()?)?; +pub(crate) fn write_x509( + cert: &X509, + file_path: &Path, +) -> Result<(), CryptoError> { + let mut file = std::fs::File::create(file_path).map_err(|source| { + CryptoError::FSCreateError { + file: file_path.display().to_string(), + source, + } + })?; + _ = file + .write(&cert.to_pem().map_err(CryptoError::X509ToPEMError)?) + .map_err(CryptoError::IOWriteError)?; Ok(()) } +/// Get the X509 certificate public key +pub(crate) fn x509_get_pubkey( + cert: &X509, +) -> Result, CryptoError> { + cert.public_key().map_err(CryptoError::X509GetPublicError) +} + +/// Encode the X509 certificate in PEM format +/// +/// The certificate is returned as a String +pub(crate) fn x509_to_pem(cert: &X509) -> Result { + String::from_utf8(cert.to_pem().map_err(CryptoError::X509ToPEMError)?) + .map_err(CryptoError::StringFromVec) +} + +/// Encode the X509 certificate in DER format +/// +/// The certificate is returned as a Vec +pub(crate) fn x509_to_der(cert: &X509) -> Result, CryptoError> { + cert.to_der().map_err(CryptoError::X509ToDERError) +} + +/// Encode a TSS Public key in PEM format +/// +/// The public key is returned as a Vec +pub(crate) fn tss_pubkey_to_pem( + pubkey: tss_esapi::structures::Public, +) -> Result, CryptoError> { + // Converting Public TSS key to PEM + let key = SubjectPublicKeyInfo::try_from(pubkey) + .map_err(CryptoError::SubjectPublicKeyInfoFromTSSPublicError)?; + let key_der = picky_asn1_der::to_vec(&key) + .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; + let openssl_key = PKey::public_key_from_der(&key_der) + .map_err(CryptoError::PublicKeyFromDERError)?; + let pem = openssl_key + .public_key_to_pem() + .map_err(CryptoError::PublicKeyToPEMError)?; + + Ok(pem) +} + +/// Calculate the hash of the input data using the given Message Digest algorithm +pub(crate) fn hash( + data: &[u8], + algorithm: MessageDigest, +) -> Result, CryptoError> { + Ok(openssl::hash::hash(algorithm, data) + .map_err(CryptoError::HashError)? + .to_vec()) +} + /// Check an x509 certificate contains a specific public key pub(crate) fn check_x509_key( cert: &X509, tpm_key: tss_esapi::structures::Public, -) -> Result { +) -> Result { // Id:RSA_PSS only added in rust-openssl from v0.10.59; remove this let and use Id::RSA_PSS after update // Id taken from https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/nid.h#4039 let id_rsa_pss: Id = Id::from_raw(912); - match cert.public_key()?.id() { + match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .id() + { Id::RSA => { - let cert_n = cert.public_key()?.rsa()?.n().to_vec(); + let cert_n = cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .rsa() + .map_err(CryptoError::RSAGetPublicKeyError)? + .n() + .to_vec(); let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key)?; - let key_der = picky_asn1_der::to_vec(&key)?; + let key = SubjectPublicKeyInfo::try_from(tpm_key) + .map_err(CryptoError::SubjectPublicKeyInfoFromRSAError)?; + let key_der = picky_asn1_der::to_vec(&key) + .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; let key_der_str = format!("{:?}", key_der); Ok(key_der_str.contains(&cert_n_str)) } cert_id if cert_id == id_rsa_pss => { - let cert_n = cert.public_key()?.rsa()?.n().to_vec(); + let cert_n = cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .rsa() + .map_err(CryptoError::RSAGetPublicKeyError)? + .n() + .to_vec(); let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key)?; - let key_der = picky_asn1_der::to_vec(&key)?; + let key = SubjectPublicKeyInfo::try_from(tpm_key) + .map_err(CryptoError::SubjectPublicKeyInfoFromRSAError)?; + let key_der = picky_asn1_der::to_vec(&key) + .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; let key_der_str = format!("{:?}", key_der); Ok(key_der_str.contains(&cert_n_str)) } Id::EC => { - let cert_n = cert.public_key()?.ec_key()?.public_key_to_der()?; + let cert_n = cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .ec_key() + .map_err(CryptoError::PublicKeyGetECCError)? + .public_key_to_der() + .map_err(CryptoError::PublicKeyToDERError)?; let mut cert_n_str = format!("{:?}", cert_n); _ = cert_n_str.pop(); _ = cert_n_str.remove(0); - let key = SubjectPublicKeyInfo::try_from(tpm_key)?; - let key_der = picky_asn1_der::to_vec(&key)?; + let key = SubjectPublicKeyInfo::try_from(tpm_key) + .map_err(CryptoError::SubjectPublicKeyInfoFromECCError)?; + let key_der = picky_asn1_der::to_vec(&key) + .map_err(CryptoError::SubjectPublicKeyInfoToDERError)?; let key_der_str = format!("{:?}", key_der); Ok(key_der_str.contains(&cert_n_str)) } - _ => Err(Error::Other( - "Certificate does not seem to have an RSA or EC key".to_string(), - )), + id => Err(CryptoError::X509UnsupportedKeyAlgorithm { + id: format!("{id:?}"), + }), } } /// Detect a template from a certificate /// Templates defined in: TPM 2.0 Keys for Device Identity and Attestation at https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf -pub(crate) fn match_cert_to_template(cert: &X509) -> Result { +pub(crate) fn match_cert_to_template( + cert: &X509, +) -> Result { // Id:RSA_PSS only added in rust-openssl from v0.10.59; remove this let and use Id::RSA_PSS after update // Id taken from https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/nid.h#4039 let id_rsa_pss: Id = Id::from_raw(912); - match cert.public_key()?.id() { - Id::RSA => match cert.public_key()?.bits() { + match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .id() + { + Id::RSA => match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .bits() + { 2048 => Ok("H-1".to_string()), _ => Ok("".to_string()), }, - cert_id if cert_id == id_rsa_pss => match cert.public_key()?.bits() { + cert_id if cert_id == id_rsa_pss => match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .bits() + { 2048 => Ok("H-1".to_string()), _ => Ok("".to_string()), }, - Id::EC => match cert.public_key()?.bits() { - 256 => match cert.public_key()?.ec_key()?.group().curve_name() { + Id::EC => match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .bits() + { + 256 => match cert + .public_key() + .map_err(CryptoError::X509GetPublicError)? + .ec_key() + .map_err(CryptoError::PublicKeyGetECCError)? + .group() + .curve_name() + { Some(Nid::SECP256K1) => Ok("H-2".to_string()), _ => Ok("H-5".to_string()), }, @@ -161,9 +557,9 @@ pub(crate) fn match_cert_to_template(cert: &X509) -> Result { 521 => Ok("H-4".to_string()), _ => Ok("".to_string()), }, - _ => Err(Error::Other( - "Certificate does not seem to have an RSA or EC key".to_string(), - )), + id => Err(CryptoError::X509UnsupportedKeyAlgorithm { + id: format!("{id:?}"), + }), } } @@ -171,17 +567,20 @@ pub(crate) fn match_cert_to_template(cert: &X509) -> Result { pub(crate) fn load_key_pair( key_path: &Path, key_password: Option<&str>, -) -> Result<(PKey, PKey)> { - let pem = std::fs::read(key_path)?; +) -> Result<(PKey, PKey), CryptoError> { + let pem = std::fs::read(key_path).map_err(CryptoError::IOReadError)?; let private = match key_password { Some(pw) => { if pw.is_empty() { - PKey::private_key_from_pem(&pem)? + PKey::private_key_from_pem(&pem) + .map_err(CryptoError::PrivateKeyFromPEMError)? } else { - PKey::private_key_from_pem_passphrase(&pem, pw.as_bytes())? + PKey::private_key_from_pem_passphrase(&pem, pw.as_bytes()) + .map_err(CryptoError::PrivateKeyFromPEMError)? } } - None => PKey::private_key_from_pem(&pem)?, + None => PKey::private_key_from_pem(&pem) + .map_err(CryptoError::PrivateKeyFromPEMError)?, }; let public = pkey_pub_from_priv(private.clone())?; Ok((public, private)) @@ -194,103 +593,169 @@ pub(crate) fn write_key_pair( key: &PKey, file_path: &Path, passphrase: Option<&str>, -) -> Result<()> { +) -> Result<(), CryptoError> { // Write the generated key to the file - let mut file = std::fs::File::create(file_path)?; + let mut file = std::fs::File::create(file_path).map_err(|source| { + CryptoError::FSCreateError { + file: file_path.display().to_string(), + source, + } + })?; match passphrase { Some(pw) => { if pw.is_empty() { - _ = file.write(&key.private_key_to_pem_pkcs8()?)?; + _ = file + .write( + &key.private_key_to_pem_pkcs8() + .map_err(CryptoError::PKeyToPKCS8)?, + ) + .map_err(CryptoError::IOWriteError)?; } else { - _ = file.write(&key.private_key_to_pem_pkcs8_passphrase( - openssl::symm::Cipher::aes_256_cbc(), - pw.as_bytes(), - )?)?; + _ = file + .write( + &key.private_key_to_pem_pkcs8_passphrase( + openssl::symm::Cipher::aes_256_cbc(), + pw.as_bytes(), + ) + .map_err(CryptoError::PKeyToPKCS8)?, + ) + .map_err(CryptoError::IOWriteError)?; } } None => { - _ = file.write(&key.private_key_to_pem_pkcs8()?)?; + _ = file + .write( + &key.private_key_to_pem_pkcs8() + .map_err(CryptoError::PKeyToPKCS8)?, + ) + .map_err(CryptoError::IOWriteError)?; } } - set_permissions(file_path, Permissions::from_mode(0o600))?; + set_permissions(file_path, Permissions::from_mode(0o600)) + .map_err(CryptoError::IOSetPermissionError)?; Ok(()) } -fn rsa_generate(key_size: u32) -> Result> { - PKey::from_rsa(Rsa::generate(key_size)?).map_err(Error::Crypto) +fn rsa_generate(key_size: u32) -> Result, CryptoError> { + PKey::from_rsa( + Rsa::generate(key_size).map_err(CryptoError::RSAGenerateError)?, + ) + .map_err(CryptoError::PKeyFromRSAError) } pub(crate) fn rsa_generate_pair( key_size: u32, -) -> Result<(PKey, PKey)> { +) -> Result<(PKey, PKey), CryptoError> { let private = rsa_generate(key_size)?; let public = pkey_pub_from_priv(private.clone())?; Ok((public, private)) } -fn pkey_pub_from_priv(privkey: PKey) -> Result> { +fn pkey_pub_from_priv( + privkey: PKey, +) -> Result, CryptoError> { match privkey.id() { Id::RSA => { let rsa = Rsa::from_public_components( - privkey.rsa()?.n().to_owned()?, - privkey.rsa()?.e().to_owned()?, + privkey + .rsa() + .map_err(CryptoError::RSAGetPrivateKeyError)? + .n() + .to_owned() + .map_err(CryptoError::ToOwnedError)?, + privkey + .rsa() + .map_err(CryptoError::RSAGetPrivateKeyError)? + .e() + .to_owned() + .map_err(CryptoError::ToOwnedError)?, ) - .map_err(Error::Crypto)?; - PKey::from_rsa(rsa).map_err(Error::Crypto) + .map_err(CryptoError::RSAFromComponents)?; + PKey::from_rsa(rsa).map_err(CryptoError::PKeyFromRSAError) } - id => Err(Error::Other(format!( - "pkey_pub_from_priv not yet implemented for key type {id:?}" - ))), + id => Err(CryptoError::UnsupportedKeyAlgorithm { + id: format!("{id:?}"), + }), } } -pub(crate) fn pkey_pub_to_pem(pubkey: &PKey) -> Result { +pub(crate) fn pkey_pub_to_pem( + pubkey: &PKey, +) -> Result { pubkey .public_key_to_pem() - .map_err(Error::from) - .and_then(|s| String::from_utf8(s).map_err(Error::from)) + .map_err(CryptoError::PublicKeyToPEMError) + .and_then(|s| { + String::from_utf8(s).map_err(CryptoError::StringFromVec) + }) } -pub(crate) fn generate_x509(key: &PKey, uuid: &str) -> Result { - let mut name = X509Name::builder()?; - name.append_entry_by_nid(Nid::COMMONNAME, uuid)?; +pub(crate) fn generate_x509( + key: &PKey, + uuid: &str, +) -> Result { + let mut name = + X509Name::builder().map_err(CryptoError::X509NameNewError)?; + name.append_entry_by_nid(Nid::COMMONNAME, uuid) + .map_err(CryptoError::X509NameAppendError)?; let name = name.build(); - let valid_from = Asn1Time::days_from_now(0)?; - let valid_to = Asn1Time::days_from_now(356)?; - - let mut builder = X509::builder()?; - builder.set_version(2)?; - builder.set_subject_name(&name)?; - builder.set_issuer_name(&name)?; - builder.set_not_before(&valid_from)?; - builder.set_not_after(&valid_to)?; - builder.set_pubkey(key)?; - builder.sign(key, MessageDigest::sha256())?; + let valid_from = Asn1Time::days_from_now(0) + .map_err(CryptoError::ASN1TimeDaysFromNowError)?; + let valid_to = Asn1Time::days_from_now(356) + .map_err(CryptoError::ASN1TimeDaysFromNowError)?; + + let mut builder = + X509::builder().map_err(CryptoError::X509BuilderNewError)?; + builder + .set_version(2) + .map_err(CryptoError::X509BuilderSetVersion)?; + builder + .set_subject_name(&name) + .map_err(CryptoError::X509BuilderSetSubjectName)?; + builder + .set_issuer_name(&name) + .map_err(CryptoError::X509BuilderSetIssuerName)?; + builder + .set_not_before(&valid_from) + .map_err(CryptoError::X509BuilderSetNotBefore)?; + builder + .set_not_after(&valid_to) + .map_err(CryptoError::X509BuilderSetNotAfter)?; + builder + .set_pubkey(key) + .map_err(CryptoError::X509BuilderSetPubKey)?; + builder + .sign(key, MessageDigest::sha256()) + .map_err(CryptoError::X509BuilderSign)?; Ok(builder.build()) } -pub(crate) fn generate_mtls_context( - mtls_cert: &X509, +pub(crate) fn generate_tls_context( + tls_cert: &X509, key: &PKey, - keylime_ca_certs: Vec, -) -> Result { + ca_certs: Vec, +) -> Result { let mut ssl_context_builder = - SslAcceptor::mozilla_intermediate(SslMethod::tls())?; - ssl_context_builder.set_certificate(mtls_cert); + SslAcceptor::mozilla_intermediate(SslMethod::tls()) + .map_err(CryptoError::SSLContextBuilderSetAcceptorParameters)?; + ssl_context_builder.set_certificate(tls_cert); ssl_context_builder.set_private_key(key); // Build verification cert store. - let mut mtls_store_builder = X509StoreBuilder::new()?; - for cert in keylime_ca_certs { - mtls_store_builder.add_cert(cert)?; + let mut mtls_store_builder = X509StoreBuilder::new() + .map_err(CryptoError::X509StoreBuilderNewError)?; + for cert in ca_certs { + mtls_store_builder + .add_cert(cert) + .map_err(CryptoError::X509StoreBuilderAddCertError)?; } let mtls_store = mtls_store_builder.build(); ssl_context_builder.set_verify_cert_store(mtls_store); - // Enable mTLS verification + // Enable mutual TLS verification let mut verify_mode = SslVerifyMode::empty(); verify_mode.set(SslVerifyMode::PEER, true); verify_mode.set(SslVerifyMode::FAIL_IF_NO_PEER_CERT, true); @@ -313,10 +778,10 @@ pub(crate) fn generate_mtls_context( * PBKDF2 function defaults to SHA-1 unless otherwise specified, and * Python-Keylime uses this default. */ -pub(crate) fn kdf( +pub(crate) fn pbkdf2( input_password: String, input_salt: String, -) -> Result { +) -> Result { let password = input_password.as_bytes(); let salt = input_salt.as_bytes(); let count = 2000; @@ -330,7 +795,8 @@ pub(crate) fn kdf( count, MessageDigest::sha1(), &mut key, - )?; + ) + .map_err(CryptoError::PBKDF2Error)?; Ok(hex::encode(&key[..])) } @@ -344,15 +810,28 @@ pub(crate) fn asym_verify( keypair: &PKeyRef, message: &str, signature: &str, -) -> Result { - let mut verifier = Verifier::new(MessageDigest::sha256(), keypair)?; - verifier.set_rsa_padding(Padding::PKCS1_PSS)?; - verifier.set_rsa_mgf1_md(MessageDigest::sha256())?; +) -> Result { + let mut verifier = Verifier::new(MessageDigest::sha256(), keypair) + .map_err(CryptoError::VerifierNewError)?; + verifier + .set_rsa_padding(Padding::PKCS1_PSS) + .map_err(CryptoError::VerifierSetPaddingError)?; + verifier + .set_rsa_mgf1_md(MessageDigest::sha256()) + .map_err(CryptoError::VerifierSetMessageDigestError)?; + verifier + .set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::MAXIMUM_LENGTH) + .map_err(CryptoError::VerifierSetRSAPSSSaltLengthError)?; verifier - .set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::MAXIMUM_LENGTH)?; - verifier.update(message.as_bytes())?; - Ok(verifier - .verify(&general_purpose::STANDARD.decode(signature.as_bytes())?)?) + .update(message.as_bytes()) + .map_err(CryptoError::VerifierUpdateError)?; + verifier + .verify( + &general_purpose::STANDARD + .decode(signature.as_bytes()) + .map_err(CryptoError::Base64DecodeError)?, + ) + .map_err(CryptoError::VerifySignatureError) } /* @@ -366,19 +845,30 @@ pub(crate) fn asym_verify( pub(crate) fn rsa_oaep_decrypt( priv_key: &PKey, data: &[u8], -) -> Result> { - let mut decrypter = Decrypter::new(priv_key)?; - - decrypter.set_rsa_padding(Padding::PKCS1_OAEP)?; - decrypter.set_rsa_mgf1_md(MessageDigest::sha1())?; - decrypter.set_rsa_oaep_md(MessageDigest::sha1())?; +) -> Result, CryptoError> { + let mut decrypter = + Decrypter::new(priv_key).map_err(CryptoError::DecrypterNewError)?; + + decrypter + .set_rsa_padding(Padding::PKCS1_OAEP) + .map_err(CryptoError::DecrypterSetPaddingError)?; + decrypter + .set_rsa_mgf1_md(MessageDigest::sha1()) + .map_err(CryptoError::DecrypterSetMessageDigestError)?; + decrypter + .set_rsa_oaep_md(MessageDigest::sha1()) + .map_err(CryptoError::DecrypterSetOAEPMessageDigestError)?; // Create an output buffer - let buffer_len = decrypter.decrypt_len(data)?; + let buffer_len = decrypter + .decrypt_len(data) + .map_err(CryptoError::DecrypterGetOutputLenError)?; let mut decrypted = vec![0; buffer_len]; // Decrypt and truncate the buffer - let decrypted_len = decrypter.decrypt(data, &mut decrypted)?; + let decrypted_len = decrypter + .decrypt(data, &mut decrypted) + .map_err(CryptoError::DecryptRSAOAEPError)?; decrypted.truncate(decrypted_len); Ok(decrypted) @@ -391,48 +881,59 @@ pub(crate) fn rsa_oaep_decrypt( * * Sign message and return HMAC result string */ -pub(crate) fn compute_hmac(key: &[u8], data: &[u8]) -> Result> { - let pkey = PKey::hmac(key)?; +pub(crate) fn compute_hmac( + key: &[u8], + data: &[u8], +) -> Result, CryptoError> { + let pkey = PKey::hmac(key).map_err(CryptoError::PKeyHMACNewError)?; // SHA-384 is used as the underlying hash algorithm. // // Reference: // https://keylime-docs.readthedocs.io/en/latest/rest_apis.html#post--v1.0-keys-ukey // https://github.com/keylime/keylime/blob/910b38b296038b187a020c095dc747e9c46cbef3/keylime/crypto.py#L151 - let mut signer = Signer::new(MessageDigest::sha384(), &pkey)?; - signer.update(data)?; - signer.sign_to_vec().map_err(Error::Crypto) + let mut signer = Signer::new(MessageDigest::sha384(), &pkey) + .map_err(CryptoError::SignerNewError)?; + signer + .update(data) + .map_err(CryptoError::SignerUpdateError)?; + signer.sign_to_vec().map_err(CryptoError::SignError) } pub(crate) fn verify_hmac( key: &[u8], data: &[u8], hmac: &[u8], -) -> Result<()> { - let pkey = PKey::hmac(key)?; +) -> Result<(), CryptoError> { + let pkey = PKey::hmac(key).map_err(CryptoError::PKeyHMACNewError)?; // SHA-384 is used as the underlying hash algorithm. // // Reference: // https://keylime-docs.readthedocs.io/en/latest/rest_apis.html#post--v1.0-keys-ukey // https://github.com/keylime/keylime/blob/910b38b296038b187a020c095dc747e9c46cbef3/keylime/crypto.py#L151 - let mut signer = Signer::new(MessageDigest::sha384(), &pkey)?; - signer.update(data)?; - - if !memcmp::eq(&signer.sign_to_vec()?, hmac) { - return Err(Error::Other("hmac check failed".to_string())); + let mut signer = Signer::new(MessageDigest::sha384(), &pkey) + .map_err(CryptoError::SignerNewError)?; + signer + .update(data) + .map_err(CryptoError::SignerUpdateError)?; + + if !memcmp::eq( + &signer.sign_to_vec().map_err(CryptoError::SignError)?, + hmac, + ) { + return Err(CryptoError::HMACError); } Ok(()) } -pub(crate) fn decrypt_aead(key: &[u8], data: &[u8]) -> Result> { +pub(crate) fn decrypt_aead( + key: &[u8], + data: &[u8], +) -> Result, CryptoError> { let cipher = match key.len() { AES_128_KEY_LEN => Cipher::aes_128_gcm(), AES_256_KEY_LEN => Cipher::aes_256_gcm(), - other => { - return Err(Error::Other(format!( - "key length {other} does not correspond to valid GCM cipher" - ))) - } + other => return Err(CryptoError::InvalidKeyLength { length: other }), }; // Parse out payload IV, tag, ciphertext. Note that Keylime @@ -441,14 +942,15 @@ pub(crate) fn decrypt_aead(key: &[u8], data: &[u8]) -> Result> { // // Reference: // https://github.com/keylime/keylime/blob/1663a7702b3286152b38dbcb715a9eb6705e05e9/keylime/crypto.py#L191 - if data.len() < AES_BLOCK_SIZE * 2 { - return Err(Error::InvalidRequest); + let length = data.len(); + if length < AES_BLOCK_SIZE * 2 { + return Err(CryptoError::InvalidInputLength { length }); } let (iv, rest) = data.split_at(AES_BLOCK_SIZE); let (ciphertext, tag) = rest.split_at(rest.len() - AES_BLOCK_SIZE); openssl::symm::decrypt_aead(cipher, key, Some(iv), &[], ciphertext, tag) - .map_err(Error::Crypto) + .map_err(CryptoError::DecryptAEADError) } pub mod testing { @@ -456,24 +958,45 @@ pub mod testing { use openssl::encrypt::Encrypter; use std::path::Path; + #[derive(Error, Debug)] + pub(crate) enum CryptoTestError { + /// Crypto error + #[error("CryptoError")] + CryptoError(#[from] CryptoError), + + /// IO error + #[error("IOError")] + IoError(#[from] std::io::Error), + + /// OpenSSL error + #[error("IOError")] + OpenSSLError(#[from] openssl::error::ErrorStack), + + /// Invalid IV length + #[error("Invalid IV length: expected {expected} got {got}")] + InvalidIVLen { expected: usize, got: usize }, + } + pub(crate) fn rsa_import_pair( path: impl AsRef, - ) -> Result<(PKey, PKey)> { + ) -> Result<(PKey, PKey), CryptoTestError> { let contents = read_to_string(path)?; let private = PKey::private_key_from_pem(contents.as_bytes())?; let public = pkey_pub_from_priv(private.clone())?; Ok((public, private)) } - pub(crate) fn pkey_pub_from_pem(pem: &str) -> Result> { + pub(crate) fn pkey_pub_from_pem( + pem: &str, + ) -> Result, CryptoTestError> { PKey::::public_key_from_pem(pem.as_bytes()) - .map_err(Error::Crypto) + .map_err(CryptoTestError::OpenSSLError) } pub(crate) fn rsa_oaep_encrypt( pub_key: &PKey, data: &[u8], - ) -> Result> { + ) -> Result, CryptoTestError> { let mut encrypter = Encrypter::new(pub_key)?; encrypter.set_rsa_padding(Padding::PKCS1_OAEP)?; @@ -495,22 +1018,22 @@ pub mod testing { key: &[u8], iv: &[u8], data: &[u8], - ) -> Result> { + ) -> Result, CryptoTestError> { let cipher = match key.len() { AES_128_KEY_LEN => Cipher::aes_128_gcm(), AES_256_KEY_LEN => Cipher::aes_256_gcm(), other => { - return Err(Error::Other(format!( - "key length {other} does not correspond to valid GCM cipher" - ))) + return Err( + CryptoError::InvalidKeyLength { length: other }.into() + ); } }; - if iv.len() != AES_BLOCK_SIZE { - return Err(Error::Other(format!( - "IV length {} does not correspond to valid GCM cipher {}", - iv.len(), - AES_BLOCK_SIZE - ))); + let iv_len = iv.len(); + if iv_len != AES_BLOCK_SIZE { + return Err(CryptoTestError::InvalidIVLen { + expected: AES_BLOCK_SIZE, + got: iv_len, + }); } let mut tag = vec![0u8; AES_BLOCK_SIZE]; let ciphertext = openssl::symm::encrypt_aead( @@ -520,8 +1043,8 @@ pub mod testing { &[], data, &mut tag, - ) - .map_err(Error::Crypto)?; + )?; + let mut result = Vec::with_capacity(iv.len() + ciphertext.len() + tag.len()); result.extend(iv); @@ -530,8 +1053,27 @@ pub mod testing { Ok(result) } - pub(crate) fn rsa_generate(key_size: u32) -> Result> { - super::rsa_generate(key_size) + pub(crate) fn rsa_generate( + key_size: u32, + ) -> Result, CryptoTestError> { + super::rsa_generate(key_size).map_err(CryptoTestError::CryptoError) + } + + pub(crate) fn write_x509_der( + cert: &X509, + file_path: &Path, + ) -> Result<(), CryptoTestError> { + let mut file = + std::fs::File::create(file_path).map_err(|source| { + CryptoError::FSCreateError { + file: file_path.display().to_string(), + source, + } + })?; + _ = file + .write(&cert.to_der().map_err(CryptoError::X509ToDERError)?) + .map_err(CryptoError::IOWriteError)?; + Ok(()) } } @@ -566,7 +1108,7 @@ mod tests { fn test_kdf() { let password = String::from("myverysecretsecret"); let salt = String::from("thesaltiestsalt"); - let key = kdf(password, salt); + let key = pbkdf2(password, salt); assert_eq!( "8a6de415abb8b27de5c572c8137bd14e5658395f9a2346e0b1ad8b9d8b9028af" .to_string(), @@ -689,7 +1231,11 @@ mod tests { let key = b"0123456789012345"; let ciphertext = hex::decode("41424344").unwrap(); //#[allow_ci] let result = decrypt_aead(&key[..], &ciphertext[..]); - assert!(matches!(result, Err(Error::InvalidRequest))); + let length = ciphertext.len(); + assert!(matches!( + result, + Err(CryptoError::InvalidInputLength { length }) + )); } #[test] @@ -768,6 +1314,25 @@ mod tests { } } + #[test] + fn test_hash() { + let input = "hello world!".as_bytes(); + let h = hash(input, MessageDigest::sha256()); + assert!(h.is_ok()); + let hex = hex::encode(h.unwrap()); //#[allow_ci] + assert_eq!(hex, "7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9"); + + let h = hash(input, MessageDigest::sha384()); + assert!(h.is_ok()); + let hex = hex::encode(h.unwrap()); //#[allow_ci] + assert_eq!(hex, "d33d40f7010ce34aa86efd353630309ed5c3d7ffac66d988825cf699f4803ccdf3f033230612f0945332fb580d8af805"); + + let h = hash(input, MessageDigest::sha512()); + assert!(h.is_ok()); + let hex = hex::encode(h.unwrap()); //#[allow_ci] + assert_eq!(hex, "db9b1cd3262dee37756a09b9064973589847caa8e53d31a9d142ea2701b1b28abd97838bb9a27068ba305dc8d04a45a1fcf079de54d607666996b3cc54f6b67c"); + } + #[test] fn test_x509() { let tempdir = tempfile::tempdir().unwrap(); //#[allow_ci] @@ -790,9 +1355,11 @@ mod tests { assert!(r.is_ok()); assert!(cert_b_path.exists()); - let loaded_a = load_x509(&cert_a_path); - assert!(loaded_a.is_ok()); - let loaded_a = loaded_a.unwrap(); //#[allow_ci] + let loaded_chain = load_x509_cert_chain(&cert_a_path); + assert!(loaded_chain.is_ok()); + let mut loaded_chain = loaded_chain.unwrap(); //#[allow_ci] + assert_eq!(loaded_chain.len(), 1); + let loaded_a = loaded_chain.pop().unwrap(); //#[allow_ci] let a_str = read_to_string(&cert_a_path).unwrap(); //#[allow_ci] let b_str = read_to_string(&cert_b_path).unwrap(); //#[allow_ci] @@ -800,11 +1367,32 @@ mod tests { let concat_path = tempdir.path().join("concat.pem"); fs::write(&concat_path, concat).unwrap(); //#[allow_ci] - // Expect error as there are more than one certificate - let r = load_x509(&concat_path); - assert!(r.is_err()); + // Load a single certificate from a file with multiple certificates + let r = load_x509_pem(&concat_path); + assert!(r.is_ok()); + + let cert = r.unwrap(); //#[allow_ci] + + // Test getting public key from cert + let r = x509_get_pubkey(&cert); + assert!(r.is_ok()); + + // Test converting certificate to DER + let r = x509_to_der(&cert); + assert!(r.is_ok()); + + // Test converting certificate to PEM + let r = x509_to_pem(&cert); + assert!(r.is_ok()); + + // Test loading DER certificate + let der_path = tempdir.path().join("cert.der"); + let r = testing::write_x509_der(&cert, &der_path); + assert!(r.is_ok()); + let e = load_x509_der(&der_path); + assert!(r.is_ok()); - // Loading multiple certs should work when loading chain + // Loading multiple PEM certs should work when loading chain let r = load_x509_cert_chain(&concat_path); assert!(r.is_ok()); let chain = r.unwrap(); //#[allow_ci] @@ -820,7 +1408,7 @@ mod tests { let loaded_list = r.unwrap(); //#[allow_ci] assert!(loaded_list.len() == 2); - let r = generate_mtls_context(&loaded_a, &privkey, loaded_list); + let r = generate_tls_context(&loaded_a, &privkey, loaded_list); assert!(r.is_ok()); } } diff --git a/keylime-agent/src/error.rs b/keylime-agent/src/error.rs index 8b0f481e..a5978c0b 100644 --- a/keylime-agent/src/error.rs +++ b/keylime-agent/src/error.rs @@ -59,7 +59,7 @@ pub(crate) enum Error { #[error("Number parsing error: {0}")] NumParse(#[from] std::num::ParseIntError), #[error("Crypto error: {0}")] - Crypto(#[from] openssl::error::ErrorStack), + Crypto(#[from] crate::crypto::CryptoError), #[cfg(feature = "with-zmq")] #[error("ZMQ error: {0}")] Zmq(#[from] zmq::Error), diff --git a/keylime-agent/src/keys_handler.rs b/keylime-agent/src/keys_handler.rs index 929133cb..6f4489d3 100644 --- a/keylime-agent/src/keys_handler.rs +++ b/keylime-agent/src/keys_handler.rs @@ -4,8 +4,8 @@ use crate::crypto; use crate::{ common::{ - AuthTag, EncryptedData, JsonWrapper, KeySet, SymmKey, AES_BLOCK_SIZE, - AGENT_UUID_LEN, AUTH_TAG_LEN, + AuthTag, EncryptedData, JsonWrapper, KeySet, SymmKey, AGENT_UUID_LEN, + AUTH_TAG_LEN, }, config::KeylimeConfig, payloads::{Payload, PayloadMessage}, @@ -552,9 +552,9 @@ mod tests { encrypt_aead, pkey_pub_from_pem, rsa_oaep_encrypt, }; use crate::{ - common::{AES_128_KEY_LEN, AES_256_KEY_LEN, API_VERSION}, + common::API_VERSION, config::KeylimeConfig, - crypto::compute_hmac, + crypto::{compute_hmac, AES_128_KEY_LEN, AES_256_KEY_LEN}, payloads, }; use actix_rt::Arbiter; diff --git a/keylime-agent/src/main.rs b/keylime-agent/src/main.rs index d752dfba..920b4cce 100644 --- a/keylime-agent/src/main.rs +++ b/keylime-agent/src/main.rs @@ -599,7 +599,7 @@ async fn main() -> Result<()> { "Loading existing mTLS certificate from {}", cert_path.display() ); - crypto::load_x509(cert_path)? + crypto::load_x509_pem(cert_path)? } else { debug!("Generating new mTLS certificate"); let cert = crypto::generate_x509(&nk_priv, &agent_uuid)?; @@ -641,7 +641,7 @@ async fn main() -> Result<()> { }?; mtls_cert = Some(&cert); - ssl_context = Some(crypto::generate_mtls_context( + ssl_context = Some(crypto::generate_tls_context( &cert, &nk_priv, keylime_ca_certs, @@ -1024,10 +1024,42 @@ fn read_in_file(path: String) -> std::io::Result { #[cfg(feature = "testing")] mod testing { use super::*; - use crate::config::KeylimeConfig; + use crate::{config::KeylimeConfig, crypto::CryptoError}; + use thiserror::Error; + + #[derive(Error, Debug)] + pub(crate) enum MainTestError { + /// Algorithm error + #[error("AlgorithmError")] + Error(#[from] keylime::algorithms::AlgorithmError), + + /// Crypto error + #[error("CryptoError")] + CryptoError(#[from] CryptoError), + + /// CryptoTest error + #[error("CryptoTestError")] + CryptoTestError(#[from] crate::crypto::testing::CryptoTestError), + + /// IO error + #[error("IOError")] + IoError(#[from] std::io::Error), + + /// OpenSSL error + #[error("IOError")] + OpenSSLError(#[from] openssl::error::ErrorStack), + + /// TPM error + #[error("TPMError")] + TPMError(#[from] keylime::tpm::TpmError), + + /// TSS esapi error + #[error("TSSError")] + TSSError(#[from] tss_esapi::Error), + } impl QuoteData { - pub(crate) fn fixture() -> Result { + pub(crate) fn fixture() -> std::result::Result { let test_config = KeylimeConfig::default(); let mut ctx = tpm::Context::new()?; diff --git a/keylime-agent/src/payloads.rs b/keylime-agent/src/payloads.rs index cf9789c2..07b872ea 100644 --- a/keylime-agent/src/payloads.rs +++ b/keylime-agent/src/payloads.rs @@ -322,8 +322,9 @@ mod tests { encrypt_aead, pkey_pub_from_pem, rsa_oaep_encrypt, }; use crate::{ - common::{AES_128_KEY_LEN, AES_256_KEY_LEN, API_VERSION}, + common::API_VERSION, config::KeylimeConfig, + crypto::{AES_128_KEY_LEN, AES_256_KEY_LEN}, payloads, }; use actix_rt::Arbiter; diff --git a/keylime-agent/src/registrar_agent.rs b/keylime-agent/src/registrar_agent.rs index f086f71a..be205d51 100644 --- a/keylime-agent/src/registrar_agent.rs +++ b/keylime-agent/src/registrar_agent.rs @@ -129,17 +129,17 @@ pub(crate) async fn do_register_agent( port: u32, ) -> crate::error::Result> { let mtls_cert = match mtls_cert_x509 { - Some(cert) => Some(String::from_utf8(cert.to_pem()?)?), + Some(cert) => Some(crate::crypto::x509_to_pem(cert)?), None => Some("disabled".to_string()), }; let idevid_cert = match idevid_cert_x509 { - Some(cert) => Some(cert.to_der()?), + Some(cert) => Some(crate::crypto::x509_to_der(&cert)?), None => None, }; let iak_cert = match iak_cert_x509 { - Some(cert) => Some(cert.to_der()?), + Some(cert) => Some(crate::crypto::x509_to_der(&cert)?), None => None, }; diff --git a/keylime-agent/src/revocation.rs b/keylime-agent/src/revocation.rs index 025a9299..e5ffaade 100644 --- a/keylime-agent/src/revocation.rs +++ b/keylime-agent/src/revocation.rs @@ -258,7 +258,7 @@ fn process_revocation( work_dir: &Path, mount: &Path, ) -> Result<()> { - let cert_key = revocation_cert.public_key()?; + let cert_key = crypto::x509_get_pubkey(revocation_cert)?; // Verify the message and signature with our key let mut verified = crypto::asym_verify( @@ -498,11 +498,10 @@ pub(crate) async fn worker( cert_absolute_path.display() ); - revocation_cert = match crypto::load_x509(&cert_absolute_path) - { - Ok(cert) => Some(cert), - Err(e) => None, - }; + // If successful, use the certificate, otherwise ignore the error and continue + // without the certificate + revocation_cert = + crypto::load_x509_pem(&cert_absolute_path).ok(); } RevocationMessage::Shutdown => { revocation_rx.close(); @@ -772,7 +771,7 @@ mod tests { let cert_path = Path::new(env!("CARGO_MANIFEST_DIR")) .join("test-data/test-cert.pem"); - let cert = crypto::load_x509(&cert_path).unwrap(); //#[allow_ci] + let cert = crypto::load_x509_pem(&cert_path).unwrap(); //#[allow_ci] let actions_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/actions"); From 1562b4cbff833cfd1c3a069fe825869d74efdfee Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Wed, 7 Feb 2024 17:43:32 +0100 Subject: [PATCH 5/8] crypto: Move to keylime library Move the crypto module from keylime-agent to the keylime library. Signed-off-by: Anderson Toshiyuki Sasaki --- Cargo.lock | 4 +- keylime-agent/Cargo.toml | 2 - keylime-agent/src/common.rs | 17 +-- keylime-agent/src/error.rs | 4 +- keylime-agent/src/main.rs | 3 +- keylime-agent/src/quotes_handler.rs | 3 +- keylime/Cargo.toml | 2 + {keylime-agent => keylime}/src/crypto.rs | 134 +++++++++--------- keylime/src/lib.rs | 1 + keylime/test-data/test-rsa.pem | 27 ++++ .../test-data/test-rsa.sig | 0 11 files changed, 108 insertions(+), 89 deletions(-) rename {keylime-agent => keylime}/src/crypto.rs (94%) create mode 100644 keylime/test-data/test-rsa.pem rename {keylime-agent => keylime}/test-data/test-rsa.sig (100%) diff --git a/Cargo.lock b/Cargo.lock index 76e95d3d..951460a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,6 +1149,8 @@ dependencies = [ "openssl", "pest", "pest_derive", + "picky-asn1-der", + "picky-asn1-x509", "serde", "serde_derive", "static_assertions", @@ -1174,8 +1176,6 @@ dependencies = [ "libc", "log", "openssl", - "picky-asn1-der", - "picky-asn1-x509", "pretty_env_logger", "reqwest", "serde", diff --git a/keylime-agent/Cargo.toml b/keylime-agent/Cargo.toml index ee067b89..512491c3 100644 --- a/keylime-agent/Cargo.toml +++ b/keylime-agent/Cargo.toml @@ -20,8 +20,6 @@ keylime.workspace = true libc.workspace = true log.workspace = true openssl.workspace = true -picky-asn1-der.workspace = true -picky-asn1-x509.workspace = true pretty_env_logger.workspace = true reqwest.workspace = true serde.workspace = true diff --git a/keylime-agent/src/common.rs b/keylime-agent/src/common.rs index a9ac01fe..e3bbfa33 100644 --- a/keylime-agent/src/common.rs +++ b/keylime-agent/src/common.rs @@ -2,7 +2,6 @@ // Copyright 2021 Keylime Authors use crate::{ - crypto::{AES_128_KEY_LEN, AES_256_KEY_LEN}, error::{Error, Result}, permissions, }; @@ -10,14 +9,12 @@ use crate::{ use keylime::algorithms::{ EncryptionAlgorithm, HashAlgorithm, SignAlgorithm, }; -use keylime::tpm; -use log::*; -use openssl::{ - hash::{hash, MessageDigest}, - pkey::PKey, - x509::X509, +use keylime::{ + crypto::{hash, tss_pubkey_to_pem, AES_128_KEY_LEN, AES_256_KEY_LEN}, + tpm, }; -use picky_asn1_x509::SubjectPublicKeyInfo; +use log::*; +use openssl::hash::MessageDigest; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::{ @@ -255,8 +252,8 @@ impl AgentData { /// This is used as the agent UUID when the configuration option 'uuid' is set as 'hash_ek' pub(crate) fn hash_ek_pubkey(ek_pub: Public) -> Result { // Calculate the SHA-256 hash of the public key in PEM format - let pem = crate::crypto::tss_pubkey_to_pem(ek_pub)?; - let hash = crate::crypto::hash(&pem, MessageDigest::sha256())?; + let pem = tss_pubkey_to_pem(ek_pub)?; + let hash = hash(&pem, MessageDigest::sha256())?; Ok(hex::encode(hash)) } diff --git a/keylime-agent/src/error.rs b/keylime-agent/src/error.rs index a5978c0b..23f4e4c2 100644 --- a/keylime-agent/src/error.rs +++ b/keylime-agent/src/error.rs @@ -59,7 +59,7 @@ pub(crate) enum Error { #[error("Number parsing error: {0}")] NumParse(#[from] std::num::ParseIntError), #[error("Crypto error: {0}")] - Crypto(#[from] crate::crypto::CryptoError), + Crypto(#[from] keylime::crypto::CryptoError), #[cfg(feature = "with-zmq")] #[error("ZMQ error: {0}")] Zmq(#[from] zmq::Error), @@ -81,8 +81,6 @@ pub(crate) enum Error { Persist(#[from] tempfile::PersistError), #[error("Error joining threads: {0}")] Join(#[from] tokio::task::JoinError), - #[error("Asn1DerError: {0}")] - PickyAsn1(#[from] picky_asn1_der::Asn1DerError), #[error("Error sending internal message: {0}")] Sender(String), #[error("Error receiving internal message: {0}")] diff --git a/keylime-agent/src/main.rs b/keylime-agent/src/main.rs index 920b4cce..56df4c5e 100644 --- a/keylime-agent/src/main.rs +++ b/keylime-agent/src/main.rs @@ -33,7 +33,6 @@ mod common; mod config; -mod crypto; mod error; mod errors_handler; mod keys_handler; @@ -56,7 +55,7 @@ use futures::{ future::{ok, TryFutureExt}, try_join, }; -use keylime::{ima::MeasurementList, list_parser::parse_list, tpm}; +use keylime::{crypto, ima::MeasurementList, list_parser::parse_list, tpm}; use log::*; use openssl::{ pkey::{PKey, Private, Public}, diff --git a/keylime-agent/src/quotes_handler.rs b/keylime-agent/src/quotes_handler.rs index 33ffb1c0..61a7fc25 100644 --- a/keylime-agent/src/quotes_handler.rs +++ b/keylime-agent/src/quotes_handler.rs @@ -340,8 +340,9 @@ pub async fn integrity( #[cfg(test)] mod tests { use super::*; - use crate::{common::API_VERSION, crypto::testing::pkey_pub_from_pem}; + use crate::common::API_VERSION; use actix_web::{test, web, App}; + use keylime::{crypto::testing::pkey_pub_from_pem, tpm}; #[actix_rt::test] async fn test_identity() { diff --git a/keylime/Cargo.toml b/keylime/Cargo.toml index ead3e288..c35d5fd6 100644 --- a/keylime/Cargo.toml +++ b/keylime/Cargo.toml @@ -19,6 +19,8 @@ serde_derive.workspace = true static_assertions.workspace = true thiserror.workspace = true tss-esapi.workspace = true +picky-asn1-der.workspace = true +picky-asn1-x509.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/keylime-agent/src/crypto.rs b/keylime/src/crypto.rs similarity index 94% rename from keylime-agent/src/crypto.rs rename to keylime/src/crypto.rs index bb8c682f..7afca54f 100644 --- a/keylime-agent/src/crypto.rs +++ b/keylime/src/crypto.rs @@ -20,8 +20,8 @@ use openssl::{ }; use picky_asn1_x509::SubjectPublicKeyInfo; use std::{ - fs::{read_to_string, set_permissions, File, Permissions}, - io::{Read, Write}, + fs::{read_to_string, set_permissions, Permissions}, + io::Write, os::unix::fs::PermissionsExt, path::Path, string::{FromUtf8Error, String}, @@ -90,19 +90,19 @@ pub enum CryptoError { Infallible(#[source] std::convert::Infallible), /// Invalid input length - #[error("Invalid input length")] + #[error("Invalid input length {length}")] InvalidInputLength { length: usize }, /// Invalid key length - #[error("Invalid key length")] + #[error("Invalid key length {length}")] InvalidKeyLength { length: usize }, - /// Error reading file - #[error("failed to read file")] + /// Read error + #[error("failed to read")] IOReadError(#[source] std::io::Error), - /// Error writing file - #[error("failed to write file")] + /// Write error + #[error("failed to write")] IOWriteError(#[source] std::io::Error), /// Error setting file permission @@ -181,6 +181,20 @@ pub enum CryptoError { #[source] openssl::error::ErrorStack, ), + /// Error setting SSL server certificate + #[error("failed to set SSL server certificate")] + SSLContextBuilderSetCertificate(#[source] openssl::error::ErrorStack), + + /// Error setting SSL server private key + #[error("failed to set SSL server private key")] + SSLContextBuilderSetPrivateKey(#[source] openssl::error::ErrorStack), + + /// Error setting SSL server trusted certificate store + #[error("failed to set SSL server trusted certificate store")] + SSLContextBuilderSetTrustedCertStore( + #[source] openssl::error::ErrorStack, + ), + /// Error getting String from UTF-8 Vec #[error("failed to create String from UTF-8 Vec")] StringFromVec(#[from] FromUtf8Error), @@ -313,9 +327,7 @@ pub enum CryptoError { } /// Load a X509 certificate in DER format from file -pub(crate) fn load_x509_der( - input_cert_path: &Path, -) -> Result { +pub fn load_x509_der(input_cert_path: &Path) -> Result { let contents = std::fs::read(input_cert_path).map_err(CryptoError::IOReadError)?; @@ -323,9 +335,7 @@ pub(crate) fn load_x509_der( } /// Load X509 certificate in PEM format from file -pub(crate) fn load_x509_pem( - input_cert_path: &Path, -) -> Result { +pub fn load_x509_pem(input_cert_path: &Path) -> Result { let contents = std::fs::read(input_cert_path).map_err(CryptoError::IOReadError)?; @@ -344,7 +354,7 @@ fn load_x509_cert_chain( } /// Load X509 certificate chains in PEM format from a list of files -pub(crate) fn load_x509_cert_list( +pub fn load_x509_cert_list( input_cert_list: Vec<&Path>, ) -> Result, CryptoError> { let mut loaded = Vec::::new(); @@ -364,10 +374,7 @@ pub(crate) fn load_x509_cert_list( } /// Write a X509 certificate to a file in PEM format -pub(crate) fn write_x509( - cert: &X509, - file_path: &Path, -) -> Result<(), CryptoError> { +pub fn write_x509(cert: &X509, file_path: &Path) -> Result<(), CryptoError> { let mut file = std::fs::File::create(file_path).map_err(|source| { CryptoError::FSCreateError { file: file_path.display().to_string(), @@ -381,16 +388,14 @@ pub(crate) fn write_x509( } /// Get the X509 certificate public key -pub(crate) fn x509_get_pubkey( - cert: &X509, -) -> Result, CryptoError> { +pub fn x509_get_pubkey(cert: &X509) -> Result, CryptoError> { cert.public_key().map_err(CryptoError::X509GetPublicError) } /// Encode the X509 certificate in PEM format /// /// The certificate is returned as a String -pub(crate) fn x509_to_pem(cert: &X509) -> Result { +pub fn x509_to_pem(cert: &X509) -> Result { String::from_utf8(cert.to_pem().map_err(CryptoError::X509ToPEMError)?) .map_err(CryptoError::StringFromVec) } @@ -398,14 +403,14 @@ pub(crate) fn x509_to_pem(cert: &X509) -> Result { /// Encode the X509 certificate in DER format /// /// The certificate is returned as a Vec -pub(crate) fn x509_to_der(cert: &X509) -> Result, CryptoError> { +pub fn x509_to_der(cert: &X509) -> Result, CryptoError> { cert.to_der().map_err(CryptoError::X509ToDERError) } /// Encode a TSS Public key in PEM format /// /// The public key is returned as a Vec -pub(crate) fn tss_pubkey_to_pem( +pub fn tss_pubkey_to_pem( pubkey: tss_esapi::structures::Public, ) -> Result, CryptoError> { // Converting Public TSS key to PEM @@ -423,7 +428,7 @@ pub(crate) fn tss_pubkey_to_pem( } /// Calculate the hash of the input data using the given Message Digest algorithm -pub(crate) fn hash( +pub fn hash( data: &[u8], algorithm: MessageDigest, ) -> Result, CryptoError> { @@ -433,7 +438,7 @@ pub(crate) fn hash( } /// Check an x509 certificate contains a specific public key -pub(crate) fn check_x509_key( +pub fn check_x509_key( cert: &X509, tpm_key: tss_esapi::structures::Public, ) -> Result { @@ -510,9 +515,7 @@ pub(crate) fn check_x509_key( /// Detect a template from a certificate /// Templates defined in: TPM 2.0 Keys for Device Identity and Attestation at https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf -pub(crate) fn match_cert_to_template( - cert: &X509, -) -> Result { +pub fn match_cert_to_template(cert: &X509) -> Result { // Id:RSA_PSS only added in rust-openssl from v0.10.59; remove this let and use Id::RSA_PSS after update // Id taken from https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/nid.h#4039 let id_rsa_pss: Id = Id::from_raw(912); @@ -564,7 +567,7 @@ pub(crate) fn match_cert_to_template( } /// Read a PEM file and returns the public and private keys -pub(crate) fn load_key_pair( +pub fn load_key_pair( key_path: &Path, key_password: Option<&str>, ) -> Result<(PKey, PKey), CryptoError> { @@ -589,7 +592,7 @@ pub(crate) fn load_key_pair( /// Write a private key to a file. /// /// If a passphrase is provided, the key will be stored encrypted using AES-256-CBC -pub(crate) fn write_key_pair( +pub fn write_key_pair( key: &PKey, file_path: &Path, passphrase: Option<&str>, @@ -643,7 +646,7 @@ fn rsa_generate(key_size: u32) -> Result, CryptoError> { .map_err(CryptoError::PKeyFromRSAError) } -pub(crate) fn rsa_generate_pair( +pub fn rsa_generate_pair( key_size: u32, ) -> Result<(PKey, PKey), CryptoError> { let private = rsa_generate(key_size)?; @@ -679,9 +682,7 @@ fn pkey_pub_from_priv( } } -pub(crate) fn pkey_pub_to_pem( - pubkey: &PKey, -) -> Result { +pub fn pkey_pub_to_pem(pubkey: &PKey) -> Result { pubkey .public_key_to_pem() .map_err(CryptoError::PublicKeyToPEMError) @@ -690,7 +691,7 @@ pub(crate) fn pkey_pub_to_pem( }) } -pub(crate) fn generate_x509( +pub fn generate_x509( key: &PKey, uuid: &str, ) -> Result { @@ -732,7 +733,7 @@ pub(crate) fn generate_x509( Ok(builder.build()) } -pub(crate) fn generate_tls_context( +pub fn generate_tls_context( tls_cert: &X509, key: &PKey, ca_certs: Vec, @@ -740,8 +741,12 @@ pub(crate) fn generate_tls_context( let mut ssl_context_builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()) .map_err(CryptoError::SSLContextBuilderSetAcceptorParameters)?; - ssl_context_builder.set_certificate(tls_cert); - ssl_context_builder.set_private_key(key); + ssl_context_builder + .set_certificate(tls_cert) + .map_err(CryptoError::SSLContextBuilderSetCertificate)?; + ssl_context_builder + .set_private_key(key) + .map_err(CryptoError::SSLContextBuilderSetPrivateKey)?; // Build verification cert store. let mut mtls_store_builder = X509StoreBuilder::new() @@ -753,7 +758,9 @@ pub(crate) fn generate_tls_context( } let mtls_store = mtls_store_builder.build(); - ssl_context_builder.set_verify_cert_store(mtls_store); + ssl_context_builder + .set_verify_cert_store(mtls_store) + .map_err(CryptoError::SSLContextBuilderSetTrustedCertStore)?; // Enable mutual TLS verification let mut verify_mode = SslVerifyMode::empty(); @@ -778,7 +785,7 @@ pub(crate) fn generate_tls_context( * PBKDF2 function defaults to SHA-1 unless otherwise specified, and * Python-Keylime uses this default. */ -pub(crate) fn pbkdf2( +pub fn pbkdf2( input_password: String, input_salt: String, ) -> Result { @@ -806,7 +813,7 @@ pub(crate) fn pbkdf2( * * Verify a remote message and signature against a local rsa cert */ -pub(crate) fn asym_verify( +pub fn asym_verify( keypair: &PKeyRef, message: &str, signature: &str, @@ -842,7 +849,7 @@ pub(crate) fn asym_verify( * Take in an RSA-encrypted ciphertext and an RSA private key and decrypt the * ciphertext based on PKCS1 OAEP. */ -pub(crate) fn rsa_oaep_decrypt( +pub fn rsa_oaep_decrypt( priv_key: &PKey, data: &[u8], ) -> Result, CryptoError> { @@ -881,10 +888,7 @@ pub(crate) fn rsa_oaep_decrypt( * * Sign message and return HMAC result string */ -pub(crate) fn compute_hmac( - key: &[u8], - data: &[u8], -) -> Result, CryptoError> { +pub fn compute_hmac(key: &[u8], data: &[u8]) -> Result, CryptoError> { let pkey = PKey::hmac(key).map_err(CryptoError::PKeyHMACNewError)?; // SHA-384 is used as the underlying hash algorithm. // @@ -899,7 +903,7 @@ pub(crate) fn compute_hmac( signer.sign_to_vec().map_err(CryptoError::SignError) } -pub(crate) fn verify_hmac( +pub fn verify_hmac( key: &[u8], data: &[u8], hmac: &[u8], @@ -926,10 +930,7 @@ pub(crate) fn verify_hmac( Ok(()) } -pub(crate) fn decrypt_aead( - key: &[u8], - data: &[u8], -) -> Result, CryptoError> { +pub fn decrypt_aead(key: &[u8], data: &[u8]) -> Result, CryptoError> { let cipher = match key.len() { AES_128_KEY_LEN => Cipher::aes_128_gcm(), AES_256_KEY_LEN => Cipher::aes_256_gcm(), @@ -959,7 +960,7 @@ pub mod testing { use std::path::Path; #[derive(Error, Debug)] - pub(crate) enum CryptoTestError { + pub enum CryptoTestError { /// Crypto error #[error("CryptoError")] CryptoError(#[from] CryptoError), @@ -977,7 +978,7 @@ pub mod testing { InvalidIVLen { expected: usize, got: usize }, } - pub(crate) fn rsa_import_pair( + pub fn rsa_import_pair( path: impl AsRef, ) -> Result<(PKey, PKey), CryptoTestError> { let contents = read_to_string(path)?; @@ -986,14 +987,14 @@ pub mod testing { Ok((public, private)) } - pub(crate) fn pkey_pub_from_pem( + pub fn pkey_pub_from_pem( pem: &str, ) -> Result, CryptoTestError> { PKey::::public_key_from_pem(pem.as_bytes()) .map_err(CryptoTestError::OpenSSLError) } - pub(crate) fn rsa_oaep_encrypt( + pub fn rsa_oaep_encrypt( pub_key: &PKey, data: &[u8], ) -> Result, CryptoTestError> { @@ -1014,7 +1015,7 @@ pub mod testing { Ok(encrypted) } - pub(crate) fn encrypt_aead( + pub fn encrypt_aead( key: &[u8], iv: &[u8], data: &[u8], @@ -1053,13 +1054,13 @@ pub mod testing { Ok(result) } - pub(crate) fn rsa_generate( + pub fn rsa_generate( key_size: u32, ) -> Result, CryptoTestError> { super::rsa_generate(key_size).map_err(CryptoTestError::CryptoError) } - pub(crate) fn write_x509_der( + pub fn write_x509_der( cert: &X509, file_path: &Path, ) -> Result<(), CryptoTestError> { @@ -1081,7 +1082,6 @@ pub mod testing { #[cfg(test)] mod tests { use super::*; - use openssl::rsa::Rsa; use std::{fs, path::Path}; use testing::{encrypt_aead, rsa_import_pair, rsa_oaep_encrypt}; @@ -1231,11 +1231,7 @@ mod tests { let key = b"0123456789012345"; let ciphertext = hex::decode("41424344").unwrap(); //#[allow_ci] let result = decrypt_aead(&key[..], &ciphertext[..]); - let length = ciphertext.len(); - assert!(matches!( - result, - Err(CryptoError::InvalidInputLength { length }) - )); + assert!(result.is_err()); } #[test] @@ -1271,7 +1267,7 @@ mod tests { .join("test-rsa.pem"); // Get RSA keys - let (public, private) = rsa_import_pair(rsa_key_path).unwrap(); //#[allow_ci] + let (_public, private) = rsa_import_pair(rsa_key_path).unwrap(); //#[allow_ci] // Create temporary directory and files names let temp_dir = tempfile::tempdir().unwrap(); //#[allow_ci] @@ -1337,7 +1333,7 @@ mod tests { fn test_x509() { let tempdir = tempfile::tempdir().unwrap(); //#[allow_ci] - let (pubkey, privkey) = rsa_generate_pair(2048).unwrap(); //#[allow_ci] + let (_pubkey, privkey) = rsa_generate_pair(2048).unwrap(); //#[allow_ci] let r = generate_x509(&privkey, "uuidA"); assert!(r.is_ok()); @@ -1389,7 +1385,7 @@ mod tests { let der_path = tempdir.path().join("cert.der"); let r = testing::write_x509_der(&cert, &der_path); assert!(r.is_ok()); - let e = load_x509_der(&der_path); + let r = load_x509_der(&der_path); assert!(r.is_ok()); // Loading multiple PEM certs should work when loading chain diff --git a/keylime/src/lib.rs b/keylime/src/lib.rs index b5374ae6..17fff909 100644 --- a/keylime/src/lib.rs +++ b/keylime/src/lib.rs @@ -1,4 +1,5 @@ pub mod algorithms; +pub mod crypto; pub mod ima; pub mod list_parser; pub mod tpm; diff --git a/keylime/test-data/test-rsa.pem b/keylime/test-data/test-rsa.pem new file mode 100644 index 00000000..3088db14 --- /dev/null +++ b/keylime/test-data/test-rsa.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA7cexb6YAL3BeOvlMFaBEwPiplq8z7ac2ovAU5heKDjl9xlzL +ab7YwxQmFb5m1FnkDfC6jASqCdDvZswfNhbV2QIIEjKCMvBopnKtE6kjQlhUbOtR +6CpZHiZOYHJoBtQ4jM0OgqfgO9Wiq4IXmsXuLeOCpD+mJXRelAFnbs1C96CUO0LE +IyFgOYtH2bvmck6iJqPTczdtCqjAmsLbwl48FJgOKlTxoJ3uktBoIAp03XeywG9e +f1z3sZrvjljB/KXH86eME5amq/3HyaSp2+Wmnpjs8MCIRDH6eIE318lSjD/t6gqe ++ysG1MlwyDFwVrLIoPOc+2hsFVJgNNK0M+URHwIDAQABAoIBAQCspZ8XAwAFceBp +j5OH7Eufla2FRIc+2neYTRvPiW3rMCE7wyrLCBBZbKrOhOYi73XgDVdVzRktcXAy +Qqmy21fAbnIvzE6u79H8cS1sJhX82SfLwf1BxmXYt1WXP9p6guLgkQ8lHQF6UH8B +ar762RY8aYH1AmX/sgPuESrpz838/0v4Tq52yJeSiGOS3MG6iRWQ6/K0CHT3olbH +RDN/48YrCSRP3RkAXoGcbev59+qOFJazC2av+qQM+59FwM2SrzrKa7RJLSQ1+wEz +gZEmmgJu6tOVUnAd7mg5VS+N7cXu6+Xl1jCg1N7XaQWh3nRCI1bysJ9UJQQZHl0B +gnIDXAU5AoGBAPex+wA1oaf4y22PIrVoLhD1HksdIxCOhmMYkojaIo3qZCVi8bS8 +JnUiHhvo25xYuUtB2Qw0VstzBaq6RDUnVgO3yVBYABc9MSmO+up/2ljNb9S5uWrm ++FzqjCRIWwABTWVyI7zvgVKSuSbvVLAQkgwAEBibp26V5E5SmuB4uKcNAoGBAPXA +m8n9Ajo+2Fzage0JZuW4uELbCqUwCkAlwJoSP42y0SIpB5fITkwRIwOat1MEhQ0I +yh68ZxGvqfE2fO78ZbE2bSlKkkadrZjPFAG2svd57MdHnR5c4iCm6ui7D0snCgcg +pUt2qlXYesABWb7o3tKOezDlbjFtv9JDJPOmnI3bAoGBAJw/RINsUW5BDiotWYqv +jieaSCK/3YerMHDAZmc3mwaErem7kZcd/PB0tiOK70Wf3jrv7be6KGosQ43f8/jH +uIWd4Lry2BPQwPtjOzrDrfvIk9vP0Hvz+QW72u1kSyskpyrwJkUfnCd3cJ5z6Ksr +uMUjIQQ05BhpK1yQ1Sv2WxzdAoGBAKkLwd5SzOp1+mz83azI7+ALjaxnck4o2pQ/ +o9oXvWHiZFuEL7Xn0nweuaAsF/jiPge2SRqVbKzM0jCb05qtQeKB1ts1caNjqVtY +7qEzJK55TzfRejG9oMrnJuXKbv26L/qxKSLc0NTWYbGb/DkHhOb/nZwH5iHYJcAj +8dIshLpLAoGBAK9RBWaP3KdDheUDOVpSSnOjVOtzq0T2qHPLklpdZzz2BHGKTNly +qek7gOTiy/DvON0F/gFhVi0+7RnCG+CnMXe2Ac/vn3QqZLAL5pSc+AUhmfE72hn2 +HNz0GFbYmFqr8QpzXG7b4aCF/yxcPrHGN3LdBKZvPPv7OmcCPGVEOKVk +-----END RSA PRIVATE KEY----- diff --git a/keylime-agent/test-data/test-rsa.sig b/keylime/test-data/test-rsa.sig similarity index 100% rename from keylime-agent/test-data/test-rsa.sig rename to keylime/test-data/test-rsa.sig From b259659557abaced38134053b3ca1ed064b06db2 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Wed, 14 Feb 2024 10:00:34 +0100 Subject: [PATCH 6/8] payloads: Remove explicit drop of channel transmitter Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent/src/payloads.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/keylime-agent/src/payloads.rs b/keylime-agent/src/payloads.rs index 07b872ea..5c40125f 100644 --- a/keylime-agent/src/payloads.rs +++ b/keylime-agent/src/payloads.rs @@ -572,7 +572,6 @@ echo hello > test-output let result = payload_tx.send(PayloadMessage::Shutdown).await; assert!(result.is_ok()); - drop(payload_tx); arbiter.join(); } From 50a3f7fa62efb8cbb952e84e54f565b0dc208505 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Wed, 14 Feb 2024 10:07:06 +0100 Subject: [PATCH 7/8] tests/run.sh: Run tarpaulin with a single thread This is to avoid frequent deadlocks on CI. Signed-off-by: Anderson Toshiyuki Sasaki --- tests/run.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/run.sh b/tests/run.sh index c08398b9..b120313f 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -47,4 +47,5 @@ cargo tarpaulin --verbose \ --ignore-panics --ignore-tests \ --out Html --out Json \ --all-features \ - --engine llvm + --engine llvm \ + -- --test-threads=1 From a895c85150f0eeab0df168110d9e6cb86eb636e8 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Thu, 15 Feb 2024 11:09:28 +0100 Subject: [PATCH 8/8] crypto: Make error types less specific Consolidate errors with same origin and add context specific messages. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime/src/crypto.rs | 439 ++++++++++++++++++++++++------------------ 1 file changed, 249 insertions(+), 190 deletions(-) diff --git a/keylime/src/crypto.rs b/keylime/src/crypto.rs index 7afca54f..ee14373d 100644 --- a/keylime/src/crypto.rs +++ b/keylime/src/crypto.rs @@ -35,8 +35,11 @@ pub const AES_BLOCK_SIZE: usize = 16; #[derive(Error, Debug)] pub enum CryptoError { /// Error getting ASN.1 Time from days from now - #[error("failed to get ASN.1 Time from days from now")] - ASN1TimeDaysFromNowError(#[source] openssl::error::ErrorStack), + #[error("failed to get ASN.1 Time for {days} day(s) from now")] + ASN1TimeDaysFromNowError { + days: u32, + source: openssl::error::ErrorStack, + }, /// Error decoding base64 #[error("failed to decode base64")] @@ -46,30 +49,6 @@ pub enum CryptoError { #[error("failed to decrypt AES GCM encrypted data")] DecryptAEADError(#[source] openssl::error::ErrorStack), - /// Error creating RSA decrypter object - #[error("failed to create RSA decrypter object")] - DecrypterNewError(#[source] openssl::error::ErrorStack), - - /// Error setting RSA decrypter padding - #[error("failed to set RSA decrypter padding")] - DecrypterSetPaddingError(#[source] openssl::error::ErrorStack), - - /// Error setting RSA decrypter Message Digest algorithm - #[error("failed to set RSA decrypter Message Digest algorithm")] - DecrypterSetMessageDigestError(#[source] openssl::error::ErrorStack), - - /// Error setting RSA decrypter OAEP Message Digest algorithm - #[error("failed to set RSA decrypter OAEP Message Digest algorithm")] - DecrypterSetOAEPMessageDigestError(#[source] openssl::error::ErrorStack), - - /// Error getting RSA decrypter output length - #[error("failed to get RSA decrypter output length")] - DecrypterGetOutputLenError(#[source] openssl::error::ErrorStack), - - /// Error decrypting data with RSA OAEP - #[error("failed to decrypt data with RSA OAEP")] - DecryptRSAOAEPError(#[source] openssl::error::ErrorStack), - /// Error creating file #[error("failed to create file {file}")] FSCreateError { @@ -81,14 +60,21 @@ pub enum CryptoError { #[error("failed to calculate hash")] HashError(#[source] openssl::error::ErrorStack), - /// Error checking HMAC - #[error("HMAC check failed")] - HMACError, + /// Error generating HMAC + #[error("Failed generating HMAC: {message}")] + HMACError { + message: String, + source: openssl::error::ErrorStack, + }, /// Infallible #[error("Infallible")] Infallible(#[source] std::convert::Infallible), + /// Invalid HMAC + #[error("invalid HMAC")] + InvalidHMAC, + /// Invalid input length #[error("Invalid input length {length}")] InvalidInputLength { length: usize }, @@ -163,37 +149,26 @@ pub enum CryptoError { #[error("failed to get RSA public key from structure")] RSAGetPublicKeyError(#[source] openssl::error::ErrorStack), - /// Error signing data - #[error("failed to sign data")] - SignError(#[source] openssl::error::ErrorStack), - - /// Error creating Signer object - #[error("failed to create Signer object")] - SignerNewError(#[source] openssl::error::ErrorStack), - - /// Error add input data to Signer - #[error("failed to add input data to Signer")] - SignerUpdateError(#[source] openssl::error::ErrorStack), - - /// Error setting SSL server context parameters - #[error("failed to set SSL server context parameters")] - SSLContextBuilderSetAcceptorParameters( - #[source] openssl::error::ErrorStack, - ), - - /// Error setting SSL server certificate - #[error("failed to set SSL server certificate")] - SSLContextBuilderSetCertificate(#[source] openssl::error::ErrorStack), + /// RSA OAEP decrypt error + #[error("RSA OAEP decrypt error: {message}")] + RSAOAEPDecryptError { + message: String, + source: openssl::error::ErrorStack, + }, - /// Error setting SSL server private key - #[error("failed to set SSL server private key")] - SSLContextBuilderSetPrivateKey(#[source] openssl::error::ErrorStack), + /// Error signing data + #[error("failed to sign data: {message}")] + SignError { + message: String, + source: openssl::error::ErrorStack, + }, - /// Error setting SSL server trusted certificate store - #[error("failed to set SSL server trusted certificate store")] - SSLContextBuilderSetTrustedCertStore( - #[source] openssl::error::ErrorStack, - ), + /// Error generating TLS context + #[error("Failed to generate TLS context: {message}")] + SSLContextBuilderError { + message: String, + source: openssl::error::ErrorStack, + }, /// Error getting String from UTF-8 Vec #[error("failed to create String from UTF-8 Vec")] @@ -225,61 +200,26 @@ pub enum CryptoError { #[error("unsupported key algorithm: {id}")] UnsupportedKeyAlgorithm { id: String }, - /// Error creating signature verifier - #[error("failed to create signature verifier")] - VerifierNewError(#[source] openssl::error::ErrorStack), - - /// Error setting signature verifier padding algorithm - #[error("failed to set signature verifier padding algorithm")] - VerifierSetPaddingError(#[source] openssl::error::ErrorStack), - - /// Error setting signature verifier Message Digest algorithm - #[error("failed to set signature verifier Message Digest algorithm")] - VerifierSetMessageDigestError(#[source] openssl::error::ErrorStack), - - /// Error setting signature verifier RSA PSS salt length - #[error("failed to set signature verifier RSA PSS salt length")] - VerifierSetRSAPSSSaltLengthError(#[source] openssl::error::ErrorStack), - - /// Error adding input data to signature verifier - #[error("failed adding input data to signature verifier")] - VerifierUpdateError(#[source] openssl::error::ErrorStack), - /// Error verifying signature - #[error("failed verifying signature")] - VerifySignatureError(#[source] openssl::error::ErrorStack), - - /// Error creating X509 builder - #[error("failed to create X509 builder")] - X509BuilderNewError(#[source] openssl::error::ErrorStack), - - /// Error setting X509 certificate issuer name in builder - #[error("failed to set X509 certificate issuer name in builder")] - X509BuilderSetIssuerName(#[source] openssl::error::ErrorStack), - - /// Error setting X509 certificate Not After in builder - #[error("failed to set X509 certificate Not After in builder")] - X509BuilderSetNotAfter(#[source] openssl::error::ErrorStack), - - /// Error setting X509 certificate Not Before in builder - #[error("failed to set X509 certificate Not Before in builder")] - X509BuilderSetNotBefore(#[source] openssl::error::ErrorStack), - - /// Error setting X509 certificate public key in builder - #[error("failed to set X509 certificate public key in builder")] - X509BuilderSetPubKey(#[source] openssl::error::ErrorStack), - - /// Error setting X509 certificate subject name in builder - #[error("failed to set X509 subject name in builder")] - X509BuilderSetSubjectName(#[source] openssl::error::ErrorStack), + #[error("Signature verification failed: {message}")] + VerifyError { + message: String, + source: openssl::error::ErrorStack, + }, - /// Error setting X509 certificate version in builder - #[error("failed to set X509 certificate version in builder")] - X509BuilderSetVersion(#[source] openssl::error::ErrorStack), + /// Trusted X509 certificate store builder error + #[error("Trusted certificate store builder error: {message}")] + X509StoreBuilderError { + message: String, + source: openssl::error::ErrorStack, + }, - /// Error signing X509 certificate in builder - #[error("failed to signing X509 certificate in builder")] - X509BuilderSign(#[source] openssl::error::ErrorStack), + /// X509 certificate builder error + #[error("X509 certificate builder error: {message}")] + X509BuilderError { + message: String, + source: openssl::error::ErrorStack, + }, /// Error loading X509 certificate chain from PEM file #[error("failed to load X509 certificate chain from PEM file")] @@ -297,21 +237,12 @@ pub enum CryptoError { #[error("failed to get certificate public key")] X509GetPublicError(#[source] openssl::error::ErrorStack), - /// Error creating X509 Name object - #[error("failed to create X509 Name object")] - X509NameNewError(#[source] openssl::error::ErrorStack), - - /// Error appending entry by NID to X509 Name object - #[error("failed to append entry by NID to X509 Name object")] - X509NameAppendError(#[source] openssl::error::ErrorStack), - - /// Error adding X509 certificates to the store builder - #[error("failed to add X509 certificates to the store builder")] - X509StoreBuilderAddCertError(#[source] openssl::error::ErrorStack), - - /// Error creating X509 certificate store builder - #[error("failed to create X509 certificate store builder")] - X509StoreBuilderNewError(#[source] openssl::error::ErrorStack), + /// Error creating X509 Name + #[error("Error creating X509 Name: {message}")] + X509NameError { + message: String, + source: openssl::error::ErrorStack, + }, /// Error encoding X509 certificate in DER format #[error("failed to encode X509 certificate in DER format")] @@ -696,39 +627,72 @@ pub fn generate_x509( uuid: &str, ) -> Result { let mut name = - X509Name::builder().map_err(CryptoError::X509NameNewError)?; + X509Name::builder().map_err(|source| CryptoError::X509NameError { + message: "failed to create X509 Name object".into(), + source, + })?; name.append_entry_by_nid(Nid::COMMONNAME, uuid) - .map_err(CryptoError::X509NameAppendError)?; + .map_err(|source| CryptoError::X509NameError { + message: "failed to append entry by NID to X509 Name".into(), + source, + })?; let name = name.build(); - let valid_from = Asn1Time::days_from_now(0) - .map_err(CryptoError::ASN1TimeDaysFromNowError)?; - let valid_to = Asn1Time::days_from_now(356) - .map_err(CryptoError::ASN1TimeDaysFromNowError)?; + let valid_from = Asn1Time::days_from_now(0).map_err(|source| { + CryptoError::ASN1TimeDaysFromNowError { days: 0, source } + })?; + let valid_to = Asn1Time::days_from_now(365).map_err(|source| { + CryptoError::ASN1TimeDaysFromNowError { days: 365, source } + })?; let mut builder = - X509::builder().map_err(CryptoError::X509BuilderNewError)?; - builder - .set_version(2) - .map_err(CryptoError::X509BuilderSetVersion)?; - builder - .set_subject_name(&name) - .map_err(CryptoError::X509BuilderSetSubjectName)?; - builder - .set_issuer_name(&name) - .map_err(CryptoError::X509BuilderSetIssuerName)?; - builder - .set_not_before(&valid_from) - .map_err(CryptoError::X509BuilderSetNotBefore)?; - builder - .set_not_after(&valid_to) - .map_err(CryptoError::X509BuilderSetNotAfter)?; - builder - .set_pubkey(key) - .map_err(CryptoError::X509BuilderSetPubKey)?; + X509::builder().map_err(|source| CryptoError::X509BuilderError { + message: "failed to create X509 certificate builder object" + .into(), + source, + })?; + builder.set_version(2).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 certificate version".into(), + source, + } + })?; + builder.set_subject_name(&name).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 certificate subject name".into(), + source, + } + })?; + builder.set_issuer_name(&name).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 issuer name".into(), + source, + } + })?; + builder.set_not_before(&valid_from).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 certificate Not Before date".into(), + source, + } + })?; + builder.set_not_after(&valid_to).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 certificate Nof After date".into(), + source, + } + })?; + builder.set_pubkey(key).map_err(|source| { + CryptoError::X509BuilderError { + message: "failed to set X509 certificate public key".into(), + source, + } + })?; builder .sign(key, MessageDigest::sha256()) - .map_err(CryptoError::X509BuilderSign)?; + .map_err(|source| CryptoError::X509BuilderError { + message: "failed to sign X509 certificate".into(), + source, + })?; Ok(builder.build()) } @@ -738,29 +702,53 @@ pub fn generate_tls_context( key: &PKey, ca_certs: Vec, ) -> Result { - let mut ssl_context_builder = - SslAcceptor::mozilla_intermediate(SslMethod::tls()) - .map_err(CryptoError::SSLContextBuilderSetAcceptorParameters)?; + let mut ssl_context_builder = SslAcceptor::mozilla_intermediate( + SslMethod::tls(), + ) + .map_err(|source| CryptoError::SSLContextBuilderError { + message: "failed to create Context Builder object".into(), + source, + })?; ssl_context_builder .set_certificate(tls_cert) - .map_err(CryptoError::SSLContextBuilderSetCertificate)?; - ssl_context_builder - .set_private_key(key) - .map_err(CryptoError::SSLContextBuilderSetPrivateKey)?; + .map_err(|source| CryptoError::SSLContextBuilderError { + message: "failed to set SSL server certificate".into(), + source, + })?; + ssl_context_builder.set_private_key(key).map_err(|source| { + CryptoError::SSLContextBuilderError { + message: "failed to set SSL server private key".into(), + source, + } + })?; // Build verification cert store. - let mut mtls_store_builder = X509StoreBuilder::new() - .map_err(CryptoError::X509StoreBuilderNewError)?; + let mut mtls_store_builder = + X509StoreBuilder::new().map_err(|source| { + CryptoError::X509StoreBuilderError { + message: + "failed to create X509 certificate store builder object" + .into(), + source, + } + })?; for cert in ca_certs { mtls_store_builder .add_cert(cert) - .map_err(CryptoError::X509StoreBuilderAddCertError)?; + .map_err(|source| CryptoError::X509StoreBuilderError{ + message: "failed to add certificate to X509 trusted certificate store".into(), + source, + })?; } let mtls_store = mtls_store_builder.build(); ssl_context_builder .set_verify_cert_store(mtls_store) - .map_err(CryptoError::SSLContextBuilderSetTrustedCertStore)?; + .map_err(|source| CryptoError::SSLContextBuilderError { + message: "failed to set SSL server trusted certificate store" + .into(), + source, + })?; // Enable mutual TLS verification let mut verify_mode = SslVerifyMode::empty(); @@ -819,26 +807,48 @@ pub fn asym_verify( signature: &str, ) -> Result { let mut verifier = Verifier::new(MessageDigest::sha256(), keypair) - .map_err(CryptoError::VerifierNewError)?; + .map_err(|source| CryptoError::VerifyError { + message: "failed to create signature verifier object".into(), + source, + })?; verifier .set_rsa_padding(Padding::PKCS1_PSS) - .map_err(CryptoError::VerifierSetPaddingError)?; + .map_err(|source| CryptoError::VerifyError { + message: "failed to set signature verifier padding algorithm" + .into(), + source, + })?; verifier .set_rsa_mgf1_md(MessageDigest::sha256()) - .map_err(CryptoError::VerifierSetMessageDigestError)?; + .map_err(|source| CryptoError::VerifyError { + message: + "failed to set signature verifier Message Digest algorithm" + .into(), + source, + })?; verifier .set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::MAXIMUM_LENGTH) - .map_err(CryptoError::VerifierSetRSAPSSSaltLengthError)?; - verifier - .update(message.as_bytes()) - .map_err(CryptoError::VerifierUpdateError)?; + .map_err(|source| CryptoError::VerifyError { + message: "failed to set signature verifier RSA PSS salt length" + .into(), + source, + })?; + verifier.update(message.as_bytes()).map_err(|source| { + CryptoError::VerifyError { + message: "failed adding input data to signature verifier".into(), + source, + } + })?; verifier .verify( &general_purpose::STANDARD .decode(signature.as_bytes()) .map_err(CryptoError::Base64DecodeError)?, ) - .map_err(CryptoError::VerifySignatureError) + .map_err(|source| CryptoError::VerifyError { + message: "invalid signature".into(), + source, + }) } /* @@ -853,29 +863,52 @@ pub fn rsa_oaep_decrypt( priv_key: &PKey, data: &[u8], ) -> Result, CryptoError> { - let mut decrypter = - Decrypter::new(priv_key).map_err(CryptoError::DecrypterNewError)?; + let mut decrypter = Decrypter::new(priv_key).map_err(|source| { + CryptoError::RSAOAEPDecryptError { + message: "failed to create RSA decrypter object".into(), + source, + } + })?; decrypter .set_rsa_padding(Padding::PKCS1_OAEP) - .map_err(CryptoError::DecrypterSetPaddingError)?; + .map_err(|source| CryptoError::RSAOAEPDecryptError { + message: "failed to set RSA decrypter padding".into(), + source, + })?; decrypter .set_rsa_mgf1_md(MessageDigest::sha1()) - .map_err(CryptoError::DecrypterSetMessageDigestError)?; + .map_err(|source| CryptoError::RSAOAEPDecryptError { + message: "failed to set RSA decrypter Message Digest algorithm" + .into(), + source, + })?; decrypter .set_rsa_oaep_md(MessageDigest::sha1()) - .map_err(CryptoError::DecrypterSetOAEPMessageDigestError)?; + .map_err(|source| CryptoError::RSAOAEPDecryptError { + message: + "failed to set RSA decrypter OAEP Message Digest algorithm" + .into(), + source, + })?; // Create an output buffer - let buffer_len = decrypter - .decrypt_len(data) - .map_err(CryptoError::DecrypterGetOutputLenError)?; + let buffer_len = decrypter.decrypt_len(data).map_err(|source| { + CryptoError::RSAOAEPDecryptError { + message: "failed to get RSA decrypter output length".into(), + source, + } + })?; let mut decrypted = vec![0; buffer_len]; // Decrypt and truncate the buffer - let decrypted_len = decrypter - .decrypt(data, &mut decrypted) - .map_err(CryptoError::DecryptRSAOAEPError)?; + let decrypted_len = + decrypter.decrypt(data, &mut decrypted).map_err(|source| { + CryptoError::RSAOAEPDecryptError { + message: "failed to decrypt data with RSA OAEP".into(), + source, + } + })?; decrypted.truncate(decrypted_len); Ok(decrypted) @@ -895,12 +928,25 @@ pub fn compute_hmac(key: &[u8], data: &[u8]) -> Result, CryptoError> { // Reference: // https://keylime-docs.readthedocs.io/en/latest/rest_apis.html#post--v1.0-keys-ukey // https://github.com/keylime/keylime/blob/910b38b296038b187a020c095dc747e9c46cbef3/keylime/crypto.py#L151 - let mut signer = Signer::new(MessageDigest::sha384(), &pkey) - .map_err(CryptoError::SignerNewError)?; + let mut signer = + Signer::new(MessageDigest::sha384(), &pkey).map_err(|source| { + CryptoError::SignError { + message: "failed creating Signer object".into(), + source, + } + })?; signer .update(data) - .map_err(CryptoError::SignerUpdateError)?; - signer.sign_to_vec().map_err(CryptoError::SignError) + .map_err(|source| CryptoError::SignError { + message: "failed to add input data to Signer".into(), + source, + })?; + signer + .sign_to_vec() + .map_err(|source| CryptoError::SignError { + message: "failed to generate signature".into(), + source, + }) } pub fn verify_hmac( @@ -914,17 +960,30 @@ pub fn verify_hmac( // Reference: // https://keylime-docs.readthedocs.io/en/latest/rest_apis.html#post--v1.0-keys-ukey // https://github.com/keylime/keylime/blob/910b38b296038b187a020c095dc747e9c46cbef3/keylime/crypto.py#L151 - let mut signer = Signer::new(MessageDigest::sha384(), &pkey) - .map_err(CryptoError::SignerNewError)?; + let mut signer = + Signer::new(MessageDigest::sha384(), &pkey).map_err(|source| { + CryptoError::HMACError { + message: "failed to create Signer object".into(), + source, + } + })?; signer .update(data) - .map_err(CryptoError::SignerUpdateError)?; + .map_err(|source| CryptoError::HMACError { + message: "failed to add input data to Signer".into(), + source, + })?; if !memcmp::eq( - &signer.sign_to_vec().map_err(CryptoError::SignError)?, + &signer + .sign_to_vec() + .map_err(|source| CryptoError::HMACError { + message: "failed to generate HMAC".into(), + source, + })?, hmac, ) { - return Err(CryptoError::HMACError); + return Err(CryptoError::InvalidHMAC); } Ok(())