use crate::{EcdsaCurve, Error, Result};
use core::cmp;
use elliptic_curve::{FieldBytes, array::typenum::Unsigned};
#[cfg(feature = "algorithm")]
use {
crate::{
RecoveryId, Signature, SignatureSize,
elliptic_curve::{FieldBytesEncoding, array::ArraySize},
},
elliptic_curve::{
CurveArithmetic, NonZeroScalar, ProjectivePoint, Scalar,
ff::PrimeField,
group::{Curve as _, Group},
ops::{Invert, MulByGeneratorVartime, Reduce},
point::AffineCoordinates,
scalar::IsHigh,
},
};
#[cfg(feature = "digest")]
use digest::block_api::EagerHash;
#[cfg(feature = "digest")]
pub trait DigestAlgorithm: EcdsaCurve {
type Digest: EagerHash + digest::Update;
}
pub fn bits2field<C: EcdsaCurve>(bits: &[u8]) -> Result<FieldBytes<C>> {
if bits.len() < C::FieldBytesSize::USIZE / 2 {
return Err(Error::new());
}
let mut field_bytes = FieldBytes::<C>::default();
match bits.len().cmp(&C::FieldBytesSize::USIZE) {
cmp::Ordering::Equal => field_bytes.copy_from_slice(bits),
cmp::Ordering::Less => {
field_bytes[(C::FieldBytesSize::USIZE - bits.len())..].copy_from_slice(bits);
}
cmp::Ordering::Greater => {
field_bytes.copy_from_slice(&bits[..C::FieldBytesSize::USIZE]);
}
}
Ok(field_bytes)
}
#[cfg(feature = "algorithm")]
#[allow(non_snake_case)]
pub fn sign_prehashed<C>(
d: &NonZeroScalar<C>,
k: &NonZeroScalar<C>,
z: &FieldBytes<C>,
) -> Result<(Signature<C>, RecoveryId)>
where
C: EcdsaCurve + CurveArithmetic,
SignatureSize<C>: ArraySize,
{
let z = Scalar::<C>::reduce(z);
let k_inv = k.invert();
let R = ProjectivePoint::<C>::mul_by_generator(k).to_affine();
let r = Scalar::<C>::reduce(&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))
}
#[cfg(feature = "algorithm")]
pub fn sign_prehashed_rfc6979<C, D>(
d: &NonZeroScalar<C>,
z: &FieldBytes<C>,
ad: &[u8],
) -> Result<(Signature<C>, RecoveryId)>
where
C: EcdsaCurve + CurveArithmetic,
D: EagerHash,
SignatureSize<C>: ArraySize,
{
let z2 = Scalar::<C>::reduce(z);
let k = NonZeroScalar::<C>::from_repr(rfc6979::generate_k::<D, _>(
&d.to_repr(),
&C::ORDER.encode_field_bytes(),
&z2.to_repr(),
ad,
))
.unwrap();
sign_prehashed(d, &k, z)
}
#[cfg(feature = "algorithm")]
pub fn verify_prehashed<C>(
q: &ProjectivePoint<C>,
z: &FieldBytes<C>,
sig: &Signature<C>,
) -> Result<()>
where
C: EcdsaCurve + CurveArithmetic,
SignatureSize<C>: ArraySize,
{
let z = Scalar::<C>::reduce(z);
let (r, s) = sig.split_scalars();
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 == Scalar::<C>::reduce(&x) {
Ok(())
} else {
Err(Error::new())
}
}
#[cfg(all(test, feature = "dev"))]
mod tests {
use super::bits2field;
use elliptic_curve::dev::MockCurve;
use hex_literal::hex;
#[test]
fn bits2field_too_small() {
assert!(bits2field::<MockCurve>(b"").is_err());
}
#[test]
fn bits2field_size_less() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
let field_bytes = bits2field::<MockCurve>(&prehash).expect("field bytes");
assert_eq!(
field_bytes.as_slice(),
&hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
#[test]
fn bits2field_size_eq() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
let field_bytes = bits2field::<MockCurve>(&prehash).expect("field bytes");
assert_eq!(field_bytes.as_slice(), &prehash);
}
#[test]
fn bits2field_size_greater() {
let prehash = hex!(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
);
let field_bytes = bits2field::<MockCurve>(&prehash).expect("field bytes");
assert_eq!(
field_bytes.as_slice(),
&hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
}