main/keyring/aws_kms_mrk_discovery_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 MRK (multi-region key) Discovery Multi Keyring
6
7AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings.
8
9The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys.
10
11When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt
12any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or
13has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt
14permission on the AWS KMS MRK.
15
16The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring
17for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring
18can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring,
19the encrypt operation fails.
20
21The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to
22create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs).
23This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA
24with an encryption context. This encrypted ciphertext is then decrypted using an
25MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration:
261. Ciphertext and plaintext data are not the same
272. Decrypted plaintext value matches EXAMPLE_DATA
28These sanity checks are for demonstration in the example only. You do not need these in your code.
29
30For information about using multi-Region keys with the AWS Encryption SDK, see
31https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
32
33For more info on KMS MRKs (multi-region keys), see the KMS documentation:
34https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
35
36For more information on how to use KMS Discovery keyrings, see
37https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery
38
39For more information on KMS Key identifiers, see
40https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
41*/
42
43use aws_config::Region;
44use aws_esdk::client as esdk_client;
45use aws_esdk::material_providers::client as mpl_client;
46use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
47use aws_esdk::material_providers::types::DiscoveryFilter;
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 mrk_key_id_encrypt: &str,
54 mrk_encrypt_region: String,
55 aws_account_id: &str,
56 aws_regions: Vec<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 the keyring that determines how your data keys are protected.
85 // Although this example highlights Discovery keyrings, Discovery keyrings cannot
86 // be used to encrypt, so for encryption we create an MRK keyring without discovery mode.
87 let mpl_config = MaterialProvidersConfig::builder().build()?;
88 let mpl = mpl_client::Client::from_conf(mpl_config)?;
89
90 // Create a KMS client in the first region
91 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
92 let encrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config)
93 .region(Region::new(mrk_encrypt_region))
94 .build();
95 let encrypt_kms_client = aws_sdk_kms::Client::from_conf(encrypt_kms_config);
96
97 // Create a keyring that will encrypt your data, using a KMS MRK in the first region.
98 let encrypt_kms_keyring = mpl
99 .create_aws_kms_mrk_keyring()
100 .kms_key_id(mrk_key_id_encrypt)
101 .kms_client(encrypt_kms_client)
102 .send()
103 .await?;
104
105 // 4. Encrypt the data with the encryption_context using the encrypt_keyring.
106 let plaintext = example_data.as_bytes();
107
108 let encryption_response = esdk_client
109 .encrypt()
110 .plaintext(plaintext)
111 .keyring(encrypt_kms_keyring)
112 .encryption_context(encryption_context.clone())
113 .send()
114 .await?;
115
116 let ciphertext = encryption_response
117 .ciphertext
118 .expect("Unable to unwrap ciphertext from encryption response");
119
120 // 5. Demonstrate that the ciphertext and plaintext are different.
121 // (This is an example for demonstration; you do not need to do this in your own code.)
122 assert_ne!(
123 ciphertext,
124 aws_smithy_types::Blob::new(plaintext),
125 "Ciphertext and plaintext data are the same. Invalid encryption"
126 );
127
128 // 6. Now create a MRK Discovery Multi Keyring to use for decryption.
129 // We'll add a discovery filter to limit the set of encrypted data keys
130 // we are willing to decrypt to only ones created by KMS keys in select
131 // accounts and the partition `aws`.
132 // MRK Discovery keyrings also filter encrypted data keys by the region
133 // the keyring is created with.
134 let discovery_filter = DiscoveryFilter::builder()
135 .account_ids(vec![aws_account_id.to_string()])
136 .partition("aws".to_string())
137 .build()?;
138
139 // This is a Multi Keyring composed of Discovery Keyrings.
140 // There is a keyring for every region in `regions`.
141 // All the keyrings have the same Discovery Filter.
142 // Each keyring has its own KMS Client, which is created for the keyring's region.
143 let discovery_multi_keyring = mpl
144 .create_aws_kms_mrk_discovery_multi_keyring()
145 .regions(aws_regions)
146 .discovery_filter(discovery_filter)
147 .send()
148 .await?;
149
150 // 7. Decrypt your encrypted data using the discovery multi keyring.
151 // On Decrypt, the header of the encrypted message (ciphertext) will be parsed.
152 // The header contains the Encrypted Data Keys (EDKs), which, if the EDK
153 // was encrypted by a KMS Keyring, includes the KMS Key ARN.
154 // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption
155 // is successful.
156 // Since every member of the Multi Keyring is a Discovery Keyring:
157 // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region.
158 // For each filtered EDK, the keyring will attempt decryption with the keyring's client.
159 // All of this is done serially, until a success occurs or all keyrings have failed
160 // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt
161 // Multi Region Keys (MRKs) and regular KMS Keys.
162 let decryption_response = esdk_client
163 .decrypt()
164 .ciphertext(ciphertext)
165 .keyring(discovery_multi_keyring)
166 // Provide the encryption context that was supplied to the encrypt method
167 .encryption_context(encryption_context)
168 .send()
169 .await?;
170
171 let decrypted_plaintext = decryption_response
172 .plaintext
173 .expect("Unable to unwrap plaintext from decryption response");
174
175 // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext.
176 // (This is an example for demonstration; you do not need to do this in your own code.)
177 assert_eq!(
178 decrypted_plaintext,
179 aws_smithy_types::Blob::new(plaintext),
180 "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
181 );
182
183 println!("KMS MRK Discovery Multi Keyring Example Completed Successfully");
184
185 Ok(())
186}
187
188#[tokio::test(flavor = "multi_thread")]
189pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> {
190 // Test function for encrypt and decrypt using the AWS KMS MRK Discovery Multi Keyring example
191 use crate::example_utils::utils;
192
193 let mrk_encrypt_region: String = "us-east-1".to_string();
194 let aws_regions: Vec<String> = vec!["us-east-1".to_string(), "us-west-2".to_string()];
195
196 encrypt_and_decrypt_with_keyring(
197 utils::TEST_EXAMPLE_DATA,
198 utils::TEST_MRK_KEY_ID_US_EAST_1,
199 mrk_encrypt_region,
200 utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID,
201 aws_regions,
202 )
203 .await?;
204
205 Ok(())
206}