main/keyring/aws_kms_multi_keyring_example.rs
1// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4/*
5This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings.
6
7A multi-keyring is a keyring that consists of one or more individual keyrings of the
8same or a different type. The effect is like using several keyrings in a series.
9When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its
10keyrings can decrypt that data.
11
12When you create a multi-keyring to encrypt data, you designate one of the keyrings as
13the generator keyring. All other keyrings are known as child keyrings. The generator keyring
14generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the
15child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext
16key and one encrypted data key for each wrapping key in the multi-keyring. If you create a
17multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt.
18If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates
19and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring,
20and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key.
21
22When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted
23data keys. The keyrings are called in the order that they are specified in the multi-keyring.
24Processing stops as soon as any key in any keyring can decrypt an encrypted data key.
25
26This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA
27with an encryption context. This example also includes some sanity checks for demonstration:
281. Ciphertext and plaintext data are not the same
292. Decryption of ciphertext is possible using the multi_keyring,
30 and every one of the keyrings from the multi_keyring separately
313. All decrypted plaintext value match EXAMPLE_DATA
32These sanity checks are for demonstration in the example only. You do not need these in your code.
33
34This example creates a multi_keyring using a KMS keyring as generator keyring and
35another KMS keyring as a child keyring.
36
37For more information on how to use Multi keyrings, see
38https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html
39
40For more information on KMS Key identifiers, see
41https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
42*/
43
44use aws_config::Region;
45use aws_esdk::client as esdk_client;
46use aws_esdk::material_providers::client as mpl_client;
47use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
48use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
49use std::collections::HashMap;
50
51pub async fn encrypt_and_decrypt_with_keyring(
52 example_data: &str,
53 default_region_kms_key_id: &str,
54 second_region_kms_key_id: &str,
55 default_region: String,
56 second_region: String,
57) -> Result<(), crate::BoxError> {
58 // 1. Instantiate the encryption SDK client.
59 // This builds the default client with the RequireEncryptRequireDecrypt commitment policy,
60 // which enforces that this client only encrypts using committing algorithm suites and enforces
61 // that this client will only decrypt encrypted messages that were created with a committing
62 // algorithm suite.
63 let esdk_config = AwsEncryptionSdkConfig::builder().build()?;
64 let esdk_client = esdk_client::Client::from_conf(esdk_config)?;
65
66 // 2. Create encryption context.
67 // Remember that your encryption context is NOT SECRET.
68 // For more information, see
69 // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
70 let encryption_context = HashMap::from([
71 ("encryption".to_string(), "context".to_string()),
72 ("is not".to_string(), "secret".to_string()),
73 ("but adds".to_string(), "useful metadata".to_string()),
74 (
75 "that can help you".to_string(),
76 "be confident that".to_string(),
77 ),
78 (
79 "the data you are handling".to_string(),
80 "is what you think it is".to_string(),
81 ),
82 ]);
83
84 // 3. Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys.
85 // Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring.
86 let mpl_config = MaterialProvidersConfig::builder().build()?;
87 let mpl = mpl_client::Client::from_conf(mpl_config)?;
88
89 let kms_multi_keyring = mpl
90 .create_aws_kms_multi_keyring()
91 .generator(default_region_kms_key_id)
92 .kms_key_ids(vec![second_region_kms_key_id.to_string()])
93 .send()
94 .await?;
95
96 // 4. Encrypt the data with the encryption_context
97 let plaintext = example_data.as_bytes();
98
99 let encryption_response = esdk_client
100 .encrypt()
101 .plaintext(plaintext)
102 .keyring(kms_multi_keyring.clone())
103 .encryption_context(encryption_context.clone())
104 .send()
105 .await?;
106
107 let ciphertext = encryption_response
108 .ciphertext
109 .expect("Unable to unwrap ciphertext from encryption response");
110
111 // 5. Demonstrate that the ciphertext and plaintext are different.
112 // (This is an example for demonstration; you do not need to do this in your own code.)
113 assert_ne!(
114 ciphertext,
115 aws_smithy_types::Blob::new(plaintext),
116 "Ciphertext and plaintext data are the same. Invalid encryption"
117 );
118
119 // 6a. Decrypt your encrypted data using the same multi_keyring you used on encrypt.
120 let decryption_response_multi_keyring = esdk_client
121 .decrypt()
122 .ciphertext(ciphertext.clone())
123 .keyring(kms_multi_keyring)
124 // Provide the encryption context that was supplied to the encrypt method
125 .encryption_context(encryption_context.clone())
126 .send()
127 .await?;
128
129 let decrypted_plaintext_multi_keyring = decryption_response_multi_keyring
130 .plaintext
131 .expect("Unable to unwrap plaintext from decryption response");
132
133 // 6b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
134 // (This is an example for demonstration; you do not need to do this in your own code.)
135 assert_eq!(
136 decrypted_plaintext_multi_keyring,
137 aws_smithy_types::Blob::new(plaintext),
138 "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
139 );
140
141 // Because you used a multi_keyring on Encrypt, you can use either of the two
142 // kms keyrings individually to decrypt the data.
143
144 // 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the
145 // `default_region_kms_key_id` directly.
146 // (This is an example for demonstration; you do not need to do this in your own code.)
147
148 // 7a. Create a client for KMS for the default region.
149 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
150 let default_region_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config)
151 .region(Region::new(default_region))
152 .build();
153 let default_region_kms_client = aws_sdk_kms::Client::from_conf(default_region_kms_config);
154
155 // 7b. Create KMS keyring
156 let default_region_kms_keyring = mpl
157 .create_aws_kms_keyring()
158 .kms_key_id(default_region_kms_key_id)
159 .kms_client(default_region_kms_client)
160 .send()
161 .await?;
162
163 // 7c. Decrypt your encrypted data using the default_region_kms_keyring.
164 let decryption_response_default_region_kms_keyring = esdk_client
165 .decrypt()
166 .ciphertext(ciphertext.clone())
167 .keyring(default_region_kms_keyring)
168 // Provide the encryption context that was supplied to the encrypt method
169 .encryption_context(encryption_context.clone())
170 .send()
171 .await?;
172
173 let decrypted_plaintext_default_region_kms_keyring =
174 decryption_response_default_region_kms_keyring
175 .plaintext
176 .expect("Unable to unwrap plaintext from decryption response");
177
178 // 7d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
179 // (This is an example for demonstration; you do not need to do this in your own code.)
180 assert_eq!(
181 decrypted_plaintext_default_region_kms_keyring,
182 aws_smithy_types::Blob::new(plaintext),
183 "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
184 );
185
186 // 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the
187 // `second_region_kms_key_id` directly.
188 // (This is an example for demonstration; you do not need to do this in your own code.)
189
190 // 8a. Create a client for KMS for the second region.
191 let second_region_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config)
192 .region(Region::new(second_region))
193 .build();
194 let second_region_kms_client = aws_sdk_kms::Client::from_conf(second_region_kms_config);
195
196 // 8b. Create KMS keyring
197 let second_region_kms_keyring = mpl
198 .create_aws_kms_keyring()
199 .kms_key_id(second_region_kms_key_id)
200 .kms_client(second_region_kms_client)
201 .send()
202 .await?;
203
204 // 8c. Decrypt your encrypted data using the second_region_kms_keyring.
205 let decryption_response_second_region_kms_keyring = esdk_client
206 .decrypt()
207 .ciphertext(ciphertext)
208 .keyring(second_region_kms_keyring)
209 // Provide the encryption context that was supplied to the encrypt method
210 .encryption_context(encryption_context)
211 .send()
212 .await?;
213
214 let decrypted_plaintext_second_region_kms_keyring =
215 decryption_response_second_region_kms_keyring
216 .plaintext
217 .expect("Unable to unwrap plaintext from decryption response");
218
219 // 8d. Demonstrate that the decrypted plaintext is identical to the original plaintext.
220 // (This is an example for demonstration; you do not need to do this in your own code.)
221 assert_eq!(
222 decrypted_plaintext_second_region_kms_keyring,
223 aws_smithy_types::Blob::new(plaintext),
224 "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
225 );
226
227 println!("KMS Multi Keyring Example Completed Successfully");
228
229 Ok(())
230}
231
232#[tokio::test(flavor = "multi_thread")]
233pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> {
234 // Test function for encrypt and decrypt using the AWS KMS Multi Keyring example
235 use crate::example_utils::utils;
236
237 let default_region: String = "us-west-2".to_string();
238 let second_region: String = "eu-central-1".to_string();
239
240 encrypt_and_decrypt_with_keyring(
241 utils::TEST_EXAMPLE_DATA,
242 utils::TEST_DEFAULT_KMS_KEY_ID,
243 utils::TEST_SECOND_REGION_KMS_KEY_ID,
244 default_region,
245 second_region,
246 )
247 .await?;
248
249 Ok(())
250}