use super::curve::*;
use super::curves::{CryptoRng, PublicKey, SecretKey};
use super::field::*;
use crate::Hasher;
#[derive(Clone, Debug)]
pub struct Signature {
pub r: Vec<u8>,
pub s: Vec<u8>,
}
impl Signature {
pub fn to_der(&self) -> Vec<u8> {
let r_der = encode_der_integer(&self.r);
let s_der = encode_der_integer(&self.s);
let payload_len = r_der.len() + s_der.len();
let mut out = Vec::with_capacity(4 + payload_len);
out.push(0x30); encode_der_len(&mut out, payload_len);
out.extend_from_slice(&r_der);
out.extend_from_slice(&s_der);
out
}
pub fn from_der(der: &[u8]) -> Option<Self> {
let (seq_tag, rest) = (*der.first()?, &der[1..]);
if seq_tag != 0x30 {
return None;
}
let (payload_len, rest) = decode_der_len(rest)?;
if rest.len() != payload_len {
return None;
}
let (r, rest) = decode_der_integer(rest)?;
let (s, tail) = decode_der_integer(rest)?;
if !tail.is_empty() {
return None;
}
if r.iter().all(|&b| b == 0) || s.iter().all(|&b| b == 0) {
return None;
}
Some(Signature { r, s })
}
}
fn encode_der_len(out: &mut Vec<u8>, len: usize) {
if len < 0x80 {
out.push(len as u8);
} else if len < 0x100 {
out.push(0x81);
out.push(len as u8);
} else if len < 0x10000 {
out.push(0x82);
out.push((len >> 8) as u8);
out.push((len & 0xff) as u8);
} else {
panic!("DER length > 65535 is not supported by this encoder");
}
}
fn decode_der_len(bytes: &[u8]) -> Option<(usize, &[u8])> {
let first = *bytes.first()?;
if first < 0x80 {
Some((first as usize, &bytes[1..]))
} else if first == 0x81 {
let b = *bytes.get(1)?;
if b < 0x80 {
return None;
}
Some((b as usize, &bytes[2..]))
} else if first == 0x82 {
let b1 = *bytes.get(1)?;
let b2 = *bytes.get(2)?;
let len = ((b1 as usize) << 8) | (b2 as usize);
if len < 0x100 {
return None;
}
Some((len, &bytes[3..]))
} else {
None
}
}
fn encode_der_integer(be: &[u8]) -> Vec<u8> {
let mut i = 0;
while i < be.len() && be[i] == 0 {
i += 1;
}
let stripped: &[u8] = if i == be.len() { &[0u8] } else { &be[i..] };
let needs_pad = !stripped.is_empty() && (stripped[0] & 0x80) != 0;
let body_len = stripped.len() + if needs_pad { 1 } else { 0 };
let mut out = Vec::with_capacity(2 + body_len);
out.push(0x02); encode_der_len(&mut out, body_len);
if needs_pad {
out.push(0x00);
}
out.extend_from_slice(stripped);
out
}
fn decode_der_integer(bytes: &[u8]) -> Option<(Vec<u8>, &[u8])> {
let tag = *bytes.first()?;
if tag != 0x02 {
return None;
}
let (len, rest) = decode_der_len(&bytes[1..])?;
if len == 0 || rest.len() < len {
return None;
}
let (content, tail) = rest.split_at(len);
if content[0] & 0x80 != 0 {
return None;
}
if content.len() > 1 && content[0] == 0x00 && (content[1] & 0x80) == 0 {
return None;
}
let out = if content[0] == 0x00 && content.len() > 1 {
content[1..].to_vec()
} else {
content.to_vec()
};
Some((out, tail))
}
fn hmac<H: Hasher>(key: &[u8], data: &[u8]) -> Vec<u8> {
let block_len = H::BLOCK_LEN;
let k = if key.len() > block_len {
H::hash(key)
} else {
key.to_vec()
};
let mut k_padded = vec![0u8; block_len];
k_padded[..k.len()].copy_from_slice(&k);
let mut ipad = vec![0u8; block_len];
for i in 0..block_len {
ipad[i] = k_padded[i] ^ 0x36;
}
let mut opad = vec![0u8; block_len];
for i in 0..block_len {
opad[i] = k_padded[i] ^ 0x5c;
}
let mut inner_hasher = H::new();
inner_hasher.update(&ipad);
inner_hasher.update(data);
let inner = inner_hasher.finalize();
let mut outer_hasher = H::new();
outer_hasher.update(&opad);
outer_hasher.update(&inner);
outer_hasher.finalize()
}
fn hmac_multi<H: Hasher>(key: &[u8], parts: &[&[u8]]) -> Vec<u8> {
let block_len = H::BLOCK_LEN;
let k = if key.len() > block_len {
H::hash(key)
} else {
key.to_vec()
};
let mut k_padded = vec![0u8; block_len];
k_padded[..k.len()].copy_from_slice(&k);
let mut ipad = vec![0u8; block_len];
let mut opad = vec![0u8; block_len];
for i in 0..block_len {
ipad[i] = k_padded[i] ^ 0x36;
opad[i] = k_padded[i] ^ 0x5c;
}
let mut inner_hasher = H::new();
inner_hasher.update(&ipad);
for part in parts {
inner_hasher.update(part);
}
let inner = inner_hasher.finalize();
let mut outer_hasher = H::new();
outer_hasher.update(&opad);
outer_hasher.update(&inner);
outer_hasher.finalize()
}
fn bits2int<const LIMBS: usize>(input: &[u8], qlen_bits: usize) -> FieldElement<LIMBS> {
let rlen_bytes = (qlen_bits + 7) / 8;
let take = input.len().min(rlen_bytes);
let mut fe = FieldElement::<LIMBS>::from_bytes_be(&input[..take]);
let take_bits = take * 8;
if take_bits > qlen_bits {
shr_limbs(&mut fe, (take_bits - qlen_bits) as u32);
}
fe
}
fn shr_limbs<const LIMBS: usize>(x: &mut FieldElement<LIMBS>, n: u32) {
debug_assert!(n > 0 && n < 64, "shr_limbs supports 1..=63");
let lo = n;
let hi = 64 - n;
for i in 0..LIMBS - 1 {
x.limbs[i] = (x.limbs[i] >> lo) | (x.limbs[i + 1] << hi);
}
x.limbs[LIMBS - 1] >>= lo;
}
fn int2octets(x_bytes: &[u8], rlen_bytes: usize) -> Vec<u8> {
use core::cmp::Ordering;
match x_bytes.len().cmp(&rlen_bytes) {
Ordering::Equal => x_bytes.to_vec(),
Ordering::Greater => x_bytes[x_bytes.len() - rlen_bytes..].to_vec(),
Ordering::Less => {
let mut out = vec![0u8; rlen_bytes];
out[rlen_bytes - x_bytes.len()..].copy_from_slice(x_bytes);
out
}
}
}
fn rfc6979_k<H: Hasher, const LIMBS: usize>(
x_bytes: &[u8],
h1: &[u8],
n: &[u64; LIMBS],
qlen_bits: usize,
) -> FieldElement<LIMBS> {
let hlen = H::OUTPUT_LEN;
let rlen_bytes = (qlen_bits + 7) / 8;
let x_octets = int2octets(x_bytes, rlen_bytes);
let z1 = bits2int::<LIMBS>(h1, qlen_bits);
let z1_reduced = reduce_mod_n(&z1, n);
let z1_octets = int2octets(&z1_reduced.to_bytes_be(), rlen_bytes);
let mut v = vec![0x01u8; hlen];
let mut k = vec![0x00u8; hlen];
k = hmac_multi::<H>(&k, &[&v, &[0x00], &x_octets, &z1_octets]);
v = hmac::<H>(&k, &v);
k = hmac_multi::<H>(&k, &[&v, &[0x01], &x_octets, &z1_octets]);
v = hmac::<H>(&k, &v);
loop {
let mut t = Vec::new();
while t.len() < rlen_bytes {
v = hmac::<H>(&k, &v);
t.extend_from_slice(&v);
}
let candidate = bits2int::<LIMBS>(&t[..rlen_bytes], qlen_bits);
if !candidate.is_zero() && scalar_is_valid(&candidate, n) {
return candidate;
}
k = hmac_multi::<H>(&k, &[&v, &[0x00]]);
v = hmac::<H>(&k, &v);
}
}
fn reduce_mod_n<const LIMBS: usize>(a: &FieldElement<LIMBS>, n: &[u64; LIMBS]) -> FieldElement<LIMBS> {
let mut result = *a;
for _ in 0..2 {
let mut borrow: u64 = 0;
let mut sub = [0u64; LIMBS];
for i in 0..LIMBS {
let diff = (result.limbs[i] as u128)
.wrapping_sub(n[i] as u128)
.wrapping_sub(borrow as u128);
sub[i] = diff as u64;
borrow = ((diff >> 64) as u64) & 1;
}
let mask = 0u64.wrapping_sub(1 - borrow); let inv_mask = !mask;
for i in 0..LIMBS {
result.limbs[i] = (sub[i] & mask) | (result.limbs[i] & inv_mask);
}
}
result
}
pub(super) fn parse_and_validate_pubkey<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
pk: &PublicKey,
) -> Option<JacobianPoint<LIMBS>> {
let felem = params.felem_bytes;
let bytes = pk.bytes.as_slice();
let (qx, qy) = match bytes.first().copied()? {
0x04 => {
if bytes.len() != 1 + 2 * felem {
return None;
}
let qx = FieldElement::<LIMBS>::from_bytes_be(&bytes[1..1 + felem]);
let qy = FieldElement::<LIMBS>::from_bytes_be(&bytes[1 + felem..1 + 2 * felem]);
(qx, qy)
}
tag @ (0x02 | 0x03) => {
if bytes.len() != 1 + felem {
return None;
}
let qx = FieldElement::<LIMBS>::from_bytes_be(&bytes[1..1 + felem]);
let p = ¶ms.p;
let x2 = field_sqr(&qx, p);
let x3 = field_mul(&x2, &qx, p);
let ax = field_mul(¶ms.a, &qx, p);
let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
let mut qy = field_sqrt_p3mod4(&rhs, p);
let want_odd = (tag & 1) == 1;
let have_odd = (qy.limbs[0] & 1) == 1;
if have_odd != want_odd {
qy = field_neg(&qy, p);
}
(qx, qy)
}
_ => return None,
};
if !is_on_curve(&qx, &qy, params) {
return None;
}
Some(JacobianPoint::from_affine(qx, qy))
}
pub(super) fn fe_to_felem_bytes<const LIMBS: usize>(fe: &FieldElement<LIMBS>, felem_bytes: usize) -> Vec<u8> {
let full = fe.to_bytes_be();
debug_assert!(felem_bytes <= full.len());
full[full.len() - felem_bytes..].to_vec()
}
pub(super) fn compress_pubkey_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
pk: &PublicKey,
) -> Option<Vec<u8>> {
let felem = params.felem_bytes;
let bytes = pk.bytes.as_slice();
let tag = bytes.first().copied()?;
match tag {
0x04 => {
if bytes.len() != 1 + 2 * felem {
return None;
}
parse_and_validate_pubkey::<LIMBS>(params, pk)?;
let y_last = 2 * felem;
let tag_out = 0x02 | (bytes[y_last] & 1);
let mut out = Vec::with_capacity(1 + felem);
out.push(tag_out);
out.extend_from_slice(&bytes[1..1 + felem]);
Some(out)
}
0x02 | 0x03 => {
if bytes.len() != 1 + felem {
return None;
}
parse_and_validate_pubkey::<LIMBS>(params, pk)?;
Some(bytes.to_vec())
}
_ => None,
}
}
pub(super) fn decompress_pubkey_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
compressed: &[u8],
) -> Option<PublicKey> {
let felem = params.felem_bytes;
let pk = PublicKey {
bytes: compressed.to_vec(),
};
let point = parse_and_validate_pubkey::<LIMBS>(params, &pk)?;
let (qx, qy) = point.to_affine(¶ms.p)?;
let mut out = Vec::with_capacity(1 + 2 * felem);
out.push(0x04);
out.extend_from_slice(&fe_to_felem_bytes(&qx, felem));
out.extend_from_slice(&fe_to_felem_bytes(&qy, felem));
Some(PublicKey { bytes: out })
}
pub(super) fn ecdh_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
sk: &SecretKey,
peer_pk: &PublicKey,
) -> Option<Vec<u8>> {
let peer_point = parse_and_validate_pubkey::<LIMBS>(params, peer_pk)?;
let d = FieldElement::<LIMBS>::from_bytes_be(&sk.bytes);
if d.is_zero() || !scalar_is_valid(&d, ¶ms.n) {
return None;
}
let shared = scalar_mul_point(&d, &peer_point, params);
let (sx, _sy) = shared.to_affine(¶ms.p)?;
Some(fe_to_felem_bytes(&sx, params.felem_bytes))
}
fn fill_bytes_masked(buf: &mut [u8], qlen_bits: usize, rng: &mut dyn CryptoRng) {
rng.fill_bytes(buf);
let excess = buf.len() * 8 - qlen_bits;
if excess > 0 && !buf.is_empty() {
buf[0] &= (1u8 << (8 - excess)) - 1;
}
}
pub(super) fn keygen_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
rng: &mut dyn CryptoRng,
) -> (PublicKey, SecretKey) {
let g = JacobianPoint::from_affine(params.gx, params.gy);
let felem = params.felem_bytes;
let rlen_bytes = (params.qlen_bits + 7) / 8;
loop {
let mut sk_bytes = vec![0u8; rlen_bytes];
fill_bytes_masked(&mut sk_bytes, params.qlen_bits, rng);
debug_assert_eq!(rlen_bytes, felem);
let d = FieldElement::<LIMBS>::from_bytes_be(&sk_bytes);
if d.is_zero() || !scalar_is_valid(&d, ¶ms.n) {
continue;
}
let q = scalar_mul_point(&d, &g, params);
let (qx, qy) = q.to_affine(¶ms.p).unwrap();
let mut pk_bytes = Vec::with_capacity(1 + 2 * felem);
pk_bytes.push(0x04);
pk_bytes.extend_from_slice(&fe_to_felem_bytes(&qx, felem));
pk_bytes.extend_from_slice(&fe_to_felem_bytes(&qy, felem));
return (PublicKey { bytes: pk_bytes }, SecretKey { bytes: sk_bytes });
}
}
fn try_sign_with_k<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
g: &JacobianPoint<LIMBS>,
d: &FieldElement<LIMBS>,
e: &FieldElement<LIMBS>,
k: &FieldElement<LIMBS>,
) -> Option<Signature> {
let n = ¶ms.n;
let kg = scalar_mul_point(k, g, params);
let (x1, _y1) = kg.to_affine(¶ms.p)?;
let r = reduce_mod_n(&x1, n);
if r.is_zero() {
return None;
}
let k_inv = scalar_inv(k, n);
let rd = scalar_mul(&r, d, n);
let e_plus_rd = scalar_add(e, &rd, n);
let s = scalar_mul(&k_inv, &e_plus_rd, n);
if s.is_zero() {
return None;
}
Some(Signature {
r: fe_to_felem_bytes(&r, params.felem_bytes),
s: fe_to_felem_bytes(&s, params.felem_bytes),
})
}
fn sample_random_scalar<const LIMBS: usize>(
n: &[u64; LIMBS],
qlen_bits: usize,
rng: &mut dyn CryptoRng,
) -> FieldElement<LIMBS> {
let rlen_bytes = (qlen_bits + 7) / 8;
let mut buf = vec![0u8; rlen_bytes];
loop {
fill_bytes_masked(&mut buf, qlen_bits, rng);
let candidate = FieldElement::<LIMBS>::from_bytes_be(&buf);
if !candidate.is_zero() && scalar_is_valid(&candidate, n) {
return candidate;
}
}
}
pub(super) fn sign_random_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
sk: &SecretKey,
digest: &[u8],
rng: &mut dyn CryptoRng,
) -> Signature {
let g = JacobianPoint::from_affine(params.gx, params.gy);
let e = bits2int::<LIMBS>(digest, params.qlen_bits);
let e = reduce_mod_n(&e, ¶ms.n);
let d = FieldElement::<LIMBS>::from_bytes_be(&sk.bytes);
loop {
let k = sample_random_scalar::<LIMBS>(¶ms.n, params.qlen_bits, rng);
if let Some(sig) = try_sign_with_k::<LIMBS>(params, &g, &d, &e, &k) {
return sig;
}
}
}
pub(super) fn sign_rfc6979_internal<H: Hasher, const LIMBS: usize>(
params: &CurveParams<LIMBS>,
sk: &SecretKey,
digest: &[u8],
) -> Signature {
let g = JacobianPoint::from_affine(params.gx, params.gy);
let e = bits2int::<LIMBS>(digest, params.qlen_bits);
let e = reduce_mod_n(&e, ¶ms.n);
let d = FieldElement::<LIMBS>::from_bytes_be(&sk.bytes);
loop {
let k = rfc6979_k::<H, LIMBS>(&sk.bytes, digest, ¶ms.n, params.qlen_bits);
if let Some(sig) = try_sign_with_k::<LIMBS>(params, &g, &d, &e, &k) {
return sig;
}
}
}
pub(super) fn verify_internal<const LIMBS: usize>(
params: &CurveParams<LIMBS>,
pk: &PublicKey,
digest: &[u8],
sig: &Signature,
) -> bool {
let n = ¶ms.n;
let q = match parse_and_validate_pubkey::<LIMBS>(params, pk) {
Some(q) => q,
None => return false,
};
let felem_bytes = LIMBS * 8;
if sig.r.len() > felem_bytes || sig.s.len() > felem_bytes {
return false;
}
let r = FieldElement::<LIMBS>::from_bytes_be(&sig.r);
let s = FieldElement::<LIMBS>::from_bytes_be(&sig.s);
if r.is_zero() || s.is_zero() {
return false;
}
if !scalar_is_valid(&r, n) || !scalar_is_valid(&s, n) {
return false;
}
let e = bits2int::<LIMBS>(digest, params.qlen_bits);
let e = reduce_mod_n(&e, n);
let w = scalar_inv(&s, n);
let u1 = scalar_mul(&e, &w, n);
let u2 = scalar_mul(&r, &w, n);
let g = JacobianPoint::from_affine(params.gx, params.gy);
let point = double_scalar_mul(&u1, &g, &u2, &q, params);
let (x1, _y1) = match point.to_affine(¶ms.p) {
Some(pt) => pt,
None => return false,
};
let v = reduce_mod_n(&x1, n);
r == v
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::sha256::Sha256;
fn hex_to_bytes(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn test_rfc6979_p256_nonce() {
let sk_bytes = hex_to_bytes("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
let msg = b"sample";
let e_hash = Sha256::hash(msg);
let k = rfc6979_k::<Sha256, 4>(&sk_bytes, &e_hash, &P256_N, 256);
let k_bytes = k.to_bytes_be();
let expected_k = hex_to_bytes("A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60");
assert_eq!(k_bytes, expected_k, "RFC 6979 nonce k mismatch");
}
#[test]
fn test_der_rfc6979_p256_sample_vector() {
let r = hex_to_bytes("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716");
let s = hex_to_bytes("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
let sig = Signature { r, s };
let expected = hex_to_bytes(
"3046\
02210\
0EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716\
0221\
00F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8",
);
assert_eq!(sig.to_der(), expected);
let parsed = Signature::from_der(&expected).unwrap();
assert_eq!(parsed.r, sig.r);
assert_eq!(parsed.s, sig.s);
}
#[test]
fn test_der_rejects_malformed() {
assert!(Signature::from_der(&[]).is_none());
assert!(Signature::from_der(&[0x31, 0x00]).is_none());
assert!(Signature::from_der(&[0x30, 0x00]).is_none());
assert!(Signature::from_der(&[0x30, 0x06, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x03, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x81, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x08, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x06, 0x02, 0x01, 0x80, 0x02, 0x01, 0x01]).is_none());
assert!(Signature::from_der(&[0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0xAB]).is_none());
}
#[test]
fn test_der_small_integers_roundtrip() {
let sig = Signature {
r: vec![0x01],
s: vec![0x01],
};
let der = sig.to_der();
assert_eq!(der, vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01]);
let back = Signature::from_der(&der).unwrap();
assert_eq!(back.r, vec![0x01]);
assert_eq!(back.s, vec![0x01]);
}
}