@@ -36,3 +36,238 @@ pub const TEST_LOGICAL_KEY_STORE_NAME: &str = "KeyStoreDdbTable";
3636
3737pub const TEST_KEY_STORE_KMS_KEY_ID : & str =
3838 "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" ;
39+
40+ // ECDH Utils
41+ use aws_esdk:: aws_cryptography_primitives:: types:: EcdhCurveSpec ;
42+ use std:: path:: Path ;
43+ use std:: io:: Write ;
44+
45+ pub const TEST_KMS_ECDH_KEY_ID_P256_SENDER : & str =
46+ "arn:aws:kms:us-west-2:370957321024:key/eabdf483-6be2-4d2d-8ee4-8c2583d416e9" ;
47+ pub const TEST_KMS_ECDH_KEY_ID_P256_RECIPIENT : & str =
48+ "arn:aws:kms:us-west-2:370957321024:key/0265c8e9-5b6a-4055-8f70-63719e09fda5" ;
49+
50+ pub const EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER : & str = "RawEcdhKeyringExamplePrivateKeySender.pem" ;
51+ pub const EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT : & str = "RawEcdhKeyringExamplePrivateKeyRecipient.pem" ;
52+ pub const EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT : & str = "RawEcdhKeyringExamplePublicKeyRecipient.pem" ;
53+ pub const EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_SENDER : & str = "KmsEccKeyringExamplePublicKeySender.pem" ;
54+ pub const EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_RECIPIENT : & str = "KmsEccKeyringExamplePublicKeyRecipient.pem" ;
55+
56+ // Following are the helper functions for running ECDH examples
57+
58+ pub ( crate ) fn x962_to_x509 (
59+ public_key : & [ u8 ] ,
60+ nid : i32
61+ ) -> Result < Vec < u8 > , String > {
62+ use aws_lc_sys:: EC_POINT_new ;
63+ use aws_lc_sys:: EC_GROUP_new_by_curve_name ;
64+ use aws_lc_sys:: EC_POINT_oct2point ;
65+ use aws_lc_sys:: EC_KEY_new_by_curve_name ;
66+ use aws_lc_sys:: EC_KEY_set_public_key ;
67+ use aws_lc_sys:: EVP_PKEY_new ;
68+ use aws_lc_sys:: EVP_PKEY_assign_EC_KEY ;
69+ use aws_lc_sys:: EVP_PKEY_size ;
70+ use aws_lc_sys:: EVP_marshal_public_key ;
71+ use aws_lc_sys:: CBB_finish ;
72+ use aws_lc_sys:: CBB_init ;
73+ use aws_lc_sys:: CBB ;
74+ use aws_lc_sys:: OPENSSL_free ;
75+ use aws_lc_sys:: EVP_PKEY_free ;
76+ use aws_lc_sys:: EC_POINT_free ;
77+ use std:: ptr:: null_mut;
78+
79+ let ec_group = unsafe { EC_GROUP_new_by_curve_name ( nid) } ;
80+ let ec_point = unsafe { EC_POINT_new ( ec_group) } ;
81+
82+ if 1 != unsafe {
83+ EC_POINT_oct2point (
84+ ec_group,
85+ ec_point,
86+ public_key. as_ptr ( ) ,
87+ public_key. len ( ) ,
88+ null_mut ( ) ,
89+ )
90+ } {
91+ return Err ( "Error in EC_POINT_oct2point." . to_string ( ) ) ;
92+ }
93+
94+ let ec_key = unsafe { EC_KEY_new_by_curve_name ( nid) } ;
95+ if 1 != unsafe { EC_KEY_set_public_key ( ec_key, ec_point) } {
96+ return Err ( "Error in EC_KEY_set_public_key." . to_string ( ) ) ;
97+ }
98+
99+ let evp_pkey = unsafe { EVP_PKEY_new ( ) } ;
100+ if 1 != unsafe { EVP_PKEY_assign_EC_KEY ( evp_pkey, ec_key) } {
101+ return Err ( "Error in EVP_PKEY_assign_EC_KEY." . to_string ( ) ) ;
102+ }
103+
104+ let key_size_bytes: usize = unsafe { EVP_PKEY_size ( evp_pkey) } . try_into ( ) . unwrap ( ) ;
105+ let mut cbb: CBB = Default :: default ( ) ;
106+ unsafe { CBB_init ( & mut cbb as * mut CBB , key_size_bytes * 5 ) } ;
107+
108+ if 1 != unsafe { EVP_marshal_public_key ( & mut cbb, evp_pkey) } {
109+ return Err ( "Error in EVP_marshal_public_key in GetPublicKey." . to_string ( ) ) ;
110+ } ;
111+
112+ let mut out_data = null_mut :: < u8 > ( ) ;
113+ let mut out_len: usize = 0 ;
114+
115+ if 1 != unsafe { CBB_finish ( & mut cbb, & mut out_data, & mut out_len) } {
116+ return Err ( "Error in CBB_finish in GetPublicKey." . to_string ( ) ) ;
117+ } ;
118+ let slice = unsafe { std:: slice:: from_raw_parts ( out_data, out_len) } ;
119+ let slice = slice. to_vec ( ) ;
120+
121+ unsafe { OPENSSL_free ( out_data as * mut :: std:: os:: raw:: c_void ) } ;
122+ unsafe { EVP_PKEY_free ( evp_pkey) } ;
123+ unsafe { EC_POINT_free ( ec_point) } ;
124+ Ok ( slice)
125+ }
126+
127+ fn get_nid ( x : EcdhCurveSpec ) -> i32 {
128+ match x {
129+ EcdhCurveSpec :: EccNistP256 { } => aws_lc_sys:: NID_X9_62_prime256v1 ,
130+ EcdhCurveSpec :: EccNistP384 { } => aws_lc_sys:: NID_secp384r1 ,
131+ EcdhCurveSpec :: EccNistP521 { } => aws_lc_sys:: NID_secp521r1 ,
132+ EcdhCurveSpec :: Sm2 { } => panic ! ( "No SM2 in Rust" ) ,
133+ }
134+ }
135+
136+ fn get_alg ( x : EcdhCurveSpec ) -> & ' static aws_lc_rs:: agreement:: Algorithm {
137+ match x {
138+ EcdhCurveSpec :: EccNistP256 { } => & aws_lc_rs:: agreement:: ECDH_P256 ,
139+ EcdhCurveSpec :: EccNistP384 { } => & aws_lc_rs:: agreement:: ECDH_P384 ,
140+ EcdhCurveSpec :: EccNistP521 { } => & aws_lc_rs:: agreement:: ECDH_P521 ,
141+ EcdhCurveSpec :: Sm2 { } => panic ! ( "No SM2 in Rust" ) ,
142+ }
143+ }
144+
145+ pub ( crate ) fn exists ( f : & str ) -> bool {
146+ Path :: new ( f) . exists ( )
147+ }
148+
149+ pub ( crate ) fn write_raw_ecdh_ecc_keys (
150+ ecdh_curve_spec : EcdhCurveSpec
151+ ) -> Result < ( ) , crate :: BoxError > {
152+ // Safety check: Validate neither file is present
153+ if exists ( EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER )
154+ || exists ( EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT )
155+ || exists ( EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT )
156+ {
157+ return Err ( crate :: BoxError (
158+ "write_raw_ecdh_ecc_keys will not overwrite existing PEM files" . to_string ( ) ,
159+ ) ) ;
160+ }
161+
162+ let ( _public_key_sender, private_key_sender) = generate_raw_ecc_key_pair ( ecdh_curve_spec) ?;
163+ let ( public_key_recipient, private_key_recipient) = generate_raw_ecc_key_pair ( ecdh_curve_spec) ?;
164+
165+ std:: fs:: OpenOptions :: new ( )
166+ . write ( true )
167+ . create ( true )
168+ . truncate ( true )
169+ . open ( Path :: new ( EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER ) ) ?
170+ . write_all ( private_key_sender. as_bytes ( ) ) ?;
171+
172+ std:: fs:: OpenOptions :: new ( )
173+ . write ( true )
174+ . create ( true )
175+ . truncate ( true )
176+ . open ( Path :: new ( EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT ) ) ?
177+ . write_all ( private_key_recipient. as_bytes ( ) ) ?;
178+
179+ std:: fs:: OpenOptions :: new ( )
180+ . write ( true )
181+ . create ( true )
182+ . truncate ( true )
183+ . open ( Path :: new ( EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT ) ) ?
184+ . write_all ( public_key_recipient. as_bytes ( ) ) ?;
185+
186+ Ok ( ( ) )
187+ }
188+
189+ fn generate_raw_ecc_key_pair (
190+ ecdh_curve_spec : EcdhCurveSpec
191+ ) -> Result < ( String , String ) , crate :: BoxError > {
192+ use aws_lc_rs:: encoding:: AsDer ;
193+ use aws_lc_rs:: encoding:: EcPrivateKeyRfc5915Der ;
194+
195+ // This code will generate new ECC keys for example use.
196+ // The public and private keys will be written to the files:
197+ // - public: EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT
198+ // - private: EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER, EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT
199+ // This example uses aws-lc-rs's KeyPairGenerator to generate the key pair.
200+ // In practice, you should not generate this in your code, and should instead
201+ // retrieve this key from a secure key management system (e.g. HSM)
202+ // These examples only demonstrate using the P256 curve while the keyring accepts
203+ // P256, P384, or P521.
204+ // This key is created here for example purposes only.
205+ let private_key =
206+ aws_lc_rs:: agreement:: PrivateKey :: generate ( get_alg ( ecdh_curve_spec) )
207+ . map_err ( |e| format ! ( "{:?}" , e) ) ?;
208+
209+ let public_key = private_key
210+ . compute_public_key ( )
211+ . map_err ( |e| format ! ( "{:?}" , e) ) ?;
212+
213+ let public_key: Vec < u8 > = x962_to_x509 ( public_key. as_ref ( ) , get_nid ( ecdh_curve_spec) ) ?;
214+ let public_key = pem:: Pem :: new ( "PUBLIC KEY" , public_key) ;
215+ let public_key = pem:: encode ( & public_key) ;
216+
217+ let private_key_der = AsDer :: < EcPrivateKeyRfc5915Der > :: as_der ( & private_key)
218+ . map_err ( |e| format ! ( "{:?}" , e) ) ?;
219+ let private_key = pem:: Pem :: new ( "PRIVATE KEY" , private_key_der. as_ref ( ) ) ;
220+ let private_key = pem:: encode ( & private_key) ;
221+
222+ Ok ( ( public_key, private_key) )
223+ }
224+
225+ pub ( crate ) async fn write_kms_ecdh_ecc_public_key (
226+ ecc_key_arn : & str ,
227+ public_key_file_path : & str
228+ ) -> Result < ( ) , crate :: BoxError > {
229+ if exists ( public_key_file_path)
230+ {
231+ return Err ( crate :: BoxError (
232+ "write_kms_ecdh_ecc_public_key will not overwrite existing PEM files" . to_string ( ) ,
233+ ) ) ;
234+ }
235+
236+ let public_key = generate_kms_ecc_public_key ( ecc_key_arn) . await ?;
237+
238+ let public_key = pem:: Pem :: new ( "PUBLIC KEY" , public_key) ;
239+ let public_key = pem:: encode ( & public_key) ;
240+
241+ std:: fs:: OpenOptions :: new ( )
242+ . write ( true )
243+ . create ( true )
244+ . truncate ( true )
245+ . open ( Path :: new ( public_key_file_path) ) ?
246+ . write_all ( public_key. as_bytes ( ) ) ?;
247+
248+ Ok ( ( ) )
249+ }
250+
251+ pub ( crate ) async fn generate_kms_ecc_public_key (
252+ ecc_key_arn : & str ,
253+ ) -> Result < aws_smithy_types:: Blob , crate :: BoxError > {
254+ // Create KMS client to get public key
255+ let sdk_config = aws_config:: load_defaults ( aws_config:: BehaviorVersion :: latest ( ) ) . await ;
256+ let kms_client = aws_sdk_kms:: Client :: new ( & sdk_config) ;
257+
258+ // This code will call KMS to get the public key for the KMS ECC key.
259+ // You must have kms:GetPublicKey permissions on the key for this to succeed.
260+ // The public key generated here will be written to the file EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_SENDER
261+ // or EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_RECIPIENT.
262+ let kms_response = kms_client
263+ . get_public_key ( )
264+ . key_id ( ecc_key_arn)
265+ . send ( )
266+ . await ?;
267+
268+ let public_key = kms_response
269+ . public_key
270+ . expect ( "Error unwrapping public key from KMS response." ) ;
271+
272+ Ok ( public_key)
273+ }
0 commit comments