use crate::{EcdsaCurve, Error, RecoveryId, Result, Signature};
use elliptic_curve::{
CurveArithmetic, FieldBytes, NonZeroScalar, ProjectivePoint, Scalar,
bigint::{BitOps, Encoding},
ff::PrimeField,
group::{Curve as _, Group},
ops::{Invert, MulByGeneratorVartime, Reduce},
point::AffineCoordinates,
scalar::IsHigh,
};
#[cfg(feature = "digest")]
use digest::{Digest, block_api::BlockSizeUser};
#[allow(non_snake_case)]
pub fn sign_prehashed<C>(
d: &NonZeroScalar<C>,
k: &NonZeroScalar<C>,
z: &[u8],
) -> Result<(Signature<C>, RecoveryId)>
where
C: EcdsaCurve + CurveArithmetic,
{
let z = bytes2scalar::<C>(z);
let k_inv = k.invert();
let R = ProjectivePoint::<C>::mul_by_generator(k).to_affine();
let r = bytes2scalar::<C>(&R.x());
let s = *k_inv * (z + (r * d.as_ref()));
let mut signature = Signature::from_scalars(r, s)?;
let x_is_reduced = r.to_repr() != R.x();
let y_is_odd = R.y_is_odd();
let mut recovery_id = RecoveryId::new(y_is_odd.into(), x_is_reduced);
if C::NORMALIZE_S {
recovery_id.0 ^= s.is_high().unwrap_u8();
signature = signature.normalize_s();
}
Ok((signature, recovery_id))
}
pub fn sign_prehashed_rfc6979<C, D>(
d: &NonZeroScalar<C>,
z: &[u8],
ad: &[u8],
) -> (Signature<C>, RecoveryId)
where
C: EcdsaCurve + CurveArithmetic,
D: Digest + BlockSizeUser,
{
let order = C::ORDER;
let mut kgen = rfc6979::KGenerator::<D, C::Uint>::new(&d.to_repr(), z, ad, &order);
loop {
let mut k_bytes = FieldBytes::<C>::default();
kgen.fill_next_k(&mut k_bytes);
if let Some(k) = NonZeroScalar::<C>::from_repr(k_bytes).into_option() {
if let Ok(ret) = sign_prehashed(d, &k, z) {
return ret;
}
}
}
}
pub fn verify_prehashed<C>(q: &ProjectivePoint<C>, z: &[u8], signature: &Signature<C>) -> Result<()>
where
C: EcdsaCurve + CurveArithmetic,
{
let (r, s) = signature.split_scalars();
if C::NORMALIZE_S && s.is_high().into() {
return Err(Error::new());
}
let z = bytes2scalar::<C>(z);
let s_inv = *s.invert_vartime();
let u1 = z * s_inv;
let u2 = *r * s_inv;
let x = ProjectivePoint::<C>::mul_by_generator_and_mul_add_vartime(&u1, &u2, q)
.to_affine()
.x();
if *r == bytes2scalar::<C>(&x) {
Ok(())
} else {
Err(Error::new())
}
}
pub(crate) fn bytes2scalar<C: EcdsaCurve + CurveArithmetic>(mut bytes: &[u8]) -> Scalar<C> {
let n_bits = C::ORDER.bits();
let n_bytes = usize::try_from(n_bits.div_ceil(8)).expect("overflow");
if bytes.len() > n_bytes {
bytes = &bytes[..n_bytes];
}
<Scalar<C> as Reduce<C::Uint>>::reduce(&C::Uint::from_be_slice_truncated(bytes, n_bits))
}