use ecdsa::signature::{Signer as EcdsaSigner, Verifier as EcdsaVerifier};
use elliptic_curve::sec1::{FromSec1Point, ToSec1Point};
use crate::{
asymmetric::{
EccCurve, EccPublicPoint, KeyAgreement, Keypair, PublicKeyComponents, Signer, Verifier,
},
error::{CryptoError, Result},
material::{
SharedSecretAlgorithm, SharedSecretBytes, SignatureAlgorithm, SignatureBytes,
SignatureEncoding,
},
rng::CryptoRng,
};
macro_rules! impl_ecc_keypair {
(
$(#[$meta:meta])*
$name:ident, $curve:ty, $curve_id:path
) => {
$(#[$meta])*
pub struct $name {
signing_key: ecdsa::SigningKey<$curve>,
}
impl $name {
pub fn as_inner(&self) -> &ecdsa::SigningKey<$curve> {
&self.signing_key
}
pub fn verifying_key(&self) -> &ecdsa::VerifyingKey<$curve> {
self.signing_key.verifying_key()
}
}
impl Keypair for $name {
fn generate(rng: &mut dyn CryptoRng, _key_size_bits: usize) -> Result<Self> {
use elliptic_curve::Generate;
let signing_key = ecdsa::SigningKey::<$curve>::try_generate_from_rng(rng)
.map_err(|_| CryptoError::InternalError)?;
Ok(Self { signing_key })
}
fn to_public_components(&self) -> Result<PublicKeyComponents> {
let point = self.verifying_key().as_affine().to_sec1_point(false);
let (x, y) = match point.coordinates() {
elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => (x, y),
_ => return Err(CryptoError::InternalError),
};
Ok(PublicKeyComponents::Ecc(EccPublicPoint::from_be_bytes(
$curve_id,
x.to_vec(),
y.to_vec(),
)))
}
}
impl Signer for $name {
fn sign(&self, msg: &[u8], _rng: &mut dyn CryptoRng) -> Result<SignatureBytes> {
let sig: ecdsa::Signature<$curve> = self.signing_key.sign(msg);
Ok(SignatureBytes::new(
sig.to_bytes().to_vec(),
SignatureAlgorithm::Ecdsa($curve_id),
SignatureEncoding::Raw,
))
}
}
impl Verifier for $name {
fn verify(&self, msg: &[u8], signature: &SignatureBytes) -> Result<()> {
if signature.algorithm() != SignatureAlgorithm::Ecdsa($curve_id) {
return Err(CryptoError::InvalidInput);
}
if signature.encoding() != SignatureEncoding::Raw {
return Err(CryptoError::InvalidInput);
}
let signature = ecdsa::Signature::<$curve>::from_slice(signature.as_bytes())
.map_err(|_| CryptoError::InvalidInput)?;
self.verifying_key()
.verify(msg, &signature)
.map_err(|_| CryptoError::VerificationFailed)
}
}
};
}
impl_ecc_keypair!(
EccP256Keypair, p256::NistP256, EccCurve::P256
);
impl_ecc_keypair!(
EccP384Keypair, p384::NistP384, EccCurve::P384
);
impl_ecc_keypair!(
EccP521Keypair, p521::NistP521, EccCurve::P521
);
fn p256_public_key_from_xy(x: &[u8], y: &[u8]) -> Result<p256::PublicKey> {
let x_bytes: &p256::FieldBytes = x.try_into().map_err(|_| CryptoError::InvalidLength)?;
let y_bytes: &p256::FieldBytes = y.try_into().map_err(|_| CryptoError::InvalidLength)?;
let point = p256::Sec1Point::from_affine_coordinates(x_bytes, y_bytes, false);
p256::AffinePoint::from_sec1_point(&point)
.into_option()
.ok_or(CryptoError::InvalidKey)
.and_then(|affine| {
p256::PublicKey::from_affine(affine).map_err(|_| CryptoError::InvalidKey)
})
}
fn p384_public_key_from_xy(x: &[u8], y: &[u8]) -> Result<p384::PublicKey> {
let x_bytes: &p384::FieldBytes = x.try_into().map_err(|_| CryptoError::InvalidLength)?;
let y_bytes: &p384::FieldBytes = y.try_into().map_err(|_| CryptoError::InvalidLength)?;
let point = p384::Sec1Point::from_affine_coordinates(x_bytes, y_bytes, false);
p384::AffinePoint::from_sec1_point(&point)
.into_option()
.ok_or(CryptoError::InvalidKey)
.and_then(|affine| {
p384::PublicKey::from_affine(affine).map_err(|_| CryptoError::InvalidKey)
})
}
fn p521_public_key_from_xy(x: &[u8], y: &[u8]) -> Result<p521::PublicKey> {
let x_bytes: &p521::FieldBytes = x.try_into().map_err(|_| CryptoError::InvalidLength)?;
let y_bytes: &p521::FieldBytes = y.try_into().map_err(|_| CryptoError::InvalidLength)?;
let point = p521::Sec1Point::from_affine_coordinates(x_bytes, y_bytes, false);
p521::AffinePoint::from_sec1_point(&point)
.into_option()
.ok_or(CryptoError::InvalidKey)
.and_then(|affine| {
p521::PublicKey::from_affine(affine).map_err(|_| CryptoError::InvalidKey)
})
}
impl KeyAgreement for EccP256Keypair {
fn shared_secret(&self, peer_public: &PublicKeyComponents) -> Result<SharedSecretBytes> {
let point = match peer_public {
PublicKeyComponents::Ecc(point) if point.curve() == EccCurve::P256 => point,
PublicKeyComponents::Ecc(_) => return Err(CryptoError::InvalidInput),
_ => return Err(CryptoError::InvalidInput),
};
let peer_pk = p256_public_key_from_xy(point.x(), point.y())?;
let secret = elliptic_curve::ecdh::diffie_hellman(
self.signing_key.as_nonzero_scalar(),
peer_pk.as_affine(),
);
Ok(SharedSecretBytes::new(
secret.raw_secret_bytes().to_vec(),
SharedSecretAlgorithm::Ecdh(EccCurve::P256),
))
}
}
impl KeyAgreement for EccP384Keypair {
fn shared_secret(&self, peer_public: &PublicKeyComponents) -> Result<SharedSecretBytes> {
let point = match peer_public {
PublicKeyComponents::Ecc(point) if point.curve() == EccCurve::P384 => point,
PublicKeyComponents::Ecc(_) => return Err(CryptoError::InvalidInput),
_ => return Err(CryptoError::InvalidInput),
};
let peer_pk = p384_public_key_from_xy(point.x(), point.y())?;
let secret = elliptic_curve::ecdh::diffie_hellman(
self.signing_key.as_nonzero_scalar(),
peer_pk.as_affine(),
);
Ok(SharedSecretBytes::new(
secret.raw_secret_bytes().to_vec(),
SharedSecretAlgorithm::Ecdh(EccCurve::P384),
))
}
}
impl KeyAgreement for EccP521Keypair {
fn shared_secret(&self, peer_public: &PublicKeyComponents) -> Result<SharedSecretBytes> {
let point = match peer_public {
PublicKeyComponents::Ecc(point) if point.curve() == EccCurve::P521 => point,
PublicKeyComponents::Ecc(_) => return Err(CryptoError::InvalidInput),
_ => return Err(CryptoError::InvalidInput),
};
let peer_pk = p521_public_key_from_xy(point.x(), point.y())?;
let secret = elliptic_curve::ecdh::diffie_hellman(
self.signing_key.as_nonzero_scalar(),
peer_pk.as_affine(),
);
Ok(SharedSecretBytes::new(
secret.raw_secret_bytes().to_vec(),
SharedSecretAlgorithm::Ecdh(EccCurve::P521),
))
}
}