#[cfg(feature = "arithmetic")]
use {
crate::{RecoveryId, SignatureSize},
core::borrow::Borrow,
elliptic_curve::{
group::Curve as _,
ops::{Invert, LinearCombination, Reduce},
subtle::CtOption,
AffineArithmetic, AffineXCoordinate, Field, Group, ProjectiveArithmetic, ProjectivePoint,
Scalar, ScalarArithmetic,
},
};
#[cfg(feature = "digest")]
use {
core::cmp,
elliptic_curve::{bigint::Encoding, FieldSize},
signature::{digest::Digest, PrehashSignature},
};
#[cfg(any(feature = "arithmetic", feature = "digest"))]
use crate::{
elliptic_curve::{generic_array::ArrayLength, FieldBytes, PrimeCurve},
Error, Result, Signature,
};
#[cfg(all(feature = "arithmetic", feature = "digest"))]
use signature::digest::FixedOutput;
#[cfg(all(feature = "rfc6979"))]
use {
elliptic_curve::ScalarCore,
signature::digest::{core_api::BlockSizeUser, FixedOutputReset},
};
#[cfg(feature = "arithmetic")]
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
pub trait SignPrimitive<C>: Field + Into<FieldBytes<C>> + Reduce<C::UInt> + Sized
where
C: PrimeCurve + ProjectiveArithmetic + ScalarArithmetic<Scalar = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
#[allow(non_snake_case)]
fn try_sign_prehashed<K>(
&self,
k: K,
z: FieldBytes<C>,
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
K: Borrow<Self> + Invert<Output = CtOption<Self>>,
{
if k.borrow().is_zero().into() {
return Err(Error::new());
}
let z = Self::from_be_bytes_reduced(z);
let k_inv = Option::<Scalar<C>>::from(k.invert()).ok_or_else(Error::new)?;
let R = (C::ProjectivePoint::generator() * k.borrow()).to_affine();
let r = Self::from_be_bytes_reduced(R.x());
let s = k_inv * (z + (r * self));
if s.is_zero().into() {
return Err(Error::new());
}
Ok((Signature::from_scalars(r, s)?, None))
}
#[cfg(all(feature = "rfc6979"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))]
fn try_sign_prehashed_rfc6979<D>(
&self,
z: FieldBytes<C>,
ad: &[u8],
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
Self: From<ScalarCore<C>>,
C::UInt: for<'a> From<&'a Self>,
D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
{
let x = C::UInt::from(self);
let k = rfc6979::generate_k::<D, C::UInt>(&x, &C::ORDER, &z, ad);
let k = Self::from(ScalarCore::<C>::new(*k).unwrap());
self.try_sign_prehashed(k, z)
}
#[cfg(all(feature = "rfc6979"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))]
fn try_sign_digest_rfc6979<D>(
&self,
msg_digest: D,
ad: &[u8],
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
Self: From<ScalarCore<C>>,
C::UInt: for<'a> From<&'a Self>,
D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
{
self.try_sign_prehashed_rfc6979::<D>(msg_digest.finalize_fixed(), ad)
}
}
#[cfg(feature = "arithmetic")]
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
pub trait VerifyPrimitive<C>: AffineXCoordinate<C> + Copy + Sized
where
C: PrimeCurve + AffineArithmetic<AffinePoint = Self> + ProjectiveArithmetic,
Scalar<C>: Reduce<C::UInt>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify_prehashed(&self, z: FieldBytes<C>, sig: &Signature<C>) -> Result<()> {
let z = Scalar::<C>::from_be_bytes_reduced(z);
let (r, s) = sig.split_scalars();
let s_inv = *s.invert();
let u1 = z * s_inv;
let u2 = *r * s_inv;
let x = ProjectivePoint::<C>::lincomb(
&ProjectivePoint::<C>::generator(),
&u1,
&ProjectivePoint::<C>::from(*self),
&u2,
)
.to_affine()
.x();
if Scalar::<C>::from_be_bytes_reduced(x) == *r {
Ok(())
} else {
Err(Error::new())
}
}
#[cfg(feature = "digest")]
#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
fn verify_digest<D>(&self, msg_digest: D, sig: &Signature<C>) -> Result<()>
where
D: FixedOutput<OutputSize = FieldSize<C>>,
{
self.verify_prehashed(msg_digest.finalize_fixed(), sig)
}
}
#[cfg(feature = "digest")]
#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
pub trait DigestPrimitive: PrimeCurve {
type Digest: Digest;
fn prehash_to_field_bytes(prehash: &[u8]) -> Result<FieldBytes<Self>> {
if prehash.len() < Self::UInt::BYTE_SIZE / 2 {
return Err(Error::new());
}
let mut field_bytes = FieldBytes::<Self>::default();
match prehash.len().cmp(&Self::UInt::BYTE_SIZE) {
cmp::Ordering::Equal => field_bytes.copy_from_slice(prehash),
cmp::Ordering::Less => {
field_bytes[(Self::UInt::BYTE_SIZE - prehash.len())..].copy_from_slice(prehash);
}
cmp::Ordering::Greater => {
field_bytes.copy_from_slice(&prehash[..Self::UInt::BYTE_SIZE]);
}
}
Ok(field_bytes)
}
}
#[cfg(feature = "digest")]
impl<C> PrehashSignature for Signature<C>
where
C: DigestPrimitive,
<FieldSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
{
type Digest = C::Digest;
}