Skip to main content

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}