main/keyring/
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 Multi Keyring
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,
30and 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 a raw AES keyring
35as a child keyring. You can use different combinations of keyrings in the multi_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_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::AesWrappingAlg;
48use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
49use rand::TryRngCore;
50use std::collections::HashMap;
51
52pub async fn encrypt_and_decrypt_with_keyring(
53    example_data: &str,
54    kms_key_id: &str,
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 a KMS client.
65    let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
66    let kms_client = aws_sdk_kms::Client::new(&sdk_config);
67
68    // 3. Create encryption context.
69    // Remember that your encryption context is NOT SECRET.
70    // For more information, see
71    // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
72    let encryption_context = HashMap::from([
73        ("encryption".to_string(), "context".to_string()),
74        ("is not".to_string(), "secret".to_string()),
75        ("but adds".to_string(), "useful metadata".to_string()),
76        (
77            "that can help you".to_string(),
78            "be confident that".to_string(),
79        ),
80        (
81            "the data you are handling".to_string(),
82            "is what you think it is".to_string(),
83        ),
84    ]);
85
86    // 4. Create a KMS keyring
87    let mpl_config = MaterialProvidersConfig::builder().build()?;
88    let mpl = mpl_client::Client::from_conf(mpl_config)?;
89
90    let kms_keyring = mpl
91        .create_aws_kms_keyring()
92        .kms_key_id(kms_key_id)
93        .kms_client(kms_client)
94        .send()
95        .await?;
96
97    // 5. Create a raw AES keyring to additionally encrypt under as child_keyring
98
99    // The key namespace and key name are defined by you.
100    // and are used by the Raw AES keyring to determine
101    // whether it should attempt to decrypt an encrypted data key.
102    // For more information, see
103    // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
104    let key_namespace: &str = "my-key-namespace";
105    let key_name: &str = "my-aes-key-name";
106
107    // Generate a 256-bit AES key to use with your raw AES keyring.
108    // In practice, you should get this key from a secure key management system such as an HSM.
109    let aes_key_bytes = generate_aes_key_bytes();
110
111    let raw_aes_keyring = mpl
112        .create_raw_aes_keyring()
113        .key_name(key_name)
114        .key_namespace(key_namespace)
115        .wrapping_key(aes_key_bytes)
116        .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
117        .send()
118        .await?;
119
120    // 6. Create a multi_keyring that consists of the previously created keyrings.
121    // When using this multi_keyring to encrypt data, either `kms_keyring` or
122    // `raw_aes_keyring` (or a multi_keyring containing either) may be used to decrypt the data.
123    let multi_keyring = mpl
124        .create_multi_keyring()
125        .generator(kms_keyring.clone())
126        .child_keyrings(vec![raw_aes_keyring.clone()])
127        .send()
128        .await?;
129
130    // 7. Encrypt the data with the encryption_context
131    let plaintext = example_data.as_bytes();
132
133    let encryption_response = esdk_client
134        .encrypt()
135        .plaintext(plaintext)
136        .keyring(multi_keyring.clone())
137        .encryption_context(encryption_context.clone())
138        .send()
139        .await?;
140
141    let ciphertext = encryption_response
142        .ciphertext
143        .expect("Unable to unwrap ciphertext from encryption response");
144
145    // 8. Demonstrate that the ciphertext and plaintext are different.
146    // (This is an example for demonstration; you do not need to do this in your own code.)
147    assert_ne!(
148        ciphertext,
149        aws_smithy_types::Blob::new(plaintext),
150        "Ciphertext and plaintext data are the same. Invalid encryption"
151    );
152
153    // 9a. Decrypt your encrypted data using the same multi_keyring you used on encrypt.
154    let decryption_response_multi_keyring = esdk_client
155        .decrypt()
156        .ciphertext(ciphertext.clone())
157        .keyring(multi_keyring)
158        // Provide the encryption context that was supplied to the encrypt method
159        .encryption_context(encryption_context.clone())
160        .send()
161        .await?;
162
163    let decrypted_plaintext_multi_keyring = decryption_response_multi_keyring
164        .plaintext
165        .expect("Unable to unwrap plaintext from decryption response");
166
167    // 9b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
168    // (This is an example for demonstration; you do not need to do this in your own code.)
169    assert_eq!(
170        decrypted_plaintext_multi_keyring,
171        aws_smithy_types::Blob::new(plaintext),
172        "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
173    );
174
175    // Because you used a multi_keyring on Encrypt, you can use either the
176    // `kms_keyring` or `raw_aes_keyring` individually to decrypt the data.
177
178    // 10. Demonstrate that you can successfully decrypt data using just the `kms_keyring`
179    // directly.
180    // (This is an example for demonstration; you do not need to do this in your own code.)
181
182    // 10a. Decrypt your encrypted data using the kms_keyring.
183    let decryption_response_kms_keyring = esdk_client
184        .decrypt()
185        .ciphertext(ciphertext.clone())
186        .keyring(kms_keyring)
187        // Provide the encryption context that was supplied to the encrypt method
188        .encryption_context(encryption_context.clone())
189        .send()
190        .await?;
191
192    let decrypted_plaintext_kms_keyring = decryption_response_kms_keyring
193        .plaintext
194        .expect("Unable to unwrap plaintext from decryption response");
195
196    // 10b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
197    // (This is an example for demonstration; you do not need to do this in your own code.)
198    assert_eq!(
199        decrypted_plaintext_kms_keyring,
200        aws_smithy_types::Blob::new(plaintext),
201        "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
202    );
203
204    // 11. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring`
205    // directly.
206    // (This is an example for demonstration; you do not need to do this in your own code.)
207
208    // 11a. Decrypt your encrypted data using the raw_aes_keyring.
209    let decryption_response_raw_aes_keyring = esdk_client
210        .decrypt()
211        .ciphertext(ciphertext)
212        .keyring(raw_aes_keyring)
213        // Provide the encryption context that was supplied to the encrypt method
214        .encryption_context(encryption_context)
215        .send()
216        .await?;
217
218    let decrypted_plaintext_raw_aes_keyring = decryption_response_raw_aes_keyring
219        .plaintext
220        .expect("Unable to unwrap plaintext from decryption response");
221
222    // 11b. Demonstrate that the decrypted plaintext is identical to the original plaintext.
223    // (This is an example for demonstration; you do not need to do this in your own code.)
224    assert_eq!(
225        decrypted_plaintext_raw_aes_keyring,
226        aws_smithy_types::Blob::new(plaintext),
227        "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
228    );
229
230    println!("Multi Keyring Example Completed Successfully");
231
232    Ok(())
233}
234
235fn generate_aes_key_bytes() -> Vec<u8> {
236    // This example returns a random AES key.
237    // In practice, you should not generate this key in your code, and should instead
238    //     retrieve this key from a secure key management system (e.g. HSM).
239    // This key is created here for example purposes only and should not be used for any other purpose.
240    let mut random_bytes = [0u8; 32];
241    rand::rngs::OsRng.try_fill_bytes(&mut random_bytes).unwrap();
242
243    random_bytes.to_vec()
244}
245
246#[tokio::test(flavor = "multi_thread")]
247pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> {
248    // Test function for encrypt and decrypt using the Multi Keyring example
249    use crate::example_utils::utils;
250
251    encrypt_and_decrypt_with_keyring(utils::TEST_EXAMPLE_DATA, utils::TEST_DEFAULT_KMS_KEY_ID)
252        .await?;
253
254    Ok(())
255}