Skip to main content

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