use super::{EllipticCurve, ProjectivePoint};
use crate::big_int::UBigInt;
use crate::finite_field::{FieldElement, FiniteField};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Signature<C: FiniteField<4>> {
r: FieldElement<4, C>,
s: FieldElement<4, C>,
}
impl<C: FiniteField<4>> Signature<C> {
pub const fn new(r: FieldElement<4, C>, s: FieldElement<4, C>) -> Self {
Self { r, s }
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct ValidSig;
impl core::fmt::Display for ValidSig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("the signature is valid")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct InvalidSig;
impl core::fmt::Display for InvalidSig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("the signature is not valid")
}
}
impl core::error::Error for InvalidSig {}
pub fn sign<C: EllipticCurve>(
msg: &[u8],
priv_key: &FieldElement<4, C::Order>,
hash_func: impl FnOnce(&[u8]) -> [u8; 32],
random_num_gen: impl Fn() -> FieldElement<4, C::Order>,
) -> Signature<C::Order> {
let mut hash: FieldElement<4, C::Order> =
FieldElement::<4, _>::new(UBigInt::<4>::from_be_bytes(hash_func(msg)));
loop {
let secret_num = random_num_gen();
let mut inverse = secret_num.inverse();
let Some(new_point) = C::BASE_POINT.mul_scalar(&secret_num).as_affine() else {
continue;
};
let r = new_point.x_ref().convert();
hash.add_assign(&r.mul(priv_key));
inverse.mul_assign(&hash);
if r != FieldElement::ZERO && inverse != FieldElement::ZERO {
return Signature::new(r, inverse);
}
}
}
pub fn verify_signature<C: EllipticCurve>(
msg: &[u8],
pub_key: &ProjectivePoint<C>,
hash_func: impl FnOnce(&[u8]) -> [u8; 32],
sig: &Signature<C::Order>,
) -> Result<ValidSig, InvalidSig> {
let hash: FieldElement<4, C::Order> =
FieldElement::<4, _>::new(UBigInt::<4>::from_be_bytes(hash_func(msg)));
let inverse = sig.s.inverse();
let u = hash.mul(&inverse);
let v = sig.r.mul(&inverse);
let r = match C::BASE_POINT
.mul_scalar(&u)
.add(&pub_key.mul_scalar(&v))
.as_affine()
{
Some(point) => point.x_ref().convert(),
None => return Err(InvalidSig),
};
match r == sig.r {
true => Ok(ValidSig),
false => Err(InvalidSig),
}
}
#[cfg(test)]
mod tests {
use crate::ec::Secp256r1;
use super::FieldElement;
use super::InvalidSig;
use super::ProjectivePoint;
use super::Signature;
use super::UBigInt;
use super::ValidSig;
use crate::hash::{Hasher, Sha256};
#[test]
fn sign_1() {
let msg = &[
0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6,
0xf2, 0xd9, 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48,
0x29, 0x35, 0xfa, 0xaf, 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2,
0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a,
0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb,
0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, 0x8a, 0xd2, 0x81, 0x75,
0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, 0xc9, 0x34,
0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f,
0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7,
0x9c, 0xe8,
];
let priv_key = unsafe {
FieldElement::new_unchecked(UBigInt([
0xca54a56dda72b464,
0x5b44c8130b4e3eac,
0x1f4fa8ee59f4771a,
0x519b423d715f8b58,
]))
};
let r = unsafe {
FieldElement::new_unchecked(UBigInt([
0xcabb5e6f79c8c2ac,
0x2afd6b1f6a555a7a,
0x8843e3d6629527ed,
0xf3ac8061b514795b,
]))
};
let s = unsafe {
FieldElement::new_unchecked(UBigInt([
0x3ccdda2acc058903,
0xef97b218e96f175a,
0x786c76262bf7371c,
0x8bf77819ca05a6b2,
]))
};
let signature = Signature::new(r, s);
let super_secure_random_num_generator = || unsafe {
FieldElement::new_unchecked(UBigInt([
0xb9670787642a68de,
0x3b4a6247824f5d33,
0xa280f245f9e93c7f,
0x94a1bbb14b906a61,
]))
};
let generated_signature = super::sign::<Secp256r1>(
msg,
&priv_key,
crate::hash::Sha256::hash,
super_secure_random_num_generator,
);
assert_eq!(generated_signature, signature);
}
#[test]
fn sign_2() {
let msg = &[
0xc3, 0x5e, 0x2f, 0x09, 0x25, 0x53, 0xc5, 0x57, 0x72, 0x92, 0x6b, 0xdb, 0xe8, 0x7c,
0x97, 0x96, 0x82, 0x7d, 0x17, 0x02, 0x4d, 0xbb, 0x92, 0x33, 0xa5, 0x45, 0x36, 0x6e,
0x2e, 0x59, 0x87, 0xdd, 0x34, 0x4d, 0xeb, 0x72, 0xdf, 0x98, 0x71, 0x44, 0xb8, 0xc6,
0xc4, 0x3b, 0xc4, 0x1b, 0x65, 0x4b, 0x94, 0xcc, 0x85, 0x6e, 0x16, 0xb9, 0x6d, 0x7a,
0x82, 0x1c, 0x8e, 0xc0, 0x39, 0xb5, 0x03, 0xe3, 0xd8, 0x67, 0x28, 0xc4, 0x94, 0xa9,
0x67, 0xd8, 0x30, 0x11, 0xa0, 0xe0, 0x90, 0xb5, 0xd5, 0x4c, 0xd4, 0x7f, 0x4e, 0x36,
0x6c, 0x09, 0x12, 0xbc, 0x80, 0x8f, 0xbb, 0x2e, 0xa9, 0x6e, 0xfa, 0xc8, 0x8f, 0xb3,
0xeb, 0xec, 0x93, 0x42, 0x73, 0x8e, 0x22, 0x5f, 0x7c, 0x7c, 0x2b, 0x01, 0x1c, 0xe3,
0x75, 0xb5, 0x66, 0x21, 0xa2, 0x06, 0x42, 0xb4, 0xd3, 0x6e, 0x06, 0x0d, 0xb4, 0x52,
0x4a, 0xf1,
];
let priv_key = unsafe {
FieldElement::new_unchecked(UBigInt([
0xc201537b85479813,
0x9a25aaf48ebb519a,
0x5c500064824bed99,
0x0f56db78ca460b05,
]))
};
let super_secure_random_num_generator = || unsafe {
FieldElement::new_unchecked(UBigInt([
0x421187ae0b2f34c6,
0xfb728068d3ae9fac,
0x56bb14e0ab184aa9,
0x6d3e71882c3b83b1,
]))
};
let r = unsafe {
FieldElement::new_unchecked(UBigInt([
0x6473b6a11079b2db,
0x53f42864f508483a,
0xc0baa9fa560b7c4e,
0x976d3a4e9d23326d,
]))
};
let s = unsafe {
FieldElement::new_unchecked(UBigInt([
0x55b8eeefe36e1932,
0x4cfa652ae5017d45,
0x01dcd46e0af462cd,
0x1b766e9ceb71ba6c,
]))
};
let signature = Signature::new(r, s);
let generated_signature = super::sign::<Secp256r1>(
msg,
&priv_key,
Sha256::hash,
super_secure_random_num_generator,
);
assert_eq!(generated_signature, signature);
}
#[test]
fn verify_signature_1() {
let msg = &[
0x59, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6,
0xf2, 0xd9, 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48,
0x29, 0x35, 0xfa, 0xaf, 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2,
0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a,
0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb,
0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, 0x8a, 0xd2, 0x81, 0x75,
0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, 0xc9, 0x34,
0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f,
0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7,
0x9c, 0xe8,
];
let pub_key_x = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0x3c59ff46c271bf83,
0xd3565de94bbfb12f,
0xf033bfa248db8fcc,
0x1ccbe91c075fc7f4,
]))
};
let pub_key_y = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0xdc7ccd5ca89a4ca9,
0x6db7ca93b7404e78,
0x1a1fdb2c0e6113e0,
0xce4014c68811f9a2,
]))
};
let pub_key =
unsafe { ProjectivePoint::new_unchecked(pub_key_x, pub_key_y, FieldElement::ONE) };
let r = unsafe {
FieldElement::new_unchecked(UBigInt([
0xcabb5e6f79c8c2ac,
0x2afd6b1f6a555a7a,
0x8843e3d6629527ed,
0xf3ac8061b514795b,
]))
};
let s = unsafe {
FieldElement::new_unchecked(UBigInt([
0x3ccdda2acc058903,
0xef97b218e96f175a,
0x786c76262bf7371c,
0x8bf77819ca05a6b2,
]))
};
let signature = Signature::new(r, s);
assert_eq!(
super::verify_signature(msg, &pub_key, Sha256::hash, &signature),
Ok(ValidSig)
);
let msg = &[
0x58, 0x05, 0x23, 0x88, 0x77, 0xc7, 0x74, 0x21, 0xf7, 0x3e, 0x43, 0xee, 0x3d, 0xa6,
0xf2, 0xd9, 0xe2, 0xcc, 0xad, 0x5f, 0xc9, 0x42, 0xdc, 0xec, 0x0c, 0xbd, 0x25, 0x48,
0x29, 0x35, 0xfa, 0xaf, 0x41, 0x69, 0x83, 0xfe, 0x16, 0x5b, 0x1a, 0x04, 0x5e, 0xe2,
0xbc, 0xd2, 0xe6, 0xdc, 0xa3, 0xbd, 0xf4, 0x6c, 0x43, 0x10, 0xa7, 0x46, 0x1f, 0x9a,
0x37, 0x96, 0x0c, 0xa6, 0x72, 0xd3, 0xfe, 0xb5, 0x47, 0x3e, 0x25, 0x36, 0x05, 0xfb,
0x1d, 0xdf, 0xd2, 0x80, 0x65, 0xb5, 0x3c, 0xb5, 0x85, 0x8a, 0x8a, 0xd2, 0x81, 0x75,
0xbf, 0x9b, 0xd3, 0x86, 0xa5, 0xe4, 0x71, 0xea, 0x7a, 0x65, 0xc1, 0x7c, 0xc9, 0x34,
0xa9, 0xd7, 0x91, 0xe9, 0x14, 0x91, 0xeb, 0x37, 0x54, 0xd0, 0x37, 0x99, 0x79, 0x0f,
0xe2, 0xd3, 0x08, 0xd1, 0x61, 0x46, 0xd5, 0xc9, 0xb0, 0xd0, 0xde, 0xbd, 0x97, 0xd7,
0x9c, 0xe8,
];
assert_eq!(
super::verify_signature(msg, &pub_key, Sha256::hash, &signature),
Err(InvalidSig)
);
}
#[test]
fn verify_signature_2() {
let msg = &[
0xc3, 0x5e, 0x2f, 0x09, 0x25, 0x53, 0xc5, 0x57, 0x72, 0x92, 0x6b, 0xdb, 0xe8, 0x7c,
0x97, 0x96, 0x82, 0x7d, 0x17, 0x02, 0x4d, 0xbb, 0x92, 0x33, 0xa5, 0x45, 0x36, 0x6e,
0x2e, 0x59, 0x87, 0xdd, 0x34, 0x4d, 0xeb, 0x72, 0xdf, 0x98, 0x71, 0x44, 0xb8, 0xc6,
0xc4, 0x3b, 0xc4, 0x1b, 0x65, 0x4b, 0x94, 0xcc, 0x85, 0x6e, 0x16, 0xb9, 0x6d, 0x7a,
0x82, 0x1c, 0x8e, 0xc0, 0x39, 0xb5, 0x03, 0xe3, 0xd8, 0x67, 0x28, 0xc4, 0x94, 0xa9,
0x67, 0xd8, 0x30, 0x11, 0xa0, 0xe0, 0x90, 0xb5, 0xd5, 0x4c, 0xd4, 0x7f, 0x4e, 0x36,
0x6c, 0x09, 0x12, 0xbc, 0x80, 0x8f, 0xbb, 0x2e, 0xa9, 0x6e, 0xfa, 0xc8, 0x8f, 0xb3,
0xeb, 0xec, 0x93, 0x42, 0x73, 0x8e, 0x22, 0x5f, 0x7c, 0x7c, 0x2b, 0x01, 0x1c, 0xe3,
0x75, 0xb5, 0x66, 0x21, 0xa2, 0x06, 0x42, 0xb4, 0xd3, 0x6e, 0x06, 0x0d, 0xb4, 0x52,
0x4a, 0xf1,
];
let r = unsafe {
FieldElement::new_unchecked(UBigInt([
0x6473b6a11079b2db,
0x53f42864f508483a,
0xc0baa9fa560b7c4e,
0x976d3a4e9d23326d,
]))
};
let s = unsafe {
FieldElement::new_unchecked(UBigInt([
0x55b8eeefe36e1932,
0x4cfa652ae5017d45,
0x01dcd46e0af462cd,
0x1b766e9ceb71ba6c,
]))
};
let signature = Signature::new(r, s);
let pub_key_x = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0x0bf3d4012aeffa8a,
0x2c416044f2d2b8c1,
0x30d4ca3e8f774943,
0xe266ddfdc12668db,
]))
};
let pub_key_y = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0x6928973ab5b1cb39,
0xf456b863b4d02cfc,
0x7d47c587ef7a97a7,
0xbfa86404a2e9ffe6,
]))
};
let pub_key =
unsafe { ProjectivePoint::new_unchecked(pub_key_x, pub_key_y, FieldElement::ONE) };
assert_eq!(
super::verify_signature(msg, &pub_key, Sha256::hash, &signature),
Ok(ValidSig)
);
let r = unsafe {
FieldElement::new_unchecked(UBigInt([
0x7473b6a11079b2db,
0x53f42864f508483a,
0xc0baa9fa560b7c4e,
0x976d3a4e9d23326d,
]))
};
let signature = Signature::new(r, s);
assert_eq!(
super::verify_signature(msg, &pub_key, Sha256::hash, &signature),
Err(InvalidSig)
);
}
}