mithril_aggregator/services/snapshotter/ancillary_signer/
with_gcp_kms.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use gcloud_kms::client::google_cloud_auth::credentials::CredentialsFile;
4use gcloud_kms::client::{Client as GcpKmsClient, ClientConfig};
5use gcloud_kms::grpc::kms::v1::AsymmetricSignRequest;
6use slog::{debug, Logger};
7
8use mithril_common::crypto_helper::ManifestSignature;
9use mithril_common::entities::AncillaryFilesManifest;
10use mithril_common::StdResult;
11
12use crate::services::ancillary_signer::{AncillarySigner, GcpCryptoKeyVersionResourceName};
13
14/// Ancillary signer that uses a key stored in a Google Cloud Platform KMS account to sign
15/// ancillary manifests.
16pub struct AncillarySignerWithGcpKms {
17    kms_client: GcpKmsClient,
18    resource_name: GcpCryptoKeyVersionResourceName,
19    logger: Logger,
20}
21
22impl AncillarySignerWithGcpKms {
23    /// Creates a new instance of `AncillarySignerWithGcpKms`
24    pub async fn new(
25        resource_name: GcpCryptoKeyVersionResourceName,
26        credentials_json_env_var: String,
27        logger: Logger,
28    ) -> StdResult<Self> {
29        const BASE_ERROR_CONTEXT: &str =
30            "Failed to create Google Cloud KMS client for Ancillary manifest signing";
31        let unparsed_credentials_json = std::env::var(&credentials_json_env_var)
32            .with_context(|| {
33                format!("Environment variable `{credentials_json_env_var}` must be set",)
34            })
35            .with_context(|| BASE_ERROR_CONTEXT)?;
36        let credentials_file = CredentialsFile::new_from_str(&unparsed_credentials_json)
37            .await
38            .with_context(|| BASE_ERROR_CONTEXT)?;
39        let config = ClientConfig::default()
40            .with_credentials(credentials_file)
41            .await
42            .with_context(|| BASE_ERROR_CONTEXT)?;
43        let kms_client = GcpKmsClient::new(config)
44            .await
45            .with_context(|| BASE_ERROR_CONTEXT)?;
46
47        Ok(AncillarySignerWithGcpKms {
48            kms_client,
49            resource_name,
50            logger,
51        })
52    }
53}
54
55#[async_trait]
56impl AncillarySigner for AncillarySignerWithGcpKms {
57    async fn compute_ancillary_manifest_signature(
58        &self,
59        manifest: &AncillaryFilesManifest,
60    ) -> StdResult<ManifestSignature> {
61        debug!(
62            self.logger,
63            ">> AncillarySignerWithGcpKms::compute_ancillary_manifest_signature"
64        );
65        let data = manifest.compute_hash();
66        let signature_response = self
67            .kms_client
68            .asymmetric_sign(
69                AsymmetricSignRequest {
70                    name: self.resource_name.to_string(),
71                    digest: None,
72                    digest_crc32c: None,
73                    data,
74                    data_crc32c: None,
75                },
76                None,
77            )
78            .await
79            .with_context(|| "Failed to sign the ancillary manifest with GCP KMS".to_string())?;
80
81        ManifestSignature::from_bytes(&signature_response.signature).with_context(|| {
82            "Failed to convert the signature response from GCP KMS to a ManifestSignature"
83                .to_string()
84        })
85    }
86}