main/keyring/ecdh/ephemeral_raw_ecdh_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 Ephemeral Raw ECDH Keyring.
6
7This example takes in the recipient's public key located at
8EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT as a
9UTF8 PEM-encoded X.509 public key,
10and the Curve Specification where the key lies.
11
12This example loads ECC keys from PEM files with paths defined in
13 - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT
14
15If you do not provide these files, running this example through this
16class' main method will generate three files required for all raw ECDH examples
17EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER, EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT
18and EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT for you.
19In practice, users of this library should not generate new key pairs
20like this, and should instead retrieve an existing key from a secure
21key management system (e.g. an HSM).
22You may also provide your own key pair by placing PEM files in the
23directory where the example is run or modifying the paths in the code
24below. These files must be valid PEM encodings of the key pair as UTF-8
25encoded bytes. If you do provide your own key pair, or if a key pair
26already exists, this class' main method will not generate a new key pair.
27
28This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme.
29This configuration will always create a new key pair as the sender key pair for the key agreement operation.
30The ephemeral configuration can only encrypt data and CANNOT decrypt messages.
31
32This example creates an Ephemeral Raw ECDH Keyring and then encrypts a custom input EXAMPLE_DATA
33with an encryption context. This example also includes some sanity checks for demonstration:
341. Ciphertext and plaintext data are not the same
35These sanity checks are for demonstration in the example only. You do not need these in your code.
36
37For more information on this configuration see:
38https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey
39*/
40
41use crate::example_utils::utils::exists;
42use crate::example_utils::utils::write_raw_ecdh_ecc_keys;
43use crate::example_utils::utils::EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT;
44use aws_esdk::aws_cryptography_primitives::types::EcdhCurveSpec;
45use aws_esdk::client as esdk_client;
46use aws_esdk::material_providers::client as mpl_client;
47use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
48use aws_esdk::material_providers::types::EphemeralPrivateKeyToStaticPublicKeyInput;
49use aws_esdk::material_providers::types::RawEcdhStaticConfigurations;
50use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
51use pem::parse;
52use std::collections::HashMap;
53use std::path::Path;
54
55pub async fn encrypt_with_keyring(
56 example_data: &str,
57 ecdh_curve_spec: EcdhCurveSpec,
58) -> Result<(), crate::BoxError> {
59 // 1. Instantiate the encryption SDK client.
60 // This builds the default client with the RequireEncryptRequireDecrypt commitment policy,
61 // which enforces that this client only encrypts using committing algorithm suites and enforces
62 // that this client will only decrypt encrypted messages that were created with a committing
63 // algorithm suite.
64 let esdk_config = AwsEncryptionSdkConfig::builder().build()?;
65 let esdk_client = esdk_client::Client::from_conf(esdk_config)?;
66
67 // 2. Create encryption context.
68 // Remember that your encryption context is NOT SECRET.
69 // For more information, see
70 // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
71 let encryption_context = HashMap::from([
72 ("encryption".to_string(), "context".to_string()),
73 ("is not".to_string(), "secret".to_string()),
74 ("but adds".to_string(), "useful metadata".to_string()),
75 (
76 "that can help you".to_string(),
77 "be confident that".to_string(),
78 ),
79 (
80 "the data you are handling".to_string(),
81 "is what you think it is".to_string(),
82 ),
83 ]);
84
85 // 3. You may provide your own ECC keys in the files located at
86 // - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT
87
88 // If you do not provide these files, running this example through this
89 // class' main method will generate three files required for all raw ECDH examples
90 // EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER, EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT
91 // and EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT for you.
92
93 // Do not use these files for any other purpose.
94 if should_generate_new_ecc_key_pair_ephemeral_raw_ecdh()? {
95 write_raw_ecdh_ecc_keys(ecdh_curve_spec)?;
96 }
97
98 // 4. Load keys from UTF-8 encoded PEM files.
99
100 // Load public key from UTF-8 encoded PEM files into a DER encoded public key.
101 let public_key_file_content =
102 std::fs::read_to_string(Path::new(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT))?;
103 let parsed_public_key_file_content = parse(public_key_file_content)?;
104 let public_key_recipient_utf8_bytes = parsed_public_key_file_content.contents();
105
106 // 5. Create the EphemeralPrivateKeyToStaticPublicKeyInput
107 let ephemeral_raw_ecdh_static_configuration_input =
108 EphemeralPrivateKeyToStaticPublicKeyInput::builder()
109 // Must be a UTF8 DER-encoded X.509 public key
110 .recipient_public_key(public_key_recipient_utf8_bytes)
111 .build()?;
112
113 let ephemeral_raw_ecdh_static_configuration =
114 RawEcdhStaticConfigurations::EphemeralPrivateKeyToStaticPublicKey(
115 ephemeral_raw_ecdh_static_configuration_input,
116 );
117
118 // 6. Create the Ephemeral Raw ECDH keyring.
119 let mpl_config = MaterialProvidersConfig::builder().build()?;
120 let mpl = mpl_client::Client::from_conf(mpl_config)?;
121
122 // Create the keyring.
123 // This keyring uses an ephemeral configuration. This configuration will always create a new
124 // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only
125 // encrypt data and CANNOT decrypt messages.
126 let ephemeral_raw_ecdh_keyring = mpl
127 .create_raw_ecdh_keyring()
128 .curve_spec(ecdh_curve_spec)
129 .key_agreement_scheme(ephemeral_raw_ecdh_static_configuration)
130 .send()
131 .await?;
132
133 // 7. Encrypt the data with the encryption_context
134
135 // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair
136 // used as the sender is ephemeral. This means that at decrypt time it does not have
137 // the private key that corresponds to the public key that is stored on the message.
138 let plaintext = example_data.as_bytes();
139
140 let encryption_response = esdk_client
141 .encrypt()
142 .plaintext(plaintext)
143 .keyring(ephemeral_raw_ecdh_keyring)
144 .encryption_context(encryption_context)
145 .send()
146 .await?;
147
148 let ciphertext = encryption_response
149 .ciphertext
150 .expect("Unable to unwrap ciphertext from encryption response");
151
152 // 8. Demonstrate that the ciphertext and plaintext are different.
153 // (This is an example for demonstration; you do not need to do this in your own code.)
154 assert_ne!(
155 ciphertext,
156 aws_smithy_types::Blob::new(plaintext),
157 "Ciphertext and plaintext data are the same. Invalid encryption"
158 );
159
160 println!("Ephemeral Raw ECDH Keyring Example Completed Successfully");
161
162 Ok(())
163}
164
165fn should_generate_new_ecc_key_pair_ephemeral_raw_ecdh() -> Result<bool, String> {
166 // If key already exists: do not overwrite existing key
167 if exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) {
168 Ok(false)
169 }
170 // If file is not present, generate a new key pair
171 else {
172 Ok(true)
173 }
174}
175
176#[tokio::test(flavor = "multi_thread")]
177pub async fn test_encrypt_with_keyring() -> Result<(), crate::BoxError2> {
178 // Test function for encrypt using the Ephemeral Raw ECDH Keyring example
179 use crate::example_utils::utils;
180
181 encrypt_with_keyring(utils::TEST_EXAMPLE_DATA, EcdhCurveSpec::EccNistP256).await?;
182
183 Ok(())
184}