main/keyring/aws_kms_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 Discovery Multi Keyring and demonstrates decryption
6using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings.
7
8The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring
9for AWS KMS multi-Region keys. For information about using multi-Region keys with the
10AWS Encryption SDK, see
11https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
12
13Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data.
14If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt
15operation fails.
16
17When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt
18any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or
19has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt
20permission on the AWS KMS key.
21
22This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA
23with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi
24keyring. This example also includes some sanity checks for demonstration:
251. Ciphertext and plaintext data are not the same
262. Decrypted plaintext value matches EXAMPLE_DATA
27These sanity checks are for demonstration in the example only. You do not need these in your code.
28
29For more information on how to use KMS Discovery keyrings, see
30https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery
31
32For more information on KMS Key identifiers, see
33https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
34*/
35
36use aws_esdk::client as esdk_client;
37use aws_esdk::material_providers::client as mpl_client;
38use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
39use aws_esdk::material_providers::types::DiscoveryFilter;
40use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
41use std::collections::HashMap;
42use std::vec::Vec;
43
44pub async fn encrypt_and_decrypt_with_keyring(
45 example_data: &str,
46 kms_key_id: &str,
47 aws_account_id: &str,
48 aws_regions: Vec<String>,
49) -> Result<(), crate::BoxError> {
50 // 1. Instantiate the encryption SDK client.
51 // This builds the default client with the RequireEncryptRequireDecrypt commitment policy,
52 // which enforces that this client only encrypts using committing algorithm suites and enforces
53 // that this client will only decrypt encrypted messages that were created with a committing
54 // algorithm suite.
55 let esdk_config = AwsEncryptionSdkConfig::builder().build()?;
56 let esdk_client = esdk_client::Client::from_conf(esdk_config)?;
57
58 // 2. Create a KMS client.
59 let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
60 let kms_client = aws_sdk_kms::Client::new(&sdk_config);
61
62 // 3. Create encryption context.
63 // Remember that your encryption context is NOT SECRET.
64 // For more information, see
65 // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
66 let encryption_context = HashMap::from([
67 ("encryption".to_string(), "context".to_string()),
68 ("is not".to_string(), "secret".to_string()),
69 ("but adds".to_string(), "useful metadata".to_string()),
70 (
71 "that can help you".to_string(),
72 "be confident that".to_string(),
73 ),
74 (
75 "the data you are handling".to_string(),
76 "is what you think it is".to_string(),
77 ),
78 ]);
79
80 // 4. Create the keyring that determines how your data keys are protected.
81 // Although this example highlights Discovery keyrings, Discovery keyrings cannot
82 // be used to encrypt, so for encryption we create a KMS keyring without discovery mode.
83 let mpl_config = MaterialProvidersConfig::builder().build()?;
84 let mpl = mpl_client::Client::from_conf(mpl_config)?;
85
86 let encrypt_kms_keyring = mpl
87 .create_aws_kms_keyring()
88 .kms_key_id(kms_key_id)
89 .kms_client(kms_client.clone())
90 .send()
91 .await?;
92
93 // 5. Encrypt the data with the encryption_context
94 let plaintext = example_data.as_bytes();
95
96 let encryption_response = esdk_client
97 .encrypt()
98 .plaintext(plaintext)
99 .keyring(encrypt_kms_keyring)
100 .encryption_context(encryption_context.clone())
101 .send()
102 .await?;
103
104 let ciphertext = encryption_response
105 .ciphertext
106 .expect("Unable to unwrap ciphertext from encryption response");
107
108 // 6. Demonstrate that the ciphertext and plaintext are different.
109 // (This is an example for demonstration; you do not need to do this in your own code.)
110 assert_ne!(
111 ciphertext,
112 aws_smithy_types::Blob::new(plaintext),
113 "Ciphertext and plaintext data are the same. Invalid encryption"
114 );
115
116 // 7. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter
117 // so that we limit the set of ciphertexts we are willing to decrypt to only ones
118 // created by KMS keys in our account and partition.
119 let discovery_filter = DiscoveryFilter::builder()
120 .account_ids(vec![aws_account_id.to_string()])
121 .partition("aws".to_string())
122 .build()?;
123
124 // This is a Multi Keyring composed of Discovery Keyrings.
125 // There is a keyring for every region in `regions`.
126 // All the keyrings have the same Discovery Filter.
127 // Each keyring has its own KMS Client, which is created for the keyring's region.
128 let discovery_multi_keyring = mpl
129 .create_aws_kms_discovery_multi_keyring()
130 .regions(aws_regions)
131 .discovery_filter(discovery_filter)
132 .send()
133 .await?;
134
135 // 8. On Decrypt, the header of the encrypted message (ciphertext) will be parsed.
136 // The header contains the Encrypted Data Keys (EDKs), which, if the EDK
137 // was encrypted by a KMS Keyring, includes the KMS Key ARN.
138 // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption
139 // is successful.
140 // Since every member of the Multi Keyring is a Discovery Keyring:
141 // Each Keyring will filter the EDKs by the Discovery Filter
142 // For the filtered EDKs, the keyring will try to decrypt it with the keyring's client.
143 // All of this is done serially, until a success occurs or all keyrings have
144 // failed all (filtered) EDKs.
145 // KMS Discovery Keyrings will attempt to decrypt Multi Region Keys (MRKs) and regular KMS Keys.
146 let decryption_response = esdk_client
147 .decrypt()
148 .ciphertext(ciphertext)
149 .keyring(discovery_multi_keyring)
150 // Provide the encryption context that was supplied to the encrypt method
151 .encryption_context(encryption_context)
152 .send()
153 .await?;
154
155 let decrypted_plaintext = decryption_response
156 .plaintext
157 .expect("Unable to unwrap plaintext from decryption response");
158
159 // 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
160 // (This is an example for demonstration; you do not need to do this in your own code.)
161 assert_eq!(
162 decrypted_plaintext,
163 aws_smithy_types::Blob::new(plaintext),
164 "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
165 );
166
167 println!("KMS Discovery Multi Keyring Example Completed Successfully");
168
169 Ok(())
170}
171
172#[tokio::test(flavor = "multi_thread")]
173pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> {
174 // Test function for encrypt and decrypt using the AWS KMS Discovery Multi Keyring example
175 use crate::example_utils::utils;
176
177 let aws_regions: Vec<String> = vec!["us-east-1".to_string(), "us-west-2".to_string()];
178
179 encrypt_and_decrypt_with_keyring(
180 utils::TEST_EXAMPLE_DATA,
181 utils::TEST_DEFAULT_KMS_KEY_ID,
182 utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID,
183 aws_regions,
184 )
185 .await?;
186
187 Ok(())
188}