diff --git a/rs/crypto/internal/crypto_lib/types/src/encrypt/forward_secure.rs b/rs/crypto/internal/crypto_lib/types/src/encrypt/forward_secure.rs index e979959724c..feb1e2a99fd 100644 --- a/rs/crypto/internal/crypto_lib/types/src/encrypt/forward_secure.rs +++ b/rs/crypto/internal/crypto_lib/types/src/encrypt/forward_secure.rs @@ -30,9 +30,17 @@ impl TryFrom for CspFsEncryptionPublicKey { type Error = MalformedFsEncryptionPublicKeyError; fn try_from(pk_proto: PublicKeyProto) -> Result { + Self::try_from(&pk_proto) + } +} + +impl TryFrom<&PublicKeyProto> for CspFsEncryptionPublicKey { + type Error = MalformedFsEncryptionPublicKeyError; + + fn try_from(pk_proto: &PublicKeyProto) -> Result { if pk_proto.algorithm != AlgorithmIdProto::Groth20Bls12381 as i32 { return Err(MalformedFsEncryptionPublicKeyError { - key_bytes: pk_proto.key_value, + key_bytes: pk_proto.clone().key_value, internal_error: format!("Unknown algorithm: {}", pk_proto.algorithm), }); } diff --git a/rs/crypto/internal/crypto_service_provider/src/keygen/mod.rs b/rs/crypto/internal/crypto_service_provider/src/keygen/mod.rs index ebb2773fc1e..62968c37799 100644 --- a/rs/crypto/internal/crypto_service_provider/src/keygen/mod.rs +++ b/rs/crypto/internal/crypto_service_provider/src/keygen/mod.rs @@ -71,7 +71,7 @@ impl CspSecretKeyStoreChecker for Csp { /// Some key related utils pub mod utils { use crate::types::{CspPop, CspPublicKey}; - use ic_crypto_internal_threshold_sig_ecdsa::MEGaPublicKey; + use ic_crypto_internal_threshold_sig_ecdsa::{EccCurveType, MEGaPublicKey}; use ic_crypto_internal_types::encrypt::forward_secure::{ CspFsEncryptionPop, CspFsEncryptionPublicKey, }; @@ -138,4 +138,32 @@ pub mod utils { timestamp: None, } } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum MEGaPublicKeyFromProtoError { + UnsupportedAlgorithm { + algorithm_id: Option, + }, + MalformedPublicKey { + key_bytes: Vec, + }, + } + + /// Deserialize a Protobuf public key to a MEGaPublicKey. + pub fn mega_public_key_from_proto( + proto: &PublicKeyProto, + ) -> Result { + let curve_type = match AlgorithmIdProto::from_i32(proto.algorithm) { + Some(AlgorithmIdProto::MegaSecp256k1) => Ok(EccCurveType::K256), + alg_id => Err(MEGaPublicKeyFromProtoError::UnsupportedAlgorithm { + algorithm_id: alg_id, + }), + }?; + + MEGaPublicKey::deserialize(curve_type, &proto.key_value).map_err(|_| { + MEGaPublicKeyFromProtoError::MalformedPublicKey { + key_bytes: proto.key_value.clone(), + } + }) + } } diff --git a/rs/crypto/internal/crypto_service_provider/src/lib.rs b/rs/crypto/internal/crypto_service_provider/src/lib.rs index c133642de79..4618e36d58f 100644 --- a/rs/crypto/internal/crypto_service_provider/src/lib.rs +++ b/rs/crypto/internal/crypto_service_provider/src/lib.rs @@ -26,15 +26,15 @@ use crate::vault::remote_csp_vault::RemoteCspVault; use crate::api::{ CspIDkgProtocol, CspKeyGenerator, CspSecretKeyStoreChecker, CspSigVerifier, CspSigner, CspThresholdEcdsaSigVerifier, CspThresholdEcdsaSigner, CspTlsHandshakeSignerProvider, - NiDkgCspClient, NodePublicKeyData, ThresholdSignatureCspClient, + DkgDealingEncryptionKeyIdRetrievalError, NiDkgCspClient, NodePublicKeyData, + NodePublicKeyDataError, ThresholdSignatureCspClient, }; -use crate::api::{DkgDealingEncryptionKeyIdRetrievalError, NodePublicKeyDataError}; use crate::public_key_store::proto_pubkey_store::ProtoPublicKeyStore; use crate::public_key_store::temp_pubkey_store::TempPublicKeyStore; use crate::public_key_store::PublicKeyStore; use crate::secret_key_store::temp_secret_key_store::TempSecretKeyStore; use crate::secret_key_store::SecretKeyStore; -use crate::types::CspPublicKey; +use crate::types::{CspPublicKey, ExternalPublicKeys}; use crate::vault::api::{CspPublicKeyStoreError, CspVault}; use ic_config::crypto::{CryptoConfig, CspVaultType}; use ic_crypto_internal_logmon::metrics::CryptoMetrics; diff --git a/rs/crypto/internal/crypto_service_provider/src/types.rs b/rs/crypto/internal/crypto_service_provider/src/types.rs index 6a0f3f1780d..302a20d772e 100644 --- a/rs/crypto/internal/crypto_service_provider/src/types.rs +++ b/rs/crypto/internal/crypto_service_provider/src/types.rs @@ -17,6 +17,7 @@ use ic_crypto_internal_threshold_sig_bls12381::types as threshold_types; #[cfg(test)] use ic_crypto_internal_threshold_sig_ecdsa::EccScalarBytes; use ic_crypto_internal_threshold_sig_ecdsa::{CommitmentOpeningBytes, MEGaKeySetK256Bytes}; +use ic_protobuf::registry::crypto::v1::{PublicKey, X509PublicKeyCert}; use ic_types::crypto::AlgorithmId; use serde::{Deserialize, Serialize}; use strum_macros::{EnumCount, IntoStaticStr}; @@ -268,3 +269,12 @@ impl CspSignature { pub struct SigConverter { target_algorithm: AlgorithmId, } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ExternalPublicKeys { + pub node_signing_public_key: PublicKey, + pub committee_signing_public_key: PublicKey, + pub tls_certificate: X509PublicKeyCert, + pub dkg_dealing_encryption_public_key: PublicKey, + pub idkg_dealing_encryption_public_key: PublicKey, +} diff --git a/rs/crypto/internal/crypto_service_provider/src/types/conversions.rs b/rs/crypto/internal/crypto_service_provider/src/types/conversions.rs index c73e12d62b1..6e5dbcb505f 100644 --- a/rs/crypto/internal/crypto_service_provider/src/types/conversions.rs +++ b/rs/crypto/internal/crypto_service_provider/src/types/conversions.rs @@ -61,10 +61,17 @@ impl TryFrom for CspPublicKey { // TODO (CRP-540): move the key bytes from pk_proto.key_value to the // resulting csp_pk (instead of copying/cloning them). fn try_from(pk_proto: PublicKeyProto) -> Result { + Self::try_from(&pk_proto) + } +} + +impl TryFrom<&PublicKeyProto> for CspPublicKey { + type Error = CryptoError; + fn try_from(pk_proto: &PublicKeyProto) -> Result { match AlgorithmId::from(pk_proto.algorithm) { AlgorithmId::Ed25519 => { let public_key_bytes = - ed25519_types::PublicKeyBytes::try_from(&pk_proto).map_err(|e| { + ed25519_types::PublicKeyBytes::try_from(pk_proto).map_err(|e| { CryptoError::MalformedPublicKey { algorithm: AlgorithmId::Ed25519, key_bytes: Some(e.key_bytes), @@ -75,7 +82,7 @@ impl TryFrom for CspPublicKey { } AlgorithmId::MultiBls12_381 => { let public_key_bytes = - multi_types::PublicKeyBytes::try_from(&pk_proto).map_err(|e| { + multi_types::PublicKeyBytes::try_from(pk_proto).map_err(|e| { CryptoError::MalformedPublicKey { algorithm: AlgorithmId::MultiBls12_381, key_bytes: Some(e.key_bytes), diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/api.rs b/rs/crypto/internal/crypto_service_provider/src/vault/api.rs index f834d5ce20c..5e1994800ad 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/api.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/api.rs @@ -2,6 +2,7 @@ use crate::api::{CspCreateMEGaKeyError, CspThresholdSignError}; use crate::key_id::KeyId; use crate::types::CspPublicCoefficients; use crate::types::{CspPop, CspPublicKey, CspSignature}; +use crate::ExternalPublicKeys; use ic_crypto_internal_seed::Seed; use ic_crypto_internal_threshold_sig_bls12381::api::ni_dkg_errors; use ic_crypto_internal_threshold_sig_ecdsa::{ @@ -147,6 +148,50 @@ pub enum CspTlsSignError { }, } +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct NodeKeysErrors { + pub node_signing_key_error: Option, + pub committee_signing_key_error: Option, + pub tls_certificate_error: Option, + pub dkg_dealing_encryption_key_error: Option, + pub idkg_dealing_encryption_key_error: Option, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct NodeKeysError { + pub external_public_key_error: Option, + pub local_public_key_error: Option, + pub secret_key_error: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ExternalPublicKeyError(pub Box); + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum LocalPublicKeyError { + /// No local public key exists. + NotFound, + /// A local public key exists, but it is not the same as the external key passed in. + Mismatch, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum SecretKeyError { + /// Unable to compute the key ID using the externally provided public key. + CannotComputeKeyId, + /// A local secret key matching the externally provided public key does not exist + NotFound, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum PksAndSksContainsErrors { + /// If one or more keys were missing, or were malformed, or did not match the corresponding + /// external public key. + NodeKeysErrors(NodeKeysErrors), + /// If a transient internal error occurs, e.g., an RPC error communicating with the remote vault + TransientInternalError(String), +} + /// `CspVault` offers a selection of operations that involve /// secret keys managed by the vault. pub trait CspVault: @@ -159,6 +204,7 @@ pub trait CspVault: + SecretKeyStoreCspVault + TlsHandshakeCspVault + PublicRandomSeedGenerator + + PublicAndSecretKeyStoreCspVault + PublicKeyStoreCspVault { } @@ -175,6 +221,7 @@ impl CspVault for T where + SecretKeyStoreCspVault + TlsHandshakeCspVault + PublicRandomSeedGenerator + + PublicAndSecretKeyStoreCspVault + PublicKeyStoreCspVault { } @@ -478,6 +525,30 @@ pub trait PublicKeyStoreCspVault { fn idkg_dealing_encryption_pubkeys_count(&self) -> Result; } +/// Operations of `CspVault` related to querying both the public and private key stores. +pub trait PublicAndSecretKeyStoreCspVault { + /// Checks whether the keys corresponding to the provided external public keys exist locally. + /// In particular, this means the provided public keys themselves are stored locally, as well + /// as the corresponding secret keys. Key comparisons will not take timestamps into account. + /// + /// # Parameters + /// The current external node public keys and TLS certificate. + /// + /// # Returns + /// An empty result if all the external public keys, and the corresponding secret keys, were + /// all found locally. + /// + /// # Errors + /// * `PksAndSksContainsErrors::NodeKeysErrors` if local public or secret keys were not + /// consistent with the provided external keys. + /// * `PksAndSksContainsErrors::TransientInternalError` if a transient internal error, e.g., an RPC + /// error, occurred. + fn pks_and_sks_contains( + &self, + external_public_keys: ExternalPublicKeys, + ) -> Result<(), PksAndSksContainsErrors>; +} + /// Operations of `CspVault` related to TLS handshakes. pub trait TlsHandshakeCspVault: Send + Sync { /// Generates TLS key material for node with ID `node_id`. diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/mod.rs b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/mod.rs index 41aed1100c0..b956f179d9e 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/mod.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/mod.rs @@ -2,6 +2,7 @@ mod basic_sig; mod idkg; mod multi_sig; mod ni_dkg; +mod public_and_secret_key_store; mod public_key_store; mod public_seed; mod secret_key_store; @@ -240,6 +241,16 @@ impl (RwLockReadGuard<'_, S>, RwLockReadGuard<'_, P>) { + let sks_read_lock = self.node_secret_key_store.read(); + let pks_read_lock = self.public_key_store.read(); + (sks_read_lock, pks_read_lock) + } + fn sks_read_lock(&self) -> RwLockReadGuard<'_, S> { self.node_secret_key_store.read() } diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/mod.rs b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/mod.rs new file mode 100644 index 00000000000..6753e98ef74 --- /dev/null +++ b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/mod.rs @@ -0,0 +1,451 @@ +//! The crypto service provider API for querying public and secret keys in combination. +use crate::vault::api::{ + ExternalPublicKeyError, LocalPublicKeyError, NodeKeysError, NodeKeysErrors, + PksAndSksContainsErrors, PublicAndSecretKeyStoreCspVault, SecretKeyError, +}; +use crate::vault::local_csp_vault::LocalCspVault; +use crate::{CspPublicKey, ExternalPublicKeys, KeyId, SecretKeyStore}; +use parking_lot::RwLockReadGuard; + +use crate::keygen::utils::{mega_public_key_from_proto, MEGaPublicKeyFromProtoError}; +use crate::public_key_store::PublicKeyStore; +use crate::types::conversions::CspPopFromPublicKeyProtoError; +use crate::types::CspPop; +use ic_crypto_internal_types::encrypt::forward_secure::{ + CspFsEncryptionPop, CspFsEncryptionPublicKey, +}; +use ic_crypto_tls_interfaces::TlsPublicKeyCert; +use ic_protobuf::registry::crypto::v1::{PublicKey as PublicKeyProto, X509PublicKeyCert}; +use ic_types::crypto::AlgorithmId; +use rand::{CryptoRng, Rng}; + +#[cfg(test)] +mod tests; + +impl + PublicAndSecretKeyStoreCspVault for LocalCspVault +{ + fn pks_and_sks_contains( + &self, + external_public_keys: ExternalPublicKeys, + ) -> Result<(), PksAndSksContainsErrors> { + let key_ids = compute_key_ids(&external_public_keys); + + let (local_public_keys, secret_key_errors_result) = { + let (sks_read_lock, pks_read_lock) = self.sks_and_pks_read_locks(); + ( + read_local_public_keys(pks_read_lock), + check_secret_keys_existence(sks_read_lock, &key_ids), + ) + }; // drop read locks on SKS and PKS + + let local_public_key_errors_result = + compare_public_keys(&external_public_keys, &local_public_keys); + if key_ids.is_ok() + && local_public_key_errors_result.is_ok() + && secret_key_errors_result.is_ok() + { + Ok(()) + } else { + Err(PksAndSksContainsErrors::NodeKeysErrors(combine_errors( + key_ids, + local_public_key_errors_result, + secret_key_errors_result, + ))) + } + } +} + +#[derive(Debug)] +struct LocalNodePublicKeyResults { + node_signing_public_key_result: Result<(), LocalPublicKeyError>, + committee_signing_public_key_result: Result<(), LocalPublicKeyError>, + tls_certificate_public_key_result: Result<(), LocalPublicKeyError>, + dkg_dealing_encryption_public_key_result: Result<(), LocalPublicKeyError>, + idkg_dealing_encryption_public_key_result: Result<(), LocalPublicKeyError>, +} + +impl LocalNodePublicKeyResults { + pub fn is_ok(&self) -> bool { + self.node_signing_public_key_result.is_ok() + && self.committee_signing_public_key_result.is_ok() + && self.tls_certificate_public_key_result.is_ok() + && self.dkg_dealing_encryption_public_key_result.is_ok() + && self.idkg_dealing_encryption_public_key_result.is_ok() + } +} + +struct KeyIds { + node_signing_key_id: Result, + committee_signing_key_id: Result, + dkg_dealing_encryption_key_id: Result, + tls_secret_key_id: Result, + idkg_dealing_encryption_key_id: Result, +} + +impl KeyIds { + pub fn is_ok(&self) -> bool { + self.node_signing_key_id.is_ok() + && self.committee_signing_key_id.is_ok() + && self.dkg_dealing_encryption_key_id.is_ok() + && self.tls_secret_key_id.is_ok() + && self.idkg_dealing_encryption_key_id.is_ok() + } +} + +fn compute_key_ids(external_public_keys: &ExternalPublicKeys) -> KeyIds { + let node_signing_key_id = + compute_node_signing_key_id(&external_public_keys.node_signing_public_key); + let committee_signing_key_id = + compute_committee_signing_key_id(&external_public_keys.committee_signing_public_key); + let dkg_dealing_encryption_key_id = compute_dkg_dealing_encryption_key_id( + &external_public_keys.dkg_dealing_encryption_public_key, + ); + let tls_secret_key_id = compute_tls_certificate_key_id(&external_public_keys.tls_certificate); + let idkg_dealing_encryption_key_id = compute_idkg_dealing_encryption_key_id( + &external_public_keys.idkg_dealing_encryption_public_key, + ); + + KeyIds { + node_signing_key_id, + committee_signing_key_id, + dkg_dealing_encryption_key_id, + tls_secret_key_id, + idkg_dealing_encryption_key_id, + } +} + +fn compute_node_signing_key_id( + external_node_signing_public_key: &PublicKeyProto, +) -> Result { + if AlgorithmId::from(external_node_signing_public_key.algorithm) != AlgorithmId::Ed25519 { + return Err(ExternalPublicKeyError(Box::new(format!( + "expected public key algorithm Ed25519, but found {:?}", + AlgorithmId::from(external_node_signing_public_key.algorithm), + )))); + } + let csp_key = CspPublicKey::try_from(external_node_signing_public_key.clone()) + .map_err(|err| ExternalPublicKeyError(Box::new(format!("{:?}", err))))?; + Ok(KeyId::from(&csp_key)) +} + +fn compute_committee_signing_key_id( + external_committee_signing_public_key: &PublicKeyProto, +) -> Result { + if AlgorithmId::from(external_committee_signing_public_key.algorithm) + != AlgorithmId::MultiBls12_381 + { + return Err(ExternalPublicKeyError(Box::new(format!( + "expected public key algorithm MultiBls12_381, but found {:?}", + AlgorithmId::from(external_committee_signing_public_key.algorithm), + )))); + } + ensure_committee_signing_key_pop_is_well_formed(external_committee_signing_public_key)?; + let csp_key = CspPublicKey::try_from(external_committee_signing_public_key) + .map_err(|err| ExternalPublicKeyError(Box::new(format!("{:?}", err))))?; + Ok(KeyId::from(&csp_key)) +} + +fn ensure_committee_signing_key_pop_is_well_formed( + pk_proto: &PublicKeyProto, +) -> Result<(), ExternalPublicKeyError> { + CspPop::try_from(pk_proto).map_err(|e| match e { + CspPopFromPublicKeyProtoError::NoPopForAlgorithm { algorithm } => { + ExternalPublicKeyError(Box::new(format!( + "Malformed public key (No POP for algorithm {:?})", + algorithm + ))) + } + CspPopFromPublicKeyProtoError::MissingProofData => ExternalPublicKeyError(Box::new( + "Malformed public key (Missing proof data)".to_string(), + )), + CspPopFromPublicKeyProtoError::MalformedPop { .. } => { + ExternalPublicKeyError(Box::new("Malformed public key (Malformed Pop)".to_string())) + } + })?; + + Ok(()) +} + +fn compute_dkg_dealing_encryption_key_id( + external_dkg_dealing_encryption_public_key: &PublicKeyProto, +) -> Result { + if AlgorithmId::from(external_dkg_dealing_encryption_public_key.algorithm) + != AlgorithmId::Groth20_Bls12_381 + { + return Err(ExternalPublicKeyError(Box::new(format!( + "Malformed public key: Expected public key algorithm Groth20_Bls12_381, but found {:?}", + AlgorithmId::from(external_dkg_dealing_encryption_public_key.algorithm), + )))); + } + let _csp_pop = CspFsEncryptionPop::try_from(external_dkg_dealing_encryption_public_key) + .map_err(|e| ExternalPublicKeyError(Box::new(format!("Malformed public key {:?}", e))))?; + let csp_key = CspFsEncryptionPublicKey::try_from(external_dkg_dealing_encryption_public_key) + .map_err(|e| { + ExternalPublicKeyError(Box::new(format!( + "Malformed public key ({:?}", + e.internal_error + ))) + })?; + Ok(KeyId::from(&csp_key)) +} + +fn compute_idkg_dealing_encryption_key_id( + external_idkg_dealing_encryption_public_key: &PublicKeyProto, +) -> Result { + let idkg_dealing_encryption_pk = mega_public_key_from_proto( + external_idkg_dealing_encryption_public_key, + ) + .map_err(|e| match e { + MEGaPublicKeyFromProtoError::UnsupportedAlgorithm { algorithm_id } => { + ExternalPublicKeyError(Box::new(format!("Malformed public key: unsupported algorithm ({:?}) of I-DKG dealing encryption key", + algorithm_id, + ), + )) + } + MEGaPublicKeyFromProtoError::MalformedPublicKey { .. } => { + ExternalPublicKeyError(Box::new("Malformed public key: I-DKG dealing encryption key malformed".to_string())) + } + })?; + + let key_id = KeyId::try_from(&idkg_dealing_encryption_pk).map_err(|error| { + ExternalPublicKeyError(Box::new(format!( + "Malformed public key: failed to derive key ID from MEGa public key: {}", + error + ))) + })?; + Ok(key_id) +} + +fn compute_tls_certificate_key_id( + external_tls_certificate: &X509PublicKeyCert, +) -> Result { + let public_key_cert = TlsPublicKeyCert::new_from_der( + external_tls_certificate.certificate_der.clone(), + ) + .map_err(|e| ExternalPublicKeyError(Box::new(format!("Malformed certificate: {:?}", e))))?; + + Ok(KeyId::from(&public_key_cert)) +} + +struct LocalNodePublicKeys { + pub node_signing_public_key: Option, + pub committee_signing_public_key: Option, + pub tls_certificate: Option, + pub dkg_dealing_encryption_public_key: Option, + pub idkg_dealing_encryption_public_keys: Vec, +} + +fn read_local_public_keys( + pks_read_lock: RwLockReadGuard<'_, P>, +) -> LocalNodePublicKeys { + LocalNodePublicKeys { + node_signing_public_key: pks_read_lock.node_signing_pubkey(), + committee_signing_public_key: pks_read_lock.committee_signing_pubkey(), + tls_certificate: pks_read_lock.tls_certificate().cloned(), + dkg_dealing_encryption_public_key: pks_read_lock.ni_dkg_dealing_encryption_pubkey(), + idkg_dealing_encryption_public_keys: pks_read_lock.idkg_dealing_encryption_pubkeys(), + } +} + +fn compare_public_keys( + external_public_keys: &ExternalPublicKeys, + local_public_keys: &LocalNodePublicKeys, +) -> LocalNodePublicKeyResults { + let node_signing_public_key_result = compare_local_and_external_public_keys( + local_public_keys.node_signing_public_key.as_ref(), + &external_public_keys.node_signing_public_key, + ); + let committee_signing_public_key_result = compare_local_and_external_public_keys( + local_public_keys.committee_signing_public_key.as_ref(), + &external_public_keys.committee_signing_public_key, + ); + let tls_certificate_public_key_result = compare_local_and_external_certificates( + local_public_keys.tls_certificate.as_ref(), + &external_public_keys.tls_certificate, + ); + let dkg_dealing_encryption_public_key_result = compare_local_and_external_public_keys( + local_public_keys.dkg_dealing_encryption_public_key.as_ref(), + &external_public_keys.dkg_dealing_encryption_public_key, + ); + let idkg_dealing_encryption_public_key_result = if !local_public_keys + .idkg_dealing_encryption_public_keys + .is_empty() + { + let found_idkg_key = local_public_keys + .idkg_dealing_encryption_public_keys + .iter() + .any(|local_idkg_key| { + local_idkg_key.equal_ignoring_timestamp( + &external_public_keys.idkg_dealing_encryption_public_key, + ) + }); + if found_idkg_key { + Ok(()) + } else { + Err(LocalPublicKeyError::Mismatch) + } + } else { + Err(LocalPublicKeyError::NotFound) + }; + + LocalNodePublicKeyResults { + node_signing_public_key_result, + committee_signing_public_key_result, + tls_certificate_public_key_result, + dkg_dealing_encryption_public_key_result, + idkg_dealing_encryption_public_key_result, + } +} + +fn compare_local_and_external_public_keys( + maybe_local_public_key: Option<&PublicKeyProto>, + external_public_key: &PublicKeyProto, +) -> Result<(), LocalPublicKeyError> { + if let Some(local_public_key) = maybe_local_public_key { + let key_match = local_public_key.equal_ignoring_timestamp(external_public_key); + if !key_match { + return Err(LocalPublicKeyError::Mismatch); + } + return Ok(()); + } + Err(LocalPublicKeyError::NotFound) +} + +fn compare_local_and_external_certificates( + maybe_local_cert: Option<&X509PublicKeyCert>, + external_cert: &X509PublicKeyCert, +) -> Result<(), LocalPublicKeyError> { + if let Some(local_cert) = maybe_local_cert { + if local_cert != external_cert { + return Err(LocalPublicKeyError::Mismatch); + } + return Ok(()); + } + Err(LocalPublicKeyError::NotFound) +} + +struct SecretKeyResults { + pub node_signing_secret_key_result: Result<(), SecretKeyError>, + pub committee_signing_secret_key_result: Result<(), SecretKeyError>, + pub tls_certificate_secret_key_result: Result<(), SecretKeyError>, + pub dkg_dealing_encryption_secret_key_result: Result<(), SecretKeyError>, + pub idkg_dealing_encryption_secret_key_result: Result<(), SecretKeyError>, +} + +impl SecretKeyResults { + pub fn is_ok(&self) -> bool { + self.node_signing_secret_key_result.is_ok() + && self.committee_signing_secret_key_result.is_ok() + && self.tls_certificate_secret_key_result.is_ok() + && self.dkg_dealing_encryption_secret_key_result.is_ok() + && self.idkg_dealing_encryption_secret_key_result.is_ok() + } +} + +fn check_secret_keys_existence( + sks_read_lock: RwLockReadGuard<'_, S>, + key_ids: &KeyIds, +) -> SecretKeyResults { + let node_signing_secret_key_result = + check_secret_key_existence(&sks_read_lock, &key_ids.node_signing_key_id); + let committee_signing_secret_key_result = + check_secret_key_existence(&sks_read_lock, &key_ids.committee_signing_key_id); + let dkg_dealing_encryption_secret_key_result = + check_secret_key_existence(&sks_read_lock, &key_ids.dkg_dealing_encryption_key_id); + let tls_certificate_secret_key_result = + check_secret_key_existence(&sks_read_lock, &key_ids.tls_secret_key_id); + let idkg_dealing_encryption_secret_key_result = + check_secret_key_existence(&sks_read_lock, &key_ids.idkg_dealing_encryption_key_id); + + SecretKeyResults { + node_signing_secret_key_result, + committee_signing_secret_key_result, + tls_certificate_secret_key_result, + dkg_dealing_encryption_secret_key_result, + idkg_dealing_encryption_secret_key_result, + } +} + +fn check_secret_key_existence( + sks_read_lock: &RwLockReadGuard<'_, S>, + key_id_result: &Result, +) -> Result<(), SecretKeyError> { + match key_id_result { + Ok(key_id) => { + if sks_read_lock.contains(key_id) { + Ok(()) + } else { + Err(SecretKeyError::NotFound) + } + } + Err(_) => Err(SecretKeyError::CannotComputeKeyId), + } +} + +fn combine_errors( + key_ids: KeyIds, + local_public_key_results: LocalNodePublicKeyResults, + secret_key_results: SecretKeyResults, +) -> NodeKeysErrors { + NodeKeysErrors { + node_signing_key_error: construct_check_key_pair_errors( + key_ids.node_signing_key_id.err(), + local_public_key_results + .node_signing_public_key_result + .err(), + secret_key_results.node_signing_secret_key_result.err(), + ), + committee_signing_key_error: construct_check_key_pair_errors( + key_ids.committee_signing_key_id.err(), + local_public_key_results + .committee_signing_public_key_result + .err(), + secret_key_results.committee_signing_secret_key_result.err(), + ), + tls_certificate_error: construct_check_key_pair_errors( + key_ids.tls_secret_key_id.err(), + local_public_key_results + .tls_certificate_public_key_result + .err(), + secret_key_results.tls_certificate_secret_key_result.err(), + ), + dkg_dealing_encryption_key_error: construct_check_key_pair_errors( + key_ids.dkg_dealing_encryption_key_id.err(), + local_public_key_results + .dkg_dealing_encryption_public_key_result + .err(), + secret_key_results + .dkg_dealing_encryption_secret_key_result + .err(), + ), + idkg_dealing_encryption_key_error: construct_check_key_pair_errors( + key_ids.idkg_dealing_encryption_key_id.err(), + local_public_key_results + .idkg_dealing_encryption_public_key_result + .err(), + secret_key_results + .idkg_dealing_encryption_secret_key_result + .err(), + ), + } +} + +fn construct_check_key_pair_errors( + external_public_key_error: Option, + local_public_key_error: Option, + secret_key_error: Option, +) -> Option { + match ( + &external_public_key_error, + &local_public_key_error, + &secret_key_error, + ) { + (None, None, None) => None, + _ => Some(NodeKeysError { + external_public_key_error, + local_public_key_error, + secret_key_error, + }), + } +} diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/tests.rs b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/tests.rs new file mode 100644 index 00000000000..06f92d86223 --- /dev/null +++ b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/public_and_secret_key_store/tests.rs @@ -0,0 +1,560 @@ +use crate::vault::test_utils; +use crate::LocalCspVault; + +mod key_id_computations { + use super::*; + use crate::vault::local_csp_vault::public_and_secret_key_store::{ + compute_committee_signing_key_id, compute_dkg_dealing_encryption_key_id, + compute_idkg_dealing_encryption_key_id, compute_node_signing_key_id, + compute_tls_certificate_key_id, ExternalPublicKeyError, + }; + use crate::vault::test_utils::public_key_store::{ + generate_idkg_dealing_encryption_key_pair, NODE_1, + }; + use crate::CspVault; + use assert_matches::assert_matches; + use ic_crypto_internal_types::encrypt::forward_secure::groth20_bls12_381; + use ic_protobuf::registry::crypto::v1::PublicKey as PublicKeyProto; + use ic_protobuf::registry::crypto::v1::{AlgorithmId as AlgorithmIdProto, X509PublicKeyCert}; + use ic_types::crypto::AlgorithmId; + use ic_types_test_utils::ids::node_test_id; + use std::sync::Arc; + + #[test] + fn should_fail_to_compute_node_signing_key_id_on_incorrect_algorithm_id() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let mut node_signing_public_key = { + let _ = csp_vault + .gen_node_signing_key_pair() + .expect("Error generating node signing key pair"); + csp_vault + .current_node_public_keys() + .expect("Error getting current node public keys") + .node_signing_public_key + .expect("Node public key missing") + }; + node_signing_public_key.algorithm = AlgorithmId::MegaSecp256k1 as i32; + let result = compute_node_signing_key_id(&node_signing_public_key); + assert_matches!( + result, + Err(ExternalPublicKeyError(internal_error)) + if internal_error.contains("expected public key algorithm Ed25519, but found MegaSecp256k1") + ) + } + + #[test] + fn should_fail_to_compute_committee_signing_key_id_on_incorrect_algorithm_id() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let mut committee_signing_public_key = { + let _ = csp_vault + .gen_committee_signing_key_pair() + .expect("Error generating committee signing key pair"); + csp_vault + .current_node_public_keys() + .expect("Error getting current node public keys") + .committee_signing_public_key + .expect("Committee public key missing") + }; + committee_signing_public_key.algorithm = AlgorithmId::MegaSecp256k1 as i32; + let result = compute_committee_signing_key_id(&committee_signing_public_key); + assert_matches!(result, + Err(ExternalPublicKeyError(internal_error)) if internal_error.contains("expected public key algorithm MultiBls12_381, but found MegaSecp256k1")) + } + + #[test] + fn should_fail_to_compute_dkg_dealing_encryption_key_id_on_incorrect_algorithm_id() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let mut dkg_dealing_encryption_public_key = { + let _ = csp_vault + .gen_dealing_encryption_key_pair(node_test_id(NODE_1)) + .expect("Error generating DKG dealing encryption key pair"); + csp_vault + .current_node_public_keys() + .expect("Error getting current node public keys") + .dkg_dealing_encryption_public_key + .expect("DKG dealing encryption public key missing") + }; + dkg_dealing_encryption_public_key.algorithm = AlgorithmId::MegaSecp256k1 as i32; + let result = compute_dkg_dealing_encryption_key_id(&dkg_dealing_encryption_public_key); + assert_matches!(result, + Err(ExternalPublicKeyError(internal_error)) if internal_error.contains("Malformed public key: Expected public key algorithm Groth20_Bls12_381, but found MegaSecp256k1")) + } + + #[test] + fn should_fail_to_compute_dkg_dealing_encryption_key_id_if_length_is_wrong() { + const WRONG_LENGTH: usize = groth20_bls12_381::FsEncryptionPublicKey::SIZE - 5; + let pk_data = [42; WRONG_LENGTH]; + let pk_proto = PublicKeyProto { + algorithm: AlgorithmIdProto::Groth20Bls12381 as i32, + key_value: pk_data.to_vec(), + version: 0, + proof_data: None, + timestamp: None, + }; + let result = compute_dkg_dealing_encryption_key_id(&pk_proto); + assert_matches!( + result, + Err(ExternalPublicKeyError(internal_error)) + if internal_error.contains("Malformed public key MissingProofData") + ); + } + + #[test] + fn should_fail_to_compute_dkg_dealing_encryption_key_id_if_proof_data_is_malformed() { + let malformed_proof_data = vec![42, 42]; + let pk_proto = PublicKeyProto { + algorithm: AlgorithmIdProto::Groth20Bls12381 as i32, + key_value: [42; groth20_bls12_381::FsEncryptionPublicKey::SIZE].to_vec(), + version: 0, + proof_data: Some(malformed_proof_data), + timestamp: None, + }; + let result = compute_dkg_dealing_encryption_key_id(&pk_proto); + assert_matches!( + result, + Err(ExternalPublicKeyError(internal_error)) + if internal_error.contains("Malformed public key MalformedPop") + ); + } + + #[test] + fn should_fail_to_compute_tls_certificate_key_id_on_incorrect_algorithm_id() { + let tls_certificate = X509PublicKeyCert { + certificate_der: b"malformed certificate".to_vec(), + }; + let result = compute_tls_certificate_key_id(&tls_certificate); + assert_matches!(result, + Err(ExternalPublicKeyError(internal_error)) if internal_error.contains("Malformed certificate: TlsPublicKeyCertCreationError")) + } + + #[test] + fn should_fail_to_compute_idkg_dealing_encryption_key_id_on_incorrect_algorithm_id() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let mut idkg_dealing_encryption_public_key = { + let _ = generate_idkg_dealing_encryption_key_pair(&csp_vault); + csp_vault + .current_node_public_keys() + .expect("Error getting current node public keys") + .idkg_dealing_encryption_public_key + .expect("iDKG dealing encryption public key missing") + }; + idkg_dealing_encryption_public_key.algorithm = AlgorithmId::Groth20_Bls12_381 as i32; + let result = compute_idkg_dealing_encryption_key_id(&idkg_dealing_encryption_public_key); + assert_matches!(result, + Err(ExternalPublicKeyError(internal_error)) if internal_error.contains("Malformed public key: unsupported algorithm")) + } + + #[test] + fn should_fail_if_committee_signing_key_missing_proof_data() { + let pk_proto = PublicKeyProto { + algorithm: AlgorithmIdProto::MultiBls12381 as i32, + key_value: [42; 10].to_vec(), + version: 1, + proof_data: None, + timestamp: None, + }; + + let result = compute_committee_signing_key_id(&pk_proto); + assert_matches!( + result, + Err(ExternalPublicKeyError(error_string)) + if *error_string == "Malformed public key (Missing proof data)" + ); + } + + #[test] + fn should_fail_if_committee_signing_key_pop_is_malformed() { + let malformed_proof_data = vec![ + 42; + ic_crypto_internal_multi_sig_bls12381::types::IndividualSignatureBytes::SIZE + - 1 + ]; + let pk_proto = PublicKeyProto { + algorithm: AlgorithmIdProto::MultiBls12381 as i32, + key_value: [42; 10].to_vec(), + version: 1, + proof_data: Some(malformed_proof_data), + timestamp: None, + }; + + let result = compute_committee_signing_key_id(&pk_proto); + assert_matches!( + result, + Err(ExternalPublicKeyError(error_string)) + if *error_string == "Malformed public key (Malformed Pop)" + ); + } +} + +mod public_key_comparisons { + use super::*; + use crate::vault::api::LocalPublicKeyError; + use crate::vault::local_csp_vault::public_and_secret_key_store::{ + compare_public_keys, LocalNodePublicKeyResults, LocalNodePublicKeys, + }; + use crate::vault::test_utils::pks_and_sks::convert_to_external_public_keys; + use crate::vault::test_utils::public_key_store::generate_all_keys; + use crate::CspVault; + use assert_matches::assert_matches; + use ic_types::crypto::CurrentNodePublicKeys; + use std::sync::Arc; + + #[test] + fn should_return_success_for_identical_registry_and_local_public_keys() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert!(results.is_ok()); + } + + #[test] + fn should_fail_for_node_signing_public_keys_mismatch() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + external_public_keys.node_signing_public_key.key_value = b"malformed key".to_vec(); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Err(LocalPublicKeyError::Mismatch), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_committee_signing_public_keys_mismatch() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + external_public_keys.committee_signing_public_key.key_value = b"malformed key".to_vec(); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Err(LocalPublicKeyError::Mismatch), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_tls_certificate_mismatch() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + external_public_keys.tls_certificate.certificate_der = b"malformed certificate".to_vec(); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Err(LocalPublicKeyError::Mismatch), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_dkg_dealing_encryption_public_keys_mismatch() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + external_public_keys + .dkg_dealing_encryption_public_key + .key_value = b"malformed key".to_vec(); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Err(LocalPublicKeyError::Mismatch), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_idkg_dealing_encryption_public_keys_mismatch() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + external_public_keys + .idkg_dealing_encryption_public_key + .key_value = b"malformed key".to_vec(); + let local_public_keys = convert_to_local_public_keys(current_node_public_keys); + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Err(LocalPublicKeyError::Mismatch), + } + ); + } + + #[test] + fn should_fail_for_missing_local_node_signing_public_key() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let mut local_public_keys = convert_to_local_public_keys(current_node_public_keys); + local_public_keys.node_signing_public_key = None; + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Err(LocalPublicKeyError::NotFound), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_missing_local_committee_signing_public_key() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let mut local_public_keys = convert_to_local_public_keys(current_node_public_keys); + local_public_keys.committee_signing_public_key = None; + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Err(LocalPublicKeyError::NotFound), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_missing_local_tls_certificate() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let mut local_public_keys = convert_to_local_public_keys(current_node_public_keys); + local_public_keys.tls_certificate = None; + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Err(LocalPublicKeyError::NotFound), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_missing_local_dkg_dealing_encryption_public_keys() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let mut local_public_keys = convert_to_local_public_keys(current_node_public_keys); + local_public_keys.dkg_dealing_encryption_public_key = None; + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Err(LocalPublicKeyError::NotFound), + idkg_dealing_encryption_public_key_result: Ok(()), + } + ); + } + + #[test] + fn should_fail_for_missing_local_idkg_dealing_encryption_public_keys() { + let csp_vault: Arc = LocalCspVault::builder().build_into_arc(); + let current_node_public_keys = generate_all_keys(&csp_vault); + let external_public_keys = + convert_to_external_public_keys(current_node_public_keys.clone()); + let mut local_public_keys = convert_to_local_public_keys(current_node_public_keys); + local_public_keys.idkg_dealing_encryption_public_keys = vec![]; + let results = compare_public_keys(&external_public_keys, &local_public_keys); + assert_matches!( + results, + LocalNodePublicKeyResults { + node_signing_public_key_result: Ok(()), + committee_signing_public_key_result: Ok(()), + tls_certificate_public_key_result: Ok(()), + dkg_dealing_encryption_public_key_result: Ok(()), + idkg_dealing_encryption_public_key_result: Err(LocalPublicKeyError::NotFound), + } + ); + } + + fn convert_to_local_public_keys( + current_node_public_keys: CurrentNodePublicKeys, + ) -> LocalNodePublicKeys { + LocalNodePublicKeys { + node_signing_public_key: current_node_public_keys.node_signing_public_key, + committee_signing_public_key: current_node_public_keys.committee_signing_public_key, + tls_certificate: current_node_public_keys.tls_certificate, + dkg_dealing_encryption_public_key: current_node_public_keys + .dkg_dealing_encryption_public_key, + idkg_dealing_encryption_public_keys: current_node_public_keys + .idkg_dealing_encryption_public_key + .map_or(vec![], |public_key| vec![public_key]), + } + } +} + +mod pks_and_sks_contains { + use super::*; + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_one_idkg_key() { + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_one_idkg_key(LocalCspVault::builder().build_into_arc()); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys() { + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys_and_external_key_not_first_in_vector( + ) { + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys_and_external_key_not_first_in_vector( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_where_idkg_keys_have_different_timestamps( + ) { + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_where_idkg_keys_have_different_timestamps( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_no_keys_match() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_no_keys_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_node_signing_key_does_not_match() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_node_signing_key_does_not_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc() + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_committee_signing_key_does_not_match() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_committee_signing_key_does_not_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc() + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_dkg_dealing_encryption_key_does_not_match() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_dkg_dealing_encryption_key_does_not_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc() + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_tls_certificate_does_not_match() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_tls_certificate_does_not_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc() + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_idkg_dealing_encryption_key_does_not_match() + { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_idkg_dealing_encryption_key_does_not_match( + LocalCspVault::builder().build_into_arc(), + LocalCspVault::builder().build_into_arc() + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_node_signing_key_is_malformed() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_node_signing_key_is_malformed( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_committee_signing_key_is_malformed() + { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_committee_signing_key_is_malformed( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_dkg_dealing_encryption_key_is_malformed( + ) { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_dkg_dealing_encryption_key_is_malformed( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_tls_certificate_is_malformed() { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_tls_certificate_is_malformed( + LocalCspVault::builder().build_into_arc(), + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_idkg_dealing_encryption_key_is_malformed( + ) { + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_idkg_dealing_encryption_key_is_malformed( + LocalCspVault::builder().build_into_arc(), + ); + } +} diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/codec.rs b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/codec.rs index f63e211ef53..dea364f6fa1 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/codec.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/codec.rs @@ -146,6 +146,7 @@ enum CspVaultMethod { LoadThresholdSigningKey, RetainThresholdKeysIfPresent, SksContains, + PksAndSksContains, PksContains, CurrentNodePublicKeys, CurrentNodePublicKeysWithTimestamps, @@ -196,6 +197,9 @@ impl CspVaultMethod { "retain_threshold_keys_if_present", ), CspVaultMethod::SksContains => (MetricsDomain::KeyManagement, "sks_contains"), + CspVaultMethod::PksAndSksContains => { + (MetricsDomain::KeyManagement, "pks_and_sks_contains") + } CspVaultMethod::PksContains => (MetricsDomain::KeyManagement, "pks_contains"), CspVaultMethod::CurrentNodePublicKeys => { (MetricsDomain::KeyManagement, "current_node_public_keys") @@ -251,6 +255,7 @@ impl From<&TarpcCspVaultRequest> for CspVaultMethod { Req::LoadThresholdSigningKey { .. } => Method::LoadThresholdSigningKey, Req::RetainThresholdKeysIfPresent { .. } => Method::RetainThresholdKeysIfPresent, Req::SksContains { .. } => Method::SksContains, + Req::PksAndSksContains { .. } => Method::PksAndSksContains, Req::PksContains { .. } => Method::PksContains, Req::CurrentNodePublicKeys { .. } => Method::CurrentNodePublicKeys, Req::CurrentNodePublicKeysWithTimestamps { .. } => { @@ -289,6 +294,7 @@ impl From<&TarpcCspVaultResponse> for CspVaultMethod { Resp::LoadThresholdSigningKey { .. } => Method::LoadThresholdSigningKey, Resp::RetainThresholdKeysIfPresent { .. } => Method::RetainThresholdKeysIfPresent, Resp::SksContains { .. } => Method::SksContains, + Resp::PksAndSksContains { .. } => Method::PksAndSksContains, Resp::PksContains { .. } => Method::PksContains, Resp::CurrentNodePublicKeys { .. } => Method::CurrentNodePublicKeys, Resp::CurrentNodePublicKeysWithTimestamps { .. } => { diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/mod.rs b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/mod.rs index 3e19054d30a..7b025684b58 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/mod.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/mod.rs @@ -3,7 +3,7 @@ use crate::types::{CspPop, CspPublicCoefficients, CspPublicKey, CspSignature}; use crate::vault::api::{ CspBasicSignatureError, CspBasicSignatureKeygenError, CspMultiSignatureError, CspMultiSignatureKeygenError, CspPublicKeyStoreError, CspSecretKeyStoreContainsError, - CspThresholdSignatureKeygenError, CspTlsKeygenError, CspTlsSignError, + CspThresholdSignatureKeygenError, CspTlsKeygenError, CspTlsSignError, PksAndSksContainsErrors, }; use ic_crypto_internal_seed::Seed; use ic_crypto_internal_threshold_sig_bls12381::api::ni_dkg_errors; @@ -36,6 +36,7 @@ mod tarpc_csp_vault_client; mod tarpc_csp_vault_server; use crate::key_id::KeyId; +use crate::ExternalPublicKeys; use ic_crypto_internal_logmon::metrics::CryptoMetrics; use std::sync::Arc; pub use tarpc_csp_vault_client::RemoteCspVault; @@ -145,6 +146,11 @@ pub trait TarpcCspVault { // Corresponds to `PublicKeyStoreCspVault.idkg_key_count()`. async fn idkg_key_count() -> Result; + // Corresponds to `PublicAndSecretKeyStoreCspVault.pks_and_sks_contains()`. + async fn pks_and_sks_contains( + external_public_keys: ExternalPublicKeys, + ) -> Result<(), PksAndSksContainsErrors>; + // Corresponds to `TlsHandshakeCspVault.gen_tls_key_pair()`. async fn gen_tls_key_pair( node: NodeId, diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_client.rs b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_client.rs index 76ac7ebfc31..139872dccce 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_client.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_client.rs @@ -6,12 +6,13 @@ use crate::vault::api::{ CspMultiSignatureError, CspMultiSignatureKeygenError, CspPublicKeyStoreError, CspSecretKeyStoreContainsError, CspThresholdSignatureKeygenError, CspTlsKeygenError, CspTlsSignError, IDkgProtocolCspVault, MultiSignatureCspVault, NiDkgCspVault, - PublicKeyStoreCspVault, PublicRandomSeedGenerator, PublicRandomSeedGeneratorError, - SecretKeyStoreCspVault, ThresholdEcdsaSignerCspVault, ThresholdSignatureCspVault, + PksAndSksContainsErrors, PublicAndSecretKeyStoreCspVault, PublicKeyStoreCspVault, + PublicRandomSeedGenerator, PublicRandomSeedGeneratorError, SecretKeyStoreCspVault, + ThresholdEcdsaSignerCspVault, ThresholdSignatureCspVault, }; use crate::vault::remote_csp_vault::codec::{CspVaultClientObserver, ObservableCodec}; use crate::vault::remote_csp_vault::{remote_vault_codec_builder, TarpcCspVaultClient}; -use crate::TlsHandshakeCspVault; +use crate::{ExternalPublicKeys, TlsHandshakeCspVault}; use core::future::Future; use ic_crypto_internal_seed::Seed; use ic_crypto_internal_threshold_sig_bls12381::api::dkg_errors::InternalError; @@ -341,6 +342,23 @@ impl PublicKeyStoreCspVault for RemoteCspVault { } } +impl PublicAndSecretKeyStoreCspVault for RemoteCspVault { + fn pks_and_sks_contains( + &self, + external_public_keys: ExternalPublicKeys, + ) -> Result<(), PksAndSksContainsErrors> { + self.tokio_block_on( + self.tarpc_csp_client + .pks_and_sks_contains(context_with_timeout(self.rpc_timeout), external_public_keys), + ) + .unwrap_or_else(|rpc_error: tarpc::client::RpcError| { + Err(PksAndSksContainsErrors::TransientInternalError( + rpc_error.to_string(), + )) + }) + } +} + impl NiDkgCspVault for RemoteCspVault { fn gen_dealing_encryption_key_pair( &self, diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_server.rs b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_server.rs index 9d002b1fdc9..52d4ede14d7 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_server.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tarpc_csp_vault_server.rs @@ -6,7 +6,8 @@ use crate::secret_key_store::proto_store::ProtoSecretKeyStore; use crate::types::{CspPop, CspPublicCoefficients, CspPublicKey, CspSignature}; use crate::vault::api::{ BasicSignatureCspVault, CspPublicKeyStoreError, IDkgProtocolCspVault, MultiSignatureCspVault, - NiDkgCspVault, PublicKeyStoreCspVault, PublicRandomSeedGenerator, SecretKeyStoreCspVault, + NiDkgCspVault, PksAndSksContainsErrors, PublicAndSecretKeyStoreCspVault, + PublicKeyStoreCspVault, PublicRandomSeedGenerator, SecretKeyStoreCspVault, ThresholdEcdsaSignerCspVault, ThresholdSignatureCspVault, TlsHandshakeCspVault, }; use crate::vault::api::{ @@ -16,6 +17,7 @@ use crate::vault::api::{ }; use crate::vault::local_csp_vault::LocalCspVault; use crate::vault::remote_csp_vault::{remote_vault_codec_builder, TarpcCspVault}; +use crate::ExternalPublicKeys; use crate::{ SecretKeyStore, CANISTER_SKS_DATA_FILENAME, PUBLIC_KEY_STORE_DATA_FILENAME, SKS_DATA_FILENAME, }; @@ -326,6 +328,17 @@ impl< execute_on_thread_pool(self.thread_pool_handle, job).await } + // PublicAndSecretKeyStoreCspVault-methods. + async fn pks_and_sks_contains( + self, + _: context::Context, + external_public_keys: ExternalPublicKeys, + ) -> Result<(), PksAndSksContainsErrors> { + let vault = self.local_csp_vault; + let job = move || vault.pks_and_sks_contains(external_public_keys); + execute_on_thread_pool(self.thread_pool_handle, job).await + } + // 'TlsHandshakeCspVault'-methods. async fn gen_tls_key_pair( self, diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tests.rs b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tests.rs index 7371805ef99..cb333beba89 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tests.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/remote_csp_vault/tests.rs @@ -1116,3 +1116,127 @@ mod logging { ); } } + +mod pks_and_sks { + use super::*; + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_one_idkg_key() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_one_idkg_key(csp_vault); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys(csp_vault); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys_and_external_key_not_first_in_vector( + ) { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys_and_external_key_not_first_in_vector(csp_vault); + } + + #[test] + fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_where_idkg_keys_have_different_timestamps( + ) { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_success_for_pks_and_sks_contains_if_all_keys_match_where_idkg_keys_have_different_timestamps(csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_no_keys_match() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_no_keys_match( + csp_vault, + shadow_csp_vault, + ); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_node_signing_key_does_not_match() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_node_signing_key_does_not_match(csp_vault, shadow_csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_committee_signing_key_does_not_match() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_committee_signing_key_does_not_match(csp_vault, shadow_csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_dkg_dealing_encryption_key_does_not_match() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_dkg_dealing_encryption_key_does_not_match(csp_vault, shadow_csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_tls_certificate_does_not_match() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_tls_certificate_does_not_match(csp_vault, shadow_csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_idkg_dealing_encryption_key_does_not_match() + { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + let shadow_csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_idkg_dealing_encryption_key_does_not_match(csp_vault, shadow_csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_node_signing_key_is_malformed() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_node_signing_key_is_malformed(csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_committee_signing_key_is_malformed() + { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_committee_signing_key_is_malformed(csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_dkg_dealing_encryption_key_is_malformed( + ) { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_dkg_dealing_encryption_key_is_malformed(csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_tls_certificate_is_malformed() { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_tls_certificate_is_malformed(csp_vault); + } + + #[test] + fn should_return_error_for_pks_and_sks_contains_if_external_idkg_dealing_encryption_key_is_malformed( + ) { + let tokio_rt = new_tokio_runtime(); + let csp_vault = new_remote_csp_vault(tokio_rt.handle()); + test_utils::pks_and_sks::should_return_error_for_pks_and_sks_contains_if_external_idkg_dealing_encryption_key_is_malformed(csp_vault); + } +} diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/mod.rs b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/mod.rs index 974f5b9ccab..edf9484ee86 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/mod.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/mod.rs @@ -4,6 +4,7 @@ pub mod basic_sig; pub mod idkg; pub mod multi_sig; pub mod ni_dkg; +pub mod pks_and_sks; pub mod public_key_store; pub mod public_seed; pub mod sks; diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/pks_and_sks.rs b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/pks_and_sks.rs new file mode 100644 index 00000000000..7fb7fd4d422 --- /dev/null +++ b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/pks_and_sks.rs @@ -0,0 +1,437 @@ +use crate::vault::api::{ + CspVault, ExternalPublicKeyError, LocalPublicKeyError, NodeKeysError, NodeKeysErrors, + PksAndSksContainsErrors, SecretKeyError, +}; +use crate::vault::test_utils::public_key_store::{ + generate_all_keys, generate_idkg_dealing_encryption_key_pair, NODE_1, NOT_AFTER, +}; +use crate::ExternalPublicKeys; +use assert_matches::assert_matches; +use ic_types::crypto::CurrentNodePublicKeys; +use ic_types_test_utils::ids::node_test_id; +use std::sync::Arc; + +pub fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_one_idkg_key( + csp_vault: Arc, +) { + let current_node_public_keys = generate_all_keys(&csp_vault); + assert!(csp_vault + .pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)) + .is_ok()); +} + +pub fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys( + csp_vault: Arc, +) { + let current_node_public_keys = generate_all_keys(&csp_vault); + let _second_idkg_pk = generate_idkg_dealing_encryption_key_pair(&csp_vault); + let _third_idkg_pk = generate_idkg_dealing_encryption_key_pair(&csp_vault); + assert!(csp_vault + .pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)) + .is_ok()); +} + +pub fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_with_multiple_idkg_keys_and_external_key_not_first_in_vector( + csp_vault: Arc, +) { + let _initial_node_public_keys = generate_all_keys(&csp_vault); + let _second_idkg_pk = generate_idkg_dealing_encryption_key_pair(&csp_vault); + let current_node_public_keys = csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys"); + let _third_idkg_pk = generate_idkg_dealing_encryption_key_pair(&csp_vault); + assert!(csp_vault + .pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)) + .is_ok()); +} + +pub fn should_return_success_for_pks_and_sks_contains_if_all_keys_match_where_idkg_keys_have_different_timestamps( + csp_vault: Arc, +) { + let _current_node_public_keys = generate_all_keys(&csp_vault); + let mut external_public_keys = convert_to_external_public_keys( + csp_vault + .current_node_public_keys_with_timestamps() + .expect("error getting current node public keys with timestamp"), + ); + external_public_keys + .idkg_dealing_encryption_public_key + .timestamp = external_public_keys + .idkg_dealing_encryption_public_key + .timestamp + .expect("timestamp of generated iDKG dealing encryption key is none") + .checked_add(42); + assert!(csp_vault.pks_and_sks_contains(external_public_keys).is_ok()); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_no_keys_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let _current_node_public_keys = generate_all_keys(&csp_vault); + let shadow_node_public_keys = generate_all_keys(&shadow_csp_vault); + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(shadow_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + committee_signing_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + tls_certificate_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + dkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + idkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_node_signing_key_does_not_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + current_node_public_keys.node_signing_public_key = { + let _ = shadow_csp_vault + .gen_node_signing_key_pair() + .expect("Failed to generate node signing key pair"); + shadow_csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys") + .node_signing_public_key + }; + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_committee_signing_key_does_not_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + current_node_public_keys.committee_signing_public_key = { + let _ = shadow_csp_vault + .gen_committee_signing_key_pair() + .expect("Failed to generate committee signing key pair"); + shadow_csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys") + .committee_signing_public_key + }; + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_dkg_dealing_encryption_key_does_not_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + current_node_public_keys.dkg_dealing_encryption_public_key = { + let _ = shadow_csp_vault + .gen_dealing_encryption_key_pair(node_test_id(NODE_1)) + .expect("Failed to generate dkg dealing encryption signing key pair"); + shadow_csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys") + .dkg_dealing_encryption_public_key + }; + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + idkg_dealing_encryption_key_error: None, + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_tls_certificate_does_not_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + current_node_public_keys.tls_certificate = { + let _ = shadow_csp_vault + .gen_tls_key_pair(node_test_id(NODE_1), NOT_AFTER) + .expect("Failed to generate tks certificate"); + shadow_csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys") + .tls_certificate + }; + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_idkg_dealing_encryption_key_does_not_match( + csp_vault: Arc, + shadow_csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + current_node_public_keys.idkg_dealing_encryption_public_key = { + let _ = generate_idkg_dealing_encryption_key_pair(&shadow_csp_vault); + shadow_csp_vault + .current_node_public_keys() + .expect("Failed to get current node public keys") + .idkg_dealing_encryption_public_key + }; + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: None, + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::NotFound), + }), + })) + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_external_node_signing_key_is_malformed( + csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + if let Some(node_signing_public_key) = &mut current_node_public_keys.node_signing_public_key { + node_signing_public_key.key_value = b"malformed key".to_vec(); + } else { + panic!("Node signing key missing"); + } + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: Some(NodeKeysError { + external_public_key_error: Some(ExternalPublicKeyError(malformed_error)), + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::CannotComputeKeyId), + }), + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) if malformed_error.contains("Malformed Ed25519 public key") + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_external_committee_signing_key_is_malformed( + csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + if let Some(committee_signing_public_key) = + &mut current_node_public_keys.committee_signing_public_key + { + committee_signing_public_key.key_value = b"malformed key".to_vec(); + } else { + panic!("Committee signing key missing"); + } + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: Some(NodeKeysError { + external_public_key_error: Some(ExternalPublicKeyError(malformed_error)), + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::CannotComputeKeyId), + }), + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) if malformed_error.contains("Malformed MultiBls12_381 public key") + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_external_dkg_dealing_encryption_key_is_malformed( + csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + if let Some(dkg_dealing_encryption_public_key) = + &mut current_node_public_keys.dkg_dealing_encryption_public_key + { + dkg_dealing_encryption_public_key.key_value = b"malformed key".to_vec(); + } else { + panic!("DKG dealing encryption key missing"); + } + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: Some(ExternalPublicKeyError(malformed_error)), + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::CannotComputeKeyId), + }), + idkg_dealing_encryption_key_error: None, + })) if malformed_error.contains("Malformed public key") + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_external_tls_certificate_is_malformed( + csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + if let Some(tls_certificate) = &mut current_node_public_keys.tls_certificate { + tls_certificate.certificate_der = b"malformed certificate".to_vec(); + } else { + panic!("TLS certificate missing"); + } + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: Some(NodeKeysError { + external_public_key_error: Some(ExternalPublicKeyError(malformed_error)), + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::CannotComputeKeyId), + }), + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: None, + })) if malformed_error.contains("Malformed certificate: TlsPublicKeyCertCreationError") + ); +} + +pub fn should_return_error_for_pks_and_sks_contains_if_external_idkg_dealing_encryption_key_is_malformed( + csp_vault: Arc, +) { + let mut current_node_public_keys = generate_all_keys(&csp_vault); + if let Some(idkg_dealing_encryption_public_key) = + &mut current_node_public_keys.idkg_dealing_encryption_public_key + { + idkg_dealing_encryption_public_key.key_value = b"malformed key".to_vec(); + } else { + panic!("iDKG dealing encryption key missing"); + } + + let result = + csp_vault.pks_and_sks_contains(convert_to_external_public_keys(current_node_public_keys)); + assert_matches!( + result, + Err(PksAndSksContainsErrors::NodeKeysErrors(NodeKeysErrors { + node_signing_key_error: None, + committee_signing_key_error: None, + tls_certificate_error: None, + dkg_dealing_encryption_key_error: None, + idkg_dealing_encryption_key_error: Some(NodeKeysError { + external_public_key_error: Some(ExternalPublicKeyError(malformed_error)), + local_public_key_error: Some(LocalPublicKeyError::Mismatch), + secret_key_error: Some(SecretKeyError::CannotComputeKeyId), + }), + })) if malformed_error.contains("Malformed public key: I-DKG dealing encryption key malformed") + ); +} + +pub(crate) fn convert_to_external_public_keys( + current_node_public_keys: CurrentNodePublicKeys, +) -> ExternalPublicKeys { + ExternalPublicKeys { + node_signing_public_key: current_node_public_keys + .node_signing_public_key + .expect("node signing public key missing"), + committee_signing_public_key: current_node_public_keys + .committee_signing_public_key + .expect("committee signing public key missing"), + tls_certificate: current_node_public_keys + .tls_certificate + .expect("tls certificate missing"), + dkg_dealing_encryption_public_key: current_node_public_keys + .dkg_dealing_encryption_public_key + .expect("dkg dealing encryption public key missing"), + idkg_dealing_encryption_public_key: current_node_public_keys + .idkg_dealing_encryption_public_key + .expect("idkg dealing encryption public key missing"), + } +} diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/public_key_store.rs b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/public_key_store.rs index cca4c9be859..a1d991bb297 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/public_key_store.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/test_utils/public_key_store.rs @@ -300,13 +300,16 @@ pub fn should_correctly_return_idkg_dealing_encryption_pubkeys_count_when_all_ot .expect("Error calling idkg_key_count"); assert_eq!(0, result); } -fn generate_idkg_dealing_encryption_key_pair(csp_vault: &Arc) -> MEGaPublicKey { + +pub(crate) fn generate_idkg_dealing_encryption_key_pair( + csp_vault: &Arc, +) -> MEGaPublicKey { csp_vault .idkg_gen_dealing_encryption_key_pair() .expect("Failed to generate IDkg dealing encryption keys") } -fn generate_all_keys(csp_vault: &Arc) -> CurrentNodePublicKeys { +pub(crate) fn generate_all_keys(csp_vault: &Arc) -> CurrentNodePublicKeys { let _node_signing_pk = csp_vault .gen_node_signing_key_pair() .expect("Failed to generate node signing key pair"); diff --git a/rs/crypto/node_key_generation/src/lib.rs b/rs/crypto/node_key_generation/src/lib.rs index 9eaa6aa9a8b..076f1c7a6be 100644 --- a/rs/crypto/node_key_generation/src/lib.rs +++ b/rs/crypto/node_key_generation/src/lib.rs @@ -4,6 +4,9 @@ use ic_crypto_internal_csp::api::{ CspCreateMEGaKeyError, CspSecretKeyStoreChecker, NodePublicKeyData, }; use ic_crypto_internal_csp::key_id::KeyId; +use ic_crypto_internal_csp::keygen::utils::{ + mega_public_key_from_proto, MEGaPublicKeyFromProtoError, +}; use ic_crypto_internal_csp::types::{CspPop, CspPublicKey}; use ic_crypto_internal_csp::Csp; use ic_crypto_internal_csp::{public_key_store, CryptoServiceProvider}; @@ -11,7 +14,7 @@ use ic_crypto_tls_interfaces::TlsPublicKeyCert; use ic_crypto_utils_basic_sig::conversions as basicsig_conversions; use ic_protobuf::crypto::v1::NodePublicKeys; use ic_protobuf::registry::crypto::v1::PublicKey as PublicKeyProto; -use ic_protobuf::registry::crypto::v1::{AlgorithmId as AlgorithmIdProto, X509PublicKeyCert}; +use ic_protobuf::registry::crypto::v1::X509PublicKeyCert; use ic_types::crypto::{AlgorithmId, CryptoError, CryptoResult, CurrentNodePublicKeys}; use ic_types::NodeId; use std::path::Path; @@ -21,7 +24,6 @@ use tempfile::TempDir; use ic_crypto_internal_csp::types::conversions::CspPopFromPublicKeyProtoError; use ic_crypto_internal_logmon::metrics::CryptoMetrics; -use ic_crypto_internal_threshold_sig_ecdsa::{EccCurveType, MEGaPublicKey}; use ic_crypto_internal_types::encrypt::forward_secure::{ CspFsEncryptionPop, CspFsEncryptionPublicKey, }; @@ -184,34 +186,6 @@ pub fn get_node_keys_or_generate_if_missing( } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MEGaPublicKeyFromProtoError { - UnsupportedAlgorithm { - algorithm_id: Option, - }, - MalformedPublicKey { - key_bytes: Vec, - }, -} - -/// Deserialize a Protobuf public key to a MEGaPublicKey. -pub fn mega_public_key_from_proto( - proto: &PublicKeyProto, -) -> Result { - let curve_type = match AlgorithmIdProto::from_i32(proto.algorithm) { - Some(AlgorithmIdProto::MegaSecp256k1) => Ok(EccCurveType::K256), - alg_id => Err(MEGaPublicKeyFromProtoError::UnsupportedAlgorithm { - algorithm_id: alg_id, - }), - }?; - - MEGaPublicKey::deserialize(curve_type, &proto.key_value).map_err(|_| { - MEGaPublicKeyFromProtoError::MalformedPublicKey { - key_bytes: proto.key_value.clone(), - } - }) -} - /// Checks whether this crypto component has complete local key material, i.e. /// whether the public key store contains the required public keys, and whether /// the secret key store contains the required secret keys. diff --git a/rs/crypto/node_key_generation/src/tests.rs b/rs/crypto/node_key_generation/src/tests.rs index 3b1fd8472dc..7ea1b0369a5 100644 --- a/rs/crypto/node_key_generation/src/tests.rs +++ b/rs/crypto/node_key_generation/src/tests.rs @@ -494,6 +494,7 @@ mod tls { mod idkg { use super::*; use crate::IDkgDealingEncryptionKeysGenerationError; + use ic_protobuf::registry::crypto::v1::AlgorithmId as AlgorithmIdProto; use std::fs; use std::fs::Permissions; use std::os::unix::fs::PermissionsExt; diff --git a/rs/crypto/node_key_generation/tests/tests.rs b/rs/crypto/node_key_generation/tests/tests.rs index bcfb6a24bb8..60bd35fead6 100644 --- a/rs/crypto/node_key_generation/tests/tests.rs +++ b/rs/crypto/node_key_generation/tests/tests.rs @@ -2,12 +2,12 @@ use assert_matches::assert_matches; use ic_base_types::NodeId; use ic_config::crypto::CryptoConfig; use ic_crypto::{CryptoComponent, CryptoComponentFatClient}; +use ic_crypto_internal_csp::keygen::utils::{ + mega_public_key_from_proto, MEGaPublicKeyFromProtoError, +}; use ic_crypto_internal_csp::Csp; use ic_crypto_internal_csp_test_utils::remote_csp_vault::start_new_remote_csp_vault_server_in_temp_dir; -use ic_crypto_node_key_generation::{ - derive_node_id, get_node_keys_or_generate_if_missing, mega_public_key_from_proto, - MEGaPublicKeyFromProtoError, -}; +use ic_crypto_node_key_generation::{derive_node_id, get_node_keys_or_generate_if_missing}; use ic_interfaces::crypto::KeyManager; use ic_logger::replica_logger::no_op_logger; use ic_metrics::MetricsRegistry; diff --git a/rs/crypto/src/common/test_utils/mockall_csp.rs b/rs/crypto/src/common/test_utils/mockall_csp.rs index 0ca13bd3704..9dace408a22 100644 --- a/rs/crypto/src/common/test_utils/mockall_csp.rs +++ b/rs/crypto/src/common/test_utils/mockall_csp.rs @@ -41,8 +41,7 @@ use ic_types::crypto::canister_threshold_sig::error::{ }; use ic_types::crypto::canister_threshold_sig::ExtendedDerivationPath; use ic_types::crypto::threshold_sig::ni_dkg::NiDkgId; -use ic_types::crypto::CurrentNodePublicKeys; -use ic_types::crypto::{AlgorithmId, CryptoError, CryptoResult}; +use ic_types::crypto::{AlgorithmId, CryptoError, CryptoResult, CurrentNodePublicKeys}; use ic_types::{NodeId, NodeIndex, NumberOfNodes, Randomness}; use mockall::predicate::*; use mockall::*; diff --git a/rs/crypto/src/keygen/mod.rs b/rs/crypto/src/keygen/mod.rs index 91481a226ff..1266d199414 100644 --- a/rs/crypto/src/keygen/mod.rs +++ b/rs/crypto/src/keygen/mod.rs @@ -8,6 +8,9 @@ use crate::{key_from_registry, CryptoComponentFatClient}; use ic_crypto_internal_csp::api::CspSecretKeyStoreChecker; use ic_crypto_internal_csp::key_id::KeyId; use ic_crypto_internal_csp::keygen::utils::idkg_dealing_encryption_pk_to_proto; +use ic_crypto_internal_csp::keygen::utils::{ + mega_public_key_from_proto, MEGaPublicKeyFromProtoError, +}; use ic_crypto_internal_csp::types::conversions::CspPopFromPublicKeyProtoError; use ic_crypto_internal_csp::types::{CspPop, CspPublicKey}; use ic_crypto_internal_csp::CryptoServiceProvider; @@ -17,7 +20,6 @@ use ic_crypto_internal_logmon::metrics::{ use ic_crypto_internal_types::encrypt::forward_secure::{ CspFsEncryptionPop, CspFsEncryptionPublicKey, }; -use ic_crypto_node_key_generation::{mega_public_key_from_proto, MEGaPublicKeyFromProtoError}; use ic_crypto_tls_interfaces::TlsPublicKeyCert; use ic_interfaces::crypto::{ CurrentNodePublicKeysError, IDkgDealingEncryptionKeyRotationError, diff --git a/rs/crypto/src/keygen/tests.rs b/rs/crypto/src/keygen/tests.rs index 6548f091edc..fcf70a45a77 100644 --- a/rs/crypto/src/keygen/tests.rs +++ b/rs/crypto/src/keygen/tests.rs @@ -9,6 +9,7 @@ use ic_crypto_internal_tls::keygen::generate_tls_key_pair_der; use ic_crypto_temp_crypto::TempCryptoBuilder; use ic_crypto_temp_crypto::{EcdsaSubnetConfig, NodeKeysToGenerate, TempCryptoComponent}; use ic_crypto_test_utils_keygen::{add_public_key_to_registry, add_tls_cert_to_registry}; +use ic_crypto_tls_interfaces::TlsPublicKeyCert; use ic_interfaces_registry::RegistryClient; use ic_interfaces_registry_mocks::MockRegistryClient; use ic_registry_client_fake::FakeRegistryClient; @@ -20,113 +21,121 @@ use rand::SeedableRng; use rand_chacha::ChaChaRng; use std::sync::Arc; -#[test] -fn should_collect_correctly_key_count_metrics_for_all_keys() { - let crypto_component = TempCryptoComponent::builder() - .with_keys(NodeKeysToGenerate::all()) - .build(); - let key_counts = crypto_component - .collect_key_count_metrics(REGISTRY_VERSION_1) - .expect("Failed to collect key count metrics"); - assert_eq!(5, key_counts.get_pk_registry()); - assert_eq!(5, key_counts.get_pk_local()); - assert_eq!(5, key_counts.get_sk_local()); - assert_eq!(1, key_counts.get_idkg_pks_local()); -} +mod key_counts { + use super::*; -#[test] -fn should_collect_correctly_key_count_metrics_for_only_node_signing_key() { - let crypto_component = TempCryptoComponent::builder() - .with_keys(NodeKeysToGenerate::only_node_signing_key()) - .build(); - let key_counts = crypto_component - .collect_key_count_metrics(REGISTRY_VERSION_1) - .expect("Failed to collect key count metrics"); - assert_eq!(1, key_counts.get_pk_registry()); - assert_eq!(1, key_counts.get_pk_local()); - assert_eq!(1, key_counts.get_sk_local()); - assert_eq!(0, key_counts.get_idkg_pks_local()); -} + const ALL_KEYS_PRESENT: u8 = 5; -#[test] -fn should_count_correctly_inconsistent_numbers_of_node_signing_keys() { - let registry_data = Arc::new(ProtoRegistryDataProvider::new()); - let registry_client = Arc::new(FakeRegistryClient::new(Arc::clone(®istry_data) as Arc<_>)); - let crypto_component = TempCryptoComponent::builder() - .with_keys(NodeKeysToGenerate::all()) - .with_registry_client_and_data( - Arc::clone(®istry_client) as Arc<_>, - Arc::clone(®istry_data) as Arc<_>, - ) - .build(); + #[test] + fn should_collect_correctly_key_count_metrics_for_all_keys() { + let crypto_component = TempCryptoComponent::builder() + .with_keys(NodeKeysToGenerate::all()) + .build(); + let key_counts = crypto_component + .collect_key_count_metrics(REGISTRY_VERSION_1) + .expect("Failed to collect key count metrics"); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_registry()); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_local()); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_sk_local()); + assert_eq!(1, key_counts.get_idkg_pks_local()); + } - let node_signing_pk_without_corresponding_secret_key = { - let mut nspk = crypto_component - .current_node_public_keys() - .expect("Failed to retrieve node public keys") - .node_signing_public_key - .unwrap(); - nspk.key_value[0] ^= 0xff; // flip some bits - nspk - }; - - add_public_key_to_registry( - node_signing_pk_without_corresponding_secret_key, - crypto_component.get_node_id(), - KeyPurpose::NodeSigning, - Arc::clone(®istry_data), - REGISTRY_VERSION_2, - ); - registry_client.reload(); - - let key_counts = crypto_component - .collect_key_count_metrics(REGISTRY_VERSION_2) - .expect("Failed to collect key count metrics"); - assert_eq!(5, key_counts.get_pk_registry()); - assert_eq!(5, key_counts.get_pk_local()); - assert_eq!(4, key_counts.get_sk_local()); - assert_eq!(1, key_counts.get_idkg_pks_local()); -} + #[test] + fn should_collect_correctly_key_count_metrics_for_only_node_signing_key() { + let crypto_component = TempCryptoComponent::builder() + .with_keys(NodeKeysToGenerate::only_node_signing_key()) + .build(); + let key_counts = crypto_component + .collect_key_count_metrics(REGISTRY_VERSION_1) + .expect("Failed to collect key count metrics"); + assert_eq!(1, key_counts.get_pk_registry()); + assert_eq!(1, key_counts.get_pk_local()); + assert_eq!(1, key_counts.get_sk_local()); + assert_eq!(0, key_counts.get_idkg_pks_local()); + } -#[test] -fn should_count_correctly_inconsistent_numbers_of_tls_certificates() { - let registry_data = Arc::new(ProtoRegistryDataProvider::new()); - let registry_client = Arc::new(FakeRegistryClient::new(Arc::clone(®istry_data) as Arc<_>)); - let crypto_component = TempCryptoComponent::builder() - .with_keys(NodeKeysToGenerate::all()) - .with_registry_client_and_data( - Arc::clone(®istry_client) as Arc<_>, - Arc::clone(®istry_data) as Arc<_>, - ) - .build(); - - let tls_cert_without_corresponding_secret_key = { - let mut csprng = ChaChaRng::from_seed([9u8; 32]); - let not_after = Asn1Time::days_from_now(31).expect("unable to create Asn1Time"); - let common_name = "another_common_name"; - let (x509_cert, _key_pair) = - generate_tls_key_pair_der(&mut csprng, common_name, ¬_after) - .expect("error generating TLS key pair"); - TlsPublicKeyCert::new_from_der(x509_cert.bytes) - .expect("generated X509 certificate has malformed DER encoding") - .to_proto() - }; - - add_tls_cert_to_registry( - tls_cert_without_corresponding_secret_key, - crypto_component.get_node_id(), - Arc::clone(®istry_data), - REGISTRY_VERSION_2, - ); - registry_client.reload(); - - let key_counts = crypto_component - .collect_key_count_metrics(REGISTRY_VERSION_2) - .expect("Failed to collect key count metrics"); - assert_eq!(5, key_counts.get_pk_registry()); - assert_eq!(5, key_counts.get_pk_local()); - assert_eq!(4, key_counts.get_sk_local()); - assert_eq!(1, key_counts.get_idkg_pks_local()); + #[test] + fn should_count_correctly_inconsistent_numbers_of_node_signing_keys() { + let registry_data = Arc::new(ProtoRegistryDataProvider::new()); + let registry_client = + Arc::new(FakeRegistryClient::new(Arc::clone(®istry_data) as Arc<_>)); + let crypto_component = TempCryptoComponent::builder() + .with_keys(NodeKeysToGenerate::all()) + .with_registry_client_and_data( + Arc::clone(®istry_client) as Arc<_>, + Arc::clone(®istry_data) as Arc<_>, + ) + .build(); + + let node_signing_pk_without_corresponding_secret_key = { + let mut nspk = crypto_component + .current_node_public_keys() + .expect("Failed to retrieve node public keys") + .node_signing_public_key + .unwrap(); + nspk.key_value[0] ^= 0xff; // flip some bits + nspk + }; + + add_public_key_to_registry( + node_signing_pk_without_corresponding_secret_key, + crypto_component.get_node_id(), + KeyPurpose::NodeSigning, + Arc::clone(®istry_data), + REGISTRY_VERSION_2, + ); + registry_client.reload(); + + let key_counts = crypto_component + .collect_key_count_metrics(REGISTRY_VERSION_2) + .expect("Failed to collect key count metrics"); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_registry()); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_local()); + assert_eq!(ALL_KEYS_PRESENT - 1, key_counts.get_sk_local()); + assert_eq!(1, key_counts.get_idkg_pks_local()); + } + + #[test] + fn should_count_correctly_inconsistent_numbers_of_tls_certificates() { + let registry_data = Arc::new(ProtoRegistryDataProvider::new()); + let registry_client = + Arc::new(FakeRegistryClient::new(Arc::clone(®istry_data) as Arc<_>)); + let crypto_component = TempCryptoComponent::builder() + .with_keys(NodeKeysToGenerate::all()) + .with_registry_client_and_data( + Arc::clone(®istry_client) as Arc<_>, + Arc::clone(®istry_data) as Arc<_>, + ) + .build(); + + let tls_cert_without_corresponding_secret_key = { + let mut csprng = ChaChaRng::from_seed([9u8; 32]); + let not_after = Asn1Time::days_from_now(31).expect("unable to create Asn1Time"); + let common_name = "another_common_name"; + let (x509_cert, _key_pair) = + generate_tls_key_pair_der(&mut csprng, common_name, ¬_after) + .expect("error generating TLS key pair"); + TlsPublicKeyCert::new_from_der(x509_cert.bytes) + .expect("generated X509 certificate has malformed DER encoding") + .to_proto() + }; + + add_tls_cert_to_registry( + tls_cert_without_corresponding_secret_key, + crypto_component.get_node_id(), + Arc::clone(®istry_data), + REGISTRY_VERSION_2, + ); + registry_client.reload(); + + let key_counts = crypto_component + .collect_key_count_metrics(REGISTRY_VERSION_2) + .expect("Failed to collect key count metrics"); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_registry()); + assert_eq!(ALL_KEYS_PRESENT, key_counts.get_pk_local()); + assert_eq!(ALL_KEYS_PRESENT - 1, key_counts.get_sk_local()); + assert_eq!(1, key_counts.get_idkg_pks_local()); + } } mod rotate_idkg_dealing_encryption_keys { diff --git a/rs/crypto/src/sign/canister_threshold_sig/idkg/utils.rs b/rs/crypto/src/sign/canister_threshold_sig/idkg/utils.rs index f3ed8856f3d..5dbd47951fb 100644 --- a/rs/crypto/src/sign/canister_threshold_sig/idkg/utils.rs +++ b/rs/crypto/src/sign/canister_threshold_sig/idkg/utils.rs @@ -3,8 +3,10 @@ mod errors; pub use errors::*; +use ic_crypto_internal_csp::keygen::utils::{ + mega_public_key_from_proto, MEGaPublicKeyFromProtoError, +}; use ic_crypto_internal_threshold_sig_ecdsa::{IDkgDealingInternal, MEGaPublicKey}; -use ic_crypto_node_key_generation::{mega_public_key_from_proto, MEGaPublicKeyFromProtoError}; use ic_interfaces_registry::RegistryClient; use ic_protobuf::registry::crypto::v1::PublicKey; use ic_registry_client_helpers::crypto::CryptoRegistry; diff --git a/rs/crypto/tests/integration_test.rs b/rs/crypto/tests/integration_test.rs index 747d7df71c0..5e49cb48d35 100644 --- a/rs/crypto/tests/integration_test.rs +++ b/rs/crypto/tests/integration_test.rs @@ -267,6 +267,7 @@ fn should_fail_check_keys_with_registry_if_tls_cert_is_missing_in_registry() { .add_generated_node_signing_key_to_registry() .add_generated_committee_signing_key_to_registry() .add_generated_dkg_dealing_enc_key_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -329,6 +330,7 @@ fn should_fail_check_keys_with_registry_if_node_signing_secret_key_is_missing() .add_generated_committee_signing_key_to_registry() .add_generated_dkg_dealing_enc_key_to_registry() .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -356,6 +358,7 @@ fn should_fail_check_keys_with_registry_if_committee_member_secret_key_is_missin .with_committee_signing_key_in_registry(committee_pk_without_corresponding_secret_key) .add_generated_dkg_dealing_enc_key_to_registry() .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -382,19 +385,12 @@ fn should_fail_check_keys_with_registry_if_dkg_dealing_encryption_secret_key_is_ dkg_dealing_enc_public_key_with_valid_pop_but_without_secret_part_in_store, ) .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); - assert!(result.is_err()); - let is_secret_key_err = match result.unwrap_err() { - CryptoError::SecretKeyNotFound { algorithm, .. } => { - algorithm == AlgorithmId::Groth20_Bls12_381 - } - _ => false, - }; - - assert!(is_secret_key_err); + assert_matches!(result, Err(CryptoError::SecretKeyNotFound { algorithm, key_id: _ }) if algorithm == AlgorithmId::Groth20_Bls12_381); } #[test] @@ -410,6 +406,7 @@ fn should_fail_check_keys_with_registry_if_dkg_dealing_encryption_pubkey_is_malf .add_generated_committee_signing_key_to_registry() .with_dkg_dealing_enc_key_in_registry(dkg_dealing_encryption_pubkey_with_malformed_pubkey) .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -439,6 +436,7 @@ fn should_fail_check_keys_with_registry_if_dkg_dealing_encryption_pop_is_missing .add_generated_committee_signing_key_to_registry() .with_dkg_dealing_enc_key_in_registry(dkg_dealing_encryption_pubkey_with_missing_pop) .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -468,6 +466,7 @@ fn should_fail_check_keys_with_registry_if_dkg_dealing_encryption_pop_is_empty() .add_generated_committee_signing_key_to_registry() .with_dkg_dealing_enc_key_in_registry(dkg_dealing_encryption_pubkey_with_empty_pop) .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -499,6 +498,7 @@ fn should_fail_check_keys_with_registry_if_dkg_dealing_encryption_pop_is_malform .add_generated_committee_signing_key_to_registry() .with_dkg_dealing_enc_key_in_registry(dkg_dealing_encryption_pubkey_with_malformed_pop) .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -528,6 +528,7 @@ fn should_fail_check_keys_with_registry_if_committee_key_pop_is_missing() { .with_committee_signing_key_in_registry(committee_key_without_pop) .add_generated_dkg_dealing_enc_key_to_registry() .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -557,6 +558,7 @@ fn should_fail_check_keys_with_registry_if_committee_key_pop_is_empty() { .with_committee_signing_key_in_registry(committee_key_with_empty_pop) .add_generated_dkg_dealing_enc_key_to_registry() .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -586,6 +588,7 @@ fn should_fail_check_keys_with_registry_if_committee_key_pop_is_malformed() { .with_committee_signing_key_in_registry(committee_key_with_malformed_pop) .add_generated_dkg_dealing_enc_key_to_registry() .add_generated_tls_cert_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .build(NODE_ID, REG_V1); let result = crypto.get().check_keys_with_registry(REG_V1); @@ -613,6 +616,7 @@ fn should_fail_check_keys_with_registry_if_tls_cert_secret_key_is_missing() { .add_generated_node_signing_key_to_registry() .add_generated_committee_signing_key_to_registry() .add_generated_dkg_dealing_enc_key_to_registry() + .add_generated_idkg_dealing_enc_key_to_registry() .with_tls_cert_in_registry(cert_without_corresponding_secret_key.clone()) .build(NODE_ID, REG_V1);