kms_aead/providers/
gcp_kms_encryption.rs1use 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}