pub const TEST_EXAMPLE_DATA: &str = "Hello World!";
pub const TEST_DEFAULT_KMS_KEY_ID: &str =
"arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f";
pub const TEST_DEFAULT_KMS_KEY_ACCOUNT_ID: &str = "658956600833";
pub const TEST_SECOND_REGION_KMS_KEY_ID: &str =
"arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2";
pub const TEST_KMS_RSA_KEY_ID: &str =
"arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297";
pub const TEST_KMS_RSA_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----\n\
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ\
oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN\
/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK\
fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2\
r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9\
yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou\
twIDAQAB\n\
-----END PUBLIC KEY-----";
pub const TEST_MRK_KEY_ID_US_EAST_1: &str =
"arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7";
pub const TEST_MRK_KEY_ID_EU_WEST_1: &str =
"arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7";
pub const TEST_KEY_STORE_NAME: &str = "KeyStoreDdbTable";
pub const TEST_LOGICAL_KEY_STORE_NAME: &str = "KeyStoreDdbTable";
pub const TEST_KEY_STORE_KMS_KEY_ID: &str =
"arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126";
use aws_esdk::aws_cryptography_primitives::types::EcdhCurveSpec;
use std::io::Write;
use std::path::Path;
pub const TEST_KMS_ECDH_KEY_ID_P256_SENDER: &str =
"arn:aws:kms:us-west-2:370957321024:key/eabdf483-6be2-4d2d-8ee4-8c2583d416e9";
pub const TEST_KMS_ECDH_KEY_ID_P256_RECIPIENT: &str =
"arn:aws:kms:us-west-2:370957321024:key/0265c8e9-5b6a-4055-8f70-63719e09fda5";
pub const EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER: &str =
"RawEcdhKeyringExamplePrivateKeySender.pem";
pub const EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT: &str =
"RawEcdhKeyringExamplePrivateKeyRecipient.pem";
pub const EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT: &str =
"RawEcdhKeyringExamplePublicKeyRecipient.pem";
pub const EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_SENDER: &str =
"KmsEccKeyringExamplePublicKeySender.pem";
pub const EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_RECIPIENT: &str =
"KmsEccKeyringExamplePublicKeyRecipient.pem";
pub(crate) fn x962_to_x509(public_key: &[u8], nid: i32) -> Result<Vec<u8>, String> {
use aws_lc_sys::CBB_finish;
use aws_lc_sys::CBB_init;
use aws_lc_sys::EC_GROUP_new_by_curve_name;
use aws_lc_sys::EC_KEY_new_by_curve_name;
use aws_lc_sys::EC_KEY_set_public_key;
use aws_lc_sys::EC_POINT_free;
use aws_lc_sys::EC_POINT_new;
use aws_lc_sys::EC_POINT_oct2point;
use aws_lc_sys::EVP_PKEY_assign_EC_KEY;
use aws_lc_sys::EVP_PKEY_free;
use aws_lc_sys::EVP_PKEY_new;
use aws_lc_sys::EVP_PKEY_size;
use aws_lc_sys::EVP_marshal_public_key;
use aws_lc_sys::OPENSSL_free;
use aws_lc_sys::CBB;
use std::ptr::null_mut;
let ec_group = unsafe { EC_GROUP_new_by_curve_name(nid) };
let ec_point = unsafe { EC_POINT_new(ec_group) };
if 1 != unsafe {
EC_POINT_oct2point(
ec_group,
ec_point,
public_key.as_ptr(),
public_key.len(),
null_mut(),
)
} {
return Err("Error in EC_POINT_oct2point.".to_string());
}
let ec_key = unsafe { EC_KEY_new_by_curve_name(nid) };
if 1 != unsafe { EC_KEY_set_public_key(ec_key, ec_point) } {
return Err("Error in EC_KEY_set_public_key.".to_string());
}
let evp_pkey = unsafe { EVP_PKEY_new() };
if 1 != unsafe { EVP_PKEY_assign_EC_KEY(evp_pkey, ec_key) } {
return Err("Error in EVP_PKEY_assign_EC_KEY.".to_string());
}
let key_size_bytes: usize = unsafe { EVP_PKEY_size(evp_pkey) }.try_into().unwrap();
let mut cbb: CBB = Default::default();
unsafe { CBB_init(&mut cbb as *mut CBB, key_size_bytes * 5) };
if 1 != unsafe { EVP_marshal_public_key(&mut cbb, evp_pkey) } {
return Err("Error in EVP_marshal_public_key in GetPublicKey.".to_string());
};
let mut out_data = null_mut::<u8>();
let mut out_len: usize = 0;
if 1 != unsafe { CBB_finish(&mut cbb, &mut out_data, &mut out_len) } {
return Err("Error in CBB_finish in GetPublicKey.".to_string());
};
let slice = unsafe { std::slice::from_raw_parts(out_data, out_len) };
let slice = slice.to_vec();
unsafe { OPENSSL_free(out_data as *mut ::std::os::raw::c_void) };
unsafe { EVP_PKEY_free(evp_pkey) };
unsafe { EC_POINT_free(ec_point) };
Ok(slice)
}
fn get_nid(x: EcdhCurveSpec) -> i32 {
match x {
EcdhCurveSpec::EccNistP256 => aws_lc_sys::NID_X9_62_prime256v1,
EcdhCurveSpec::EccNistP384 => aws_lc_sys::NID_secp384r1,
EcdhCurveSpec::EccNistP521 => aws_lc_sys::NID_secp521r1,
EcdhCurveSpec::Sm2 => panic!("No SM2 in Rust"),
}
}
fn get_alg(x: EcdhCurveSpec) -> &'static aws_lc_rs::agreement::Algorithm {
match x {
EcdhCurveSpec::EccNistP256 => &aws_lc_rs::agreement::ECDH_P256,
EcdhCurveSpec::EccNistP384 => &aws_lc_rs::agreement::ECDH_P384,
EcdhCurveSpec::EccNistP521 => &aws_lc_rs::agreement::ECDH_P521,
EcdhCurveSpec::Sm2 => panic!("No SM2 in Rust"),
}
}
pub(crate) fn exists(f: &str) -> bool {
Path::new(f).exists()
}
pub(crate) fn write_raw_ecdh_ecc_keys(
ecdh_curve_spec: EcdhCurveSpec,
) -> Result<(), crate::BoxError> {
if exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER)
|| exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT)
|| exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT)
{
return Err(crate::BoxError(
"write_raw_ecdh_ecc_keys will not overwrite existing PEM files".to_string(),
));
}
let (_public_key_sender, private_key_sender) = generate_raw_ecc_key_pair(ecdh_curve_spec)?;
let (public_key_recipient, private_key_recipient) = generate_raw_ecc_key_pair(ecdh_curve_spec)?;
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(Path::new(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER))?
.write_all(private_key_sender.as_bytes())?;
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(Path::new(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT))?
.write_all(private_key_recipient.as_bytes())?;
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(Path::new(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT))?
.write_all(public_key_recipient.as_bytes())?;
Ok(())
}
fn generate_raw_ecc_key_pair(
ecdh_curve_spec: EcdhCurveSpec,
) -> Result<(String, String), crate::BoxError> {
use aws_lc_rs::encoding::AsDer;
use aws_lc_rs::encoding::EcPrivateKeyRfc5915Der;
let private_key = aws_lc_rs::agreement::PrivateKey::generate(get_alg(ecdh_curve_spec))
.map_err(|e| format!("{e:?}"))?;
let public_key = private_key
.compute_public_key()
.map_err(|e| format!("{e:?}"))?;
let public_key: Vec<u8> = x962_to_x509(public_key.as_ref(), get_nid(ecdh_curve_spec))?;
let public_key = pem::Pem::new("PUBLIC KEY", public_key);
let public_key = pem::encode(&public_key);
let private_key_der =
AsDer::<EcPrivateKeyRfc5915Der>::as_der(&private_key).map_err(|e| format!("{e:?}"))?;
let private_key = pem::Pem::new("PRIVATE KEY", private_key_der.as_ref());
let private_key = pem::encode(&private_key);
Ok((public_key, private_key))
}
pub(crate) async fn write_kms_ecdh_ecc_public_key(
ecc_key_arn: &str,
public_key_file_path: &str,
) -> Result<(), crate::BoxError> {
if exists(public_key_file_path) {
return Err(crate::BoxError(
"write_kms_ecdh_ecc_public_key will not overwrite existing PEM files".to_string(),
));
}
let public_key = generate_kms_ecc_public_key(ecc_key_arn).await?;
let public_key = pem::Pem::new("PUBLIC KEY", public_key);
let public_key = pem::encode(&public_key);
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(Path::new(public_key_file_path))?
.write_all(public_key.as_bytes())?;
Ok(())
}
pub(crate) async fn generate_kms_ecc_public_key(
ecc_key_arn: &str,
) -> Result<aws_smithy_types::Blob, crate::BoxError> {
let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let kms_client = aws_sdk_kms::Client::new(&sdk_config);
let kms_response = kms_client
.get_public_key()
.key_id(ecc_key_arn)
.send()
.await?;
let public_key = kms_response
.public_key
.expect("Error unwrapping public key from KMS response.");
Ok(public_key)
}