Skip to main content

main/
limit_encrypted_data_keys_example.rs

1// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4/*
5Demonstrate limiting the number of Encrypted Data Keys [EDKs] allowed
6when encrypting or decrypting a message.
7Limiting encrypted data keys is most valuable when you are decrypting
8messages from an untrusted source.
9By default, the ESDK will allow up to 65,535 encrypted data keys.
10A malicious actor might construct an encrypted message with thousands of
11encrypted data keys, none of which can be decrypted.
12As a result, the AWS Encryption SDK would attempt to decrypt each
13encrypted data key until it exhausted the encrypted data keys in the message.
14
15For more information on limiting EDKs, see
16https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-limit-keys
17*/
18
19use aws_esdk::client as esdk_client;
20use aws_esdk::material_providers::client as mpl_client;
21use aws_esdk::material_providers::types::keyring::KeyringRef;
22use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
23use aws_esdk::material_providers::types::AesWrappingAlg;
24use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
25use aws_esdk::types::error::Error::AwsEncryptionSdkException;
26use rand::TryRngCore;
27use std::collections::HashMap;
28
29pub async fn encrypt_and_decrypt_with_keyring(
30    example_data: &str,
31    max_encrypted_data_keys: u16,
32) -> Result<(), crate::BoxError> {
33    // 1. Instantiate the encryption SDK client.
34    // This builds the default client with the RequireEncryptRequireDecrypt commitment policy,
35    // which enforces that this client only encrypts using committing algorithm suites and enforces
36    // that this client will only decrypt encrypted messages that were created with a committing
37    // algorithm suite.
38    // Also, set the EncryptionSDK's max_encrypted_data_keys parameter here
39    let esdk_config = AwsEncryptionSdkConfig::builder()
40        .max_encrypted_data_keys(max_encrypted_data_keys)
41        .build()?;
42    let esdk_client = esdk_client::Client::from_conf(esdk_config)?;
43
44    // 2. The key namespace and key name are defined by you.
45    // and are used by the Raw AES keyring to determine
46    // whether it should attempt to decrypt an encrypted data key.
47    // For more information, see
48    // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html
49    let key_namespace: &str = "my-key-namespace";
50    let key_name: &str = "my-aes-key-name";
51
52    // 3. Create encryption context.
53    // Remember that your encryption context is NOT SECRET.
54    // For more information, see
55    // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
56    let encryption_context = HashMap::from([
57        ("encryption".to_string(), "context".to_string()),
58        ("is not".to_string(), "secret".to_string()),
59        ("but adds".to_string(), "useful metadata".to_string()),
60        (
61            "that can help you".to_string(),
62            "be confident that".to_string(),
63        ),
64        (
65            "the data you are handling".to_string(),
66            "is what you think it is".to_string(),
67        ),
68    ]);
69
70    // 4. Generate `max_encrypted_data_keys` AES keyrings to use with your keyring.
71    // In practice, you should get this key from a secure key management system such as an HSM.
72    let mpl_config = MaterialProvidersConfig::builder().build()?;
73    let mpl = mpl_client::Client::from_conf(mpl_config)?;
74
75    let mut raw_aes_keyrings: Vec<KeyringRef> = vec![];
76
77    assert!(
78        max_encrypted_data_keys > 0,
79        "max_encrypted_data_keys MUST be greater than 0"
80    );
81
82    let mut i = 0;
83    while i < max_encrypted_data_keys {
84        let aes_key_bytes = generate_aes_key_bytes();
85
86        let raw_aes_keyring = mpl
87            .create_raw_aes_keyring()
88            .key_name(key_name)
89            .key_namespace(key_namespace)
90            .wrapping_key(aes_key_bytes)
91            .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
92            .send()
93            .await?;
94
95        raw_aes_keyrings.push(raw_aes_keyring);
96        i += 1;
97    }
98
99    // 5. Create a Multi Keyring with `max_encrypted_data_keys` AES Keyrings
100    let generator_keyring = raw_aes_keyrings.remove(0);
101
102    let multi_keyring = mpl
103        .create_multi_keyring()
104        .generator(generator_keyring)
105        .child_keyrings(raw_aes_keyrings)
106        .send()
107        .await?;
108
109    // 6. Encrypt the data with the encryption_context
110    let plaintext = example_data.as_bytes();
111
112    let encryption_response = esdk_client
113        .encrypt()
114        .plaintext(plaintext)
115        .keyring(multi_keyring.clone())
116        .encryption_context(encryption_context.clone())
117        .send()
118        .await?;
119
120    let ciphertext = encryption_response
121        .ciphertext
122        .expect("Unable to unwrap ciphertext from encryption response");
123
124    // 7. Demonstrate that the ciphertext and plaintext are different.
125    // (This is an example for demonstration; you do not need to do this in your own code.)
126    assert_ne!(
127        ciphertext,
128        aws_smithy_types::Blob::new(plaintext),
129        "Ciphertext and plaintext data are the same. Invalid encryption"
130    );
131
132    // 8. Decrypt your encrypted data using the same keyring you used on encrypt.
133    let decryption_response = esdk_client
134        .decrypt()
135        .ciphertext(ciphertext.clone())
136        .keyring(multi_keyring.clone())
137        // Provide the encryption context that was supplied to the encrypt method
138        .encryption_context(encryption_context.clone())
139        .send()
140        .await?;
141
142    let decrypted_plaintext = decryption_response
143        .plaintext
144        .expect("Unable to unwrap plaintext from decryption response");
145
146    // 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
147    // (This is an example for demonstration; you do not need to do this in your own code.)
148    assert_eq!(
149        decrypted_plaintext,
150        aws_smithy_types::Blob::new(plaintext),
151        "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"
152    );
153
154    // 10. Demonstrate that an EncryptionSDK with a lower MaxEncryptedDataKeys
155    // will fail to decrypt the encrypted message.
156    let esdk_config = AwsEncryptionSdkConfig::builder()
157        .max_encrypted_data_keys(max_encrypted_data_keys - 1)
158        .build()?;
159    let esdk_client_incorrect_max_encrypted_keys = esdk_client::Client::from_conf(esdk_config)?;
160
161    let decryption_response_incorrect_max_encrypted_keys = esdk_client_incorrect_max_encrypted_keys
162        .decrypt()
163        .ciphertext(ciphertext)
164        .keyring(multi_keyring)
165        // Provide the encryption context that was supplied to the encrypt method
166        .encryption_context(encryption_context)
167        .send()
168        .await;
169
170    match decryption_response_incorrect_max_encrypted_keys {
171        Ok(_) => panic!(
172            "Decrypt using discovery keyring with wrong AWS Account ID MUST \
173                            raise AwsCryptographicMaterialProvidersError"
174        ),
175        Err(AwsEncryptionSdkException { message: m }) => assert_eq!(
176            m,
177            "Ciphertext encrypted data keys exceed maxEncryptedDataKeys"
178        ),
179        _ => panic!("Unexpected error type"),
180    }
181
182    println!("Limit Encrypted Data Keys Example Completed Successfully");
183
184    Ok(())
185}
186
187fn generate_aes_key_bytes() -> Vec<u8> {
188    // This example returns a random AES key.
189    // In practice, you should not generate this key in your code, and should instead
190    //     retrieve this key from a secure key management system (e.g. HSM).
191    // This key is created here for example purposes only and should not be used for any other purpose.
192    let mut random_bytes = [0u8; 32];
193    rand::rngs::OsRng.try_fill_bytes(&mut random_bytes).unwrap();
194
195    random_bytes.to_vec()
196}
197
198#[tokio::test(flavor = "multi_thread")]
199pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> {
200    // Test function for encrypt and decrypt using the Limit Encrypted Data Keys example
201    use crate::example_utils::utils;
202
203    // max_encrypted_data_keys MUST be greater than 0
204    let max_encrypted_data_keys: u16 = 3;
205
206    encrypt_and_decrypt_with_keyring(utils::TEST_EXAMPLE_DATA, max_encrypted_data_keys).await?;
207
208    Ok(())
209}