use crate::ec::{validate_ec_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey};
use crate::error::{KeyRejected, Unspecified};
use crate::pkcs8::{Document, Version};
use crate::ptr::{DetachableLcPtr, LcPtr};
use crate::rand::SecureRandom;
use crate::signature::{KeyPair, Signature};
use crate::{digest, ec};
#[cfg(not(feature = "fips"))]
use aws_lc::EC_KEY_generate_key;
#[cfg(feature = "fips")]
use aws_lc::EC_KEY_generate_key_fips;
use aws_lc::{
ECDSA_do_sign, EC_KEY_new_by_curve_name, EVP_PKEY_assign_EC_KEY, EVP_PKEY_new, EC_KEY, EVP_PKEY,
};
use std::fmt;
use std::fmt::{Debug, Formatter};
#[allow(clippy::module_name_repetitions)]
pub struct EcdsaKeyPair {
algorithm: &'static EcdsaSigningAlgorithm,
ec_key: LcPtr<*mut EC_KEY>,
pubkey: PublicKey,
}
impl Debug for EcdsaKeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!("EcdsaKeyPair {{ public_key: {:?} }}", self.pubkey))
}
}
unsafe impl Send for EcdsaKeyPair {}
unsafe impl Sync for EcdsaKeyPair {}
impl KeyPair for EcdsaKeyPair {
type PublicKey = PublicKey;
#[inline]
fn public_key(&self) -> &Self::PublicKey {
&self.pubkey
}
}
pub(crate) unsafe fn generate_key(nid: i32) -> Result<LcPtr<*mut EVP_PKEY>, Unspecified> {
let ec_key = DetachableLcPtr::new(EC_KEY_new_by_curve_name(nid))?;
#[cfg(feature = "fips")]
if 1 != EC_KEY_generate_key_fips(*ec_key) {
return Err(Unspecified);
}
#[cfg(not(feature = "fips"))]
if 1 != EC_KEY_generate_key(*ec_key) {
return Err(Unspecified);
}
let evp_pkey = LcPtr::new(EVP_PKEY_new())?;
if 1 != EVP_PKEY_assign_EC_KEY(*evp_pkey, *ec_key) {
return Err(Unspecified);
}
ec_key.detach();
Ok(evp_pkey)
}
impl EcdsaKeyPair {
unsafe fn new(
algorithm: &'static EcdsaSigningAlgorithm,
ec_key: LcPtr<*mut EC_KEY>,
) -> Result<Self, ()> {
let pubkey = ec::marshal_public_key(&ec_key.as_const())?;
Ok(Self {
algorithm,
ec_key,
pubkey,
})
}
pub fn from_pkcs8(
alg: &'static EcdsaSigningAlgorithm,
pkcs8: &[u8],
) -> Result<Self, KeyRejected> {
unsafe {
let evp_pkey = LcPtr::try_from(pkcs8)?;
let ec_key = evp_pkey.get_ec_key()?;
validate_ec_key(&ec_key.as_const(), alg.bits)?;
let key_pair = Self::new(alg, ec_key)?;
Ok(key_pair)
}
}
pub fn generate_pkcs8(
alg: &'static EcdsaSigningAlgorithm,
_rng: &dyn SecureRandom,
) -> Result<Document, Unspecified> {
unsafe {
let evp_pkey = generate_key(alg.0.id.nid())?;
evp_pkey.marshall_private_key(Version::V1)
}
}
pub fn from_private_key_and_public_key(
alg: &'static EcdsaSigningAlgorithm,
private_key: &[u8],
public_key: &[u8],
) -> Result<Self, KeyRejected> {
unsafe {
let ec_group = ec::ec_group_from_nid(alg.0.id.nid())?;
let public_ec_point = ec::ec_point_from_bytes(&ec_group, public_key)
.map_err(|_| KeyRejected::invalid_encoding())?;
let private_bn = DetachableLcPtr::try_from(private_key)?;
let ec_key = ec::ec_key_from_public_private(&ec_group, &public_ec_point, &private_bn)?;
validate_ec_key(&ec_key.as_const(), alg.bits)?;
let key_pair = Self::new(alg, ec_key)?;
Ok(key_pair)
}
}
#[inline]
pub fn sign(&self, _rng: &dyn SecureRandom, message: &[u8]) -> Result<Signature, Unspecified> {
unsafe {
let digest = digest::digest(self.algorithm.digest, message);
let digest = digest.as_ref();
let ecdsa_sig = LcPtr::new(ECDSA_do_sign(digest.as_ptr(), digest.len(), *self.ec_key))?;
match self.algorithm.sig_format {
EcdsaSignatureFormat::ASN1 => ec::ecdsa_sig_to_asn1(&ecdsa_sig),
EcdsaSignatureFormat::Fixed => {
ec::ecdsa_sig_to_fixed(self.algorithm.id, &ecdsa_sig)
}
}
}
}
}