kms_aead/providers/
gcp_kms_encryption.rs

1use rsb_derive::*;
2
3use crate::*;
4use async_trait::async_trait;
5use gcloud_sdk::google::cloud::kms::v1::key_management_service_client::KeyManagementServiceClient;
6use gcloud_sdk::google::cloud::kms::v1::*;
7use gcloud_sdk::proto_ext::kms::*;
8use gcloud_sdk::*;
9use tracing::*;
10
11use crate::ring_encryption::RingAeadEncryption;
12use rvstruct::ValueStruct;
13use secret_vault_value::SecretValue;
14use tonic::metadata::MetadataValue;
15
16#[derive(Debug, Clone, Eq, PartialEq, Builder)]
17pub struct GcpKmsKeyRef {
18    pub google_project_id: String,
19    pub location: String,
20    pub key_ring: String,
21    pub key: String,
22}
23
24impl GcpKmsKeyRef {
25    fn to_google_ref(&self) -> String {
26        format!(
27            "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}",
28            self.google_project_id, self.location, self.key_ring, self.key
29        )
30    }
31}
32
33#[derive(Debug, Clone, Builder)]
34pub struct GcpKmsProviderOptions {
35    #[default = "false"]
36    pub use_kms_random_gen: bool,
37}
38
39#[derive(Clone)]
40pub struct GcpKmsProvider {
41    client: GoogleApi<KeyManagementServiceClient<GoogleAuthMiddleware>>,
42    gcp_key_ref: GcpKmsKeyRef,
43    options: GcpKmsProviderOptions,
44}
45
46impl GcpKmsProvider {
47    pub async fn new(kms_key_ref: &GcpKmsKeyRef) -> KmsAeadResult<Self> {
48        Self::with_options(kms_key_ref, GcpKmsProviderOptions::new()).await
49    }
50    pub async fn with_options(
51        kms_key_ref: &GcpKmsKeyRef,
52        options: GcpKmsProviderOptions,
53    ) -> KmsAeadResult<Self> {
54        let client: GoogleApi<KeyManagementServiceClient<GoogleAuthMiddleware>> =
55            GoogleApi::from_function(
56                KeyManagementServiceClient::new,
57                "https://cloudkms.googleapis.com",
58                None,
59            )
60            .await?;
61
62        Ok(Self::with_client(kms_key_ref, options, client))
63    }
64    pub fn with_client(
65        kms_key_ref: &GcpKmsKeyRef,
66        options: GcpKmsProviderOptions,
67        client: GoogleApi<KeyManagementServiceClient<GoogleAuthMiddleware>>,
68    ) -> Self {
69        debug!(
70            "Initialising Google KMS envelope encryption for {}",
71            kms_key_ref.to_google_ref()
72        );
73
74        Self {
75            gcp_key_ref: kms_key_ref.clone(),
76            client,
77            options,
78        }
79    }
80}
81
82#[async_trait]
83impl KmsAeadRingEncryptionProvider for GcpKmsProvider {
84    async fn encrypt_data_encryption_key(
85        &self,
86        encryption_key: &DataEncryptionKey,
87    ) -> KmsAeadResult<EncryptedDataEncryptionKey> {
88        let mut encrypt_request = tonic::Request::new(EncryptRequest {
89            name: self.gcp_key_ref.to_google_ref(),
90            plaintext: secret_vault_value::SecretValue::new(
91                hex::encode(encryption_key.value().ref_sensitive_value().as_slice()).into_bytes(),
92            ),
93            ..Default::default()
94        });
95
96        encrypt_request.metadata_mut().insert(
97            "x-goog-request-params",
98            MetadataValue::<tonic::metadata::Ascii>::try_from(format!(
99                "name={}",
100                self.gcp_key_ref.to_google_ref()
101            ))
102            .unwrap(),
103        );
104
105        let encrypt_response = self.client.get().encrypt(encrypt_request).await?;
106
107        Ok(EncryptedDataEncryptionKey::from(
108            encrypt_response.into_inner().ciphertext,
109        ))
110    }
111
112    async fn decrypt_data_encryption_key(
113        &self,
114        encrypted_key: &EncryptedDataEncryptionKey,
115    ) -> KmsAeadResult<DataEncryptionKey> {
116        let mut decrypt_request = tonic::Request::new(DecryptRequest {
117            name: self.gcp_key_ref.to_google_ref(),
118            ciphertext: encrypted_key.value().clone(),
119            ..Default::default()
120        });
121
122        decrypt_request.metadata_mut().insert(
123            "x-goog-request-params",
124            MetadataValue::<tonic::metadata::Ascii>::try_from(format!(
125                "name={}",
126                self.gcp_key_ref.to_google_ref()
127            ))
128            .unwrap(),
129        );
130
131        let decrypt_response = self.client.get().decrypt(decrypt_request).await?;
132
133        Ok(DataEncryptionKey::from(
134            secret_vault_value::SecretValue::new(
135                hex::decode(
136                    decrypt_response
137                        .into_inner()
138                        .plaintext
139                        .ref_sensitive_value(),
140                )
141                .unwrap(),
142            ),
143        ))
144    }
145
146    async fn generate_encryption_key(
147        &self,
148        aead_encryption: &RingAeadEncryption,
149    ) -> KmsAeadResult<DataEncryptionKey> {
150        if self.options.use_kms_random_gen {
151            let gcp_global_location = format!(
152                "projects/{}/locations/{}",
153                self.gcp_key_ref.google_project_id, self.gcp_key_ref.location
154            );
155
156            let mut gen_random_bytes_req = tonic::Request::new(GenerateRandomBytesRequest {
157                location: gcp_global_location.clone(),
158                length_bytes: aead_encryption.algo.key_len() as i32,
159                protection_level: ProtectionLevel::Hsm.into(),
160            });
161
162            gen_random_bytes_req.metadata_mut().insert(
163                "x-goog-request-params",
164                MetadataValue::<tonic::metadata::Ascii>::try_from(format!(
165                    "name={gcp_global_location}"
166                ))
167                .unwrap(),
168            );
169
170            let gen_random_bytes_resp = self
171                .client
172                .get()
173                .generate_random_bytes(gen_random_bytes_req)
174                .await?;
175            Ok(DataEncryptionKey::from(SecretValue::from(
176                gen_random_bytes_resp.into_inner().data,
177            )))
178        } else {
179            aead_encryption.generate_data_encryption_key()
180        }
181    }
182}