use crate::ecdsa::common::{is_canonical_nonzero_scalar, is_high_s, Rfc6979, SignatureComponents};
use alloc::vec::Vec;
use dcrypt_algorithms::ec::p384 as ec;
use dcrypt_algorithms::hash::sha2::Sha384;
use dcrypt_algorithms::hash::HashFunction;
use dcrypt_api::{
error::Error as ApiError, Result as ApiResult, Signature as SignatureTrait, ZeroizingBytes,
};
use dcrypt_common::SecretBuffer;
use dcrypt_internal::{
constant_time::ct_eq, zeroizing_bytes_from_slice, CryptoRng, RngCore, Zeroize, ZeroizeOnDrop,
Zeroizing,
};
use dcrypt_params::traditional::ecdsa::NIST_P384;
pub struct EcdsaP384;
#[derive(Clone)]
pub struct EcdsaP384PublicKey(pub(crate) [u8; ec::P384_POINT_UNCOMPRESSED_SIZE]);
#[derive(Clone)]
pub struct EcdsaP384SecretKey {
raw: ec::Scalar,
bytes: SecretBuffer<{ ec::P384_SCALAR_SIZE }>,
}
impl Zeroize for EcdsaP384SecretKey {
fn zeroize(&mut self) {
self.raw.zeroize();
self.bytes.zeroize();
}
}
impl Drop for EcdsaP384SecretKey {
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for EcdsaP384SecretKey {}
#[derive(Clone)]
pub struct EcdsaP384Signature(pub(crate) Vec<u8>);
impl AsRef<[u8]> for EcdsaP384PublicKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for EcdsaP384PublicKey {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl AsRef<[u8]> for EcdsaP384SecretKey {
fn as_ref(&self) -> &[u8] {
self.bytes.as_ref()
}
}
impl AsRef<[u8]> for EcdsaP384Signature {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for EcdsaP384Signature {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl EcdsaP384PublicKey {
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
let point = ec::Point::deserialize_uncompressed(bytes).map_err(ApiError::from)?;
if point.is_identity() {
return Err(ApiError::InvalidParameter {
context: "ECDSA-P384 public key",
#[cfg(feature = "std")]
message: "Identity is not a valid ECDSA public key".to_string(),
});
}
Ok(Self(point.serialize_uncompressed()))
}
pub fn to_bytes(&self) -> &[u8] {
&self.0
}
}
impl EcdsaP384SecretKey {
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
let raw = ec::Scalar::deserialize(bytes).map_err(ApiError::from)?;
let serialized = raw.serialize();
Ok(Self {
raw,
bytes: serialized,
})
}
pub fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
zeroizing_bytes_from_slice(self.bytes.as_ref())
}
}
impl EcdsaP384Signature {
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
SignatureComponents::from_der(bytes)?;
Ok(Self(bytes.to_vec()))
}
pub fn to_bytes(&self) -> &[u8] {
&self.0
}
}
impl SignatureTrait for EcdsaP384 {
type PublicKey = EcdsaP384PublicKey;
type SecretKey = EcdsaP384SecretKey;
type SignatureData = EcdsaP384Signature;
type KeyPair = (Self::PublicKey, Self::SecretKey);
fn name() -> &'static str {
"ECDSA-P384"
}
fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
let (sk_scalar, pk_point) = ec::generate_keypair(rng).map_err(ApiError::from)?;
let sk_bytes = sk_scalar.serialize();
if sk_bytes.iter().all(|&b| b == 0) {
return Err(ApiError::InvalidParameter {
context: "ECDSA-P384 keypair",
#[cfg(feature = "std")]
message: "Generated secret key is zero (internal error)".to_string(),
});
}
let secret_key = EcdsaP384SecretKey {
raw: sk_scalar,
bytes: sk_bytes,
};
let public_key = EcdsaP384PublicKey(pk_point.serialize_uncompressed());
Ok((public_key, secret_key))
}
fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
keypair.0.clone()
}
fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
keypair.1.clone()
}
fn sign(message: &[u8], secret_key: &Self::SecretKey) -> ApiResult<Self::SignatureData> {
let mut hasher = Sha384::new();
hasher.update(message).map_err(ApiError::from)?;
let hash_output = hasher.finalize().map_err(ApiError::from)?;
let mut h_bytes = [0u8; ec::P384_SCALAR_SIZE];
h_bytes.copy_from_slice(hash_output.as_ref());
let z = reduce_bytes_to_scalar(&h_bytes)?;
let d = secret_key.raw.clone();
let mut d_bytes = d.serialize();
let nonces_result =
Rfc6979::<Sha384>::new(d_bytes.as_ref(), hash_output.as_ref(), &NIST_P384.n, 384);
d_bytes.zeroize();
let mut nonces = nonces_result?;
loop {
let mut nonce = nonces.next_nonce()?;
let mut nonce_bytes: [u8; ec::P384_SCALAR_SIZE] =
(&nonce[..])
.try_into()
.map_err(|_| ApiError::InvalidLength {
context: "ECDSA-P384 nonce",
expected: ec::P384_SCALAR_SIZE,
actual: nonce.len(),
})?;
nonce.zeroize();
let scalar = ec::Scalar::new(nonce_bytes).map_err(ApiError::from);
nonce_bytes.zeroize();
let k = scalar?;
let kg = ec::scalar_mult_base_g(&k).map_err(ApiError::from)?;
let r_bytes = Zeroizing::new(kg.x_coordinate_bytes());
let r = reduce_bytes_to_scalar(&r_bytes)?;
if r.is_zero() {
continue;
}
let k_inv = k.inv_mod_n().map_err(ApiError::from)?;
let rd = r.mul_mod_n(&d).map_err(ApiError::from)?;
let z_plus_rd = z.add_mod_n(&rd).map_err(ApiError::from)?;
let mut s = k_inv.mul_mod_n(&z_plus_rd).map_err(ApiError::from)?;
if s.is_zero() {
continue;
}
if is_high_s(s.serialize().as_ref(), &NIST_P384.n) {
s = s.negate();
}
let sig = SignatureComponents {
r: r.serialize().to_vec(),
s: s.serialize().to_vec(),
};
let der_sig = sig.to_der();
return Ok(EcdsaP384Signature(der_sig));
}
}
fn verify(
message: &[u8],
signature: &Self::SignatureData,
public_key: &Self::PublicKey,
) -> ApiResult<()> {
let sig = SignatureComponents::from_der(&signature.0)?;
if sig.r.len() > ec::P384_SCALAR_SIZE || sig.s.len() > ec::P384_SCALAR_SIZE {
return Err(ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "Invalid signature component size".to_string(),
});
}
let mut r_bytes = [0u8; ec::P384_SCALAR_SIZE];
let mut s_bytes = [0u8; ec::P384_SCALAR_SIZE];
r_bytes[ec::P384_SCALAR_SIZE - sig.r.len()..].copy_from_slice(&sig.r);
s_bytes[ec::P384_SCALAR_SIZE - sig.s.len()..].copy_from_slice(&sig.s);
if !is_canonical_nonzero_scalar(&r_bytes, &NIST_P384.n)
|| !is_canonical_nonzero_scalar(&s_bytes, &NIST_P384.n)
{
return Err(ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "signature components must be canonical integers in [1, n-1]".to_string(),
});
}
let r = ec::Scalar::new(r_bytes).map_err(|_| ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "Invalid r component".to_string(),
})?;
let s = ec::Scalar::new(s_bytes).map_err(|_| ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "Invalid s component".to_string(),
})?;
if is_high_s(s.serialize().as_ref(), &NIST_P384.n) {
return Err(ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "high-s signatures are non-canonical".to_string(),
});
}
let mut hasher = Sha384::new();
hasher.update(message).map_err(ApiError::from)?;
let hash_output = hasher.finalize().map_err(ApiError::from)?;
let mut h_bytes = [0u8; ec::P384_SCALAR_SIZE];
h_bytes.copy_from_slice(hash_output.as_ref());
let z = reduce_bytes_to_scalar(&h_bytes)?;
let s_inv = s.inv_mod_n().map_err(ApiError::from)?;
let u1 = z.mul_mod_n(&s_inv).map_err(ApiError::from)?;
let u2 = r.mul_mod_n(&s_inv).map_err(ApiError::from)?;
let q = ec::Point::deserialize_uncompressed(&public_key.0).map_err(ApiError::from)?;
let u1g = ec::scalar_mult_base_g(&u1).map_err(ApiError::from)?;
let u2q = ec::scalar_mult(&u2, &q).map_err(ApiError::from)?;
let point = u1g.add(&u2q);
if point.is_identity() {
return Err(ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "Invalid signature: verification point is identity".to_string(),
});
}
let x1_bytes = point.x_coordinate_bytes();
let x1 = reduce_bytes_to_scalar(&x1_bytes)?;
if !ct_eq(r.serialize(), x1.serialize()) {
return Err(ApiError::InvalidSignature {
context: "ECDSA-P384 verify",
#[cfg(feature = "std")]
message: "Signature verification failed".to_string(),
});
}
Ok(())
}
}
fn reduce_bytes_to_scalar(bytes: &[u8; 48]) -> ApiResult<ec::Scalar> {
Ok(ec::Scalar::from_bytes_reduced(*bytes))
}
#[cfg(test)]
mod tests;