use crate::{Error, Result};
use core::cmp;
use elliptic_curve::{generic_array::typenum::Unsigned, FieldBytes, PrimeCurve};
#[cfg(feature = "arithmetic")]
use {
crate::{RecoveryId, SignatureSize},
elliptic_curve::{
ff::{Field, PrimeField},
group::{Curve as _, Group},
ops::{Invert, LinearCombination, MulByGenerator, Reduce},
point::AffineCoordinates,
scalar::IsHigh,
subtle::CtOption,
CurveArithmetic, ProjectivePoint, Scalar,
},
};
#[cfg(feature = "digest")]
use {
elliptic_curve::FieldBytesSize,
signature::{
digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset},
PrehashSignature,
},
};
#[cfg(feature = "rfc6979")]
use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive};
#[cfg(any(feature = "arithmetic", feature = "digest"))]
use crate::{elliptic_curve::generic_array::ArrayLength, Signature};
#[cfg(feature = "arithmetic")]
pub trait SignPrimitive<C>:
AsRef<Self>
+ Into<FieldBytes<C>>
+ IsHigh
+ PrimeField<Repr = FieldBytes<C>>
+ Reduce<C::Uint, Bytes = FieldBytes<C>>
+ Sized
where
C: PrimeCurve + CurveArithmetic<Scalar = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_prehashed<K>(
&self,
k: K,
z: &FieldBytes<C>,
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
K: AsRef<Self> + Invert<Output = CtOption<Self>>,
{
sign_prehashed(self, k, z).map(|(sig, recid)| (sig, (Some(recid))))
}
#[cfg(feature = "rfc6979")]
fn try_sign_prehashed_rfc6979<D>(
&self,
z: &FieldBytes<C>,
ad: &[u8],
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
Self: From<ScalarPrimitive<C>> + Invert<Output = CtOption<Self>>,
D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldBytesSize<C>> + FixedOutputReset,
{
let k = Scalar::<C>::from_repr(rfc6979::generate_k::<D, _>(
&self.to_repr(),
&C::ORDER.encode_field_bytes(),
z,
ad,
))
.unwrap();
self.try_sign_prehashed::<Self>(k, z)
}
}
#[cfg(feature = "arithmetic")]
pub trait VerifyPrimitive<C>: AffineCoordinates<FieldRepr = FieldBytes<C>> + Copy + Sized
where
C: PrimeCurve + CurveArithmetic<AffinePoint = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify_prehashed(&self, z: &FieldBytes<C>, sig: &Signature<C>) -> Result<()> {
verify_prehashed(&ProjectivePoint::<C>::from(*self), z, sig)
}
#[cfg(feature = "digest")]
fn verify_digest<D>(&self, msg_digest: D, sig: &Signature<C>) -> Result<()>
where
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
{
self.verify_prehashed(&msg_digest.finalize_fixed(), sig)
}
}
#[cfg(feature = "digest")]
pub trait DigestPrimitive: PrimeCurve {
type Digest: BlockSizeUser
+ Digest
+ FixedOutput<OutputSize = FieldBytesSize<Self>>
+ FixedOutputReset;
}
#[cfg(feature = "digest")]
impl<C> PrehashSignature for Signature<C>
where
C: DigestPrimitive,
<FieldBytesSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
{
type Digest = C::Digest;
}
pub fn bits2field<C: PrimeCurve>(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 = "arithmetic")]
#[allow(non_snake_case)]
pub fn sign_prehashed<C, K>(
d: &Scalar<C>,
k: K,
z: &FieldBytes<C>,
) -> Result<(Signature<C>, RecoveryId)>
where
C: PrimeCurve + CurveArithmetic,
K: AsRef<Scalar<C>> + Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArrayLength<u8>,
{
if k.as_ref().is_zero().into() {
return Err(Error::new());
}
let z = <Scalar<C> as Reduce<C::Uint>>::reduce_bytes(z);
let k_inv = Option::<Scalar<C>>::from(k.invert()).ok_or_else(Error::new)?;
let R = ProjectivePoint::<C>::mul_by_generator(k.as_ref()).to_affine();
let r = Scalar::<C>::reduce_bytes(&R.x());
let x_is_reduced = r.to_repr() != R.x();
let s = k_inv * (z + (r * d));
let signature = Signature::from_scalars(r, s)?;
let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced);
Ok((signature, recovery_id))
}
#[cfg(feature = "arithmetic")]
pub fn verify_prehashed<C>(
q: &ProjectivePoint<C>,
z: &FieldBytes<C>,
sig: &Signature<C>,
) -> Result<()>
where
C: PrimeCurve + CurveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
let z = Scalar::<C>::reduce_bytes(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>::lincomb(&ProjectivePoint::<C>::generator(), &u1, q, &u2)
.to_affine()
.x();
if *r == Scalar::<C>::reduce_bytes(&x) {
Ok(())
} else {
Err(Error::new())
}
}
#[cfg(test)]
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).unwrap();
assert_eq!(
field_bytes.as_slice(),
&hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
#[test]
fn bits2field_size_eq() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
assert_eq!(field_bytes.as_slice(), &prehash);
}
#[test]
fn bits2field_size_greater() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
assert_eq!(
field_bytes.as_slice(),
&hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
}