use alloc::vec::Vec;
use super::{reader, writer};
#[must_use]
pub fn encode_sig(r: &[u8; 32], s: &[u8; 32]) -> Vec<u8> {
let mut body = Vec::with_capacity(72);
writer::write_integer(&mut body, r);
writer::write_integer(&mut body, s);
let mut out = Vec::with_capacity(body.len() + 4);
writer::write_sequence(&mut out, &body);
out
}
#[must_use]
pub fn decode_sig(input: &[u8]) -> Option<([u8; 32], [u8; 32])> {
let (body, rest) = reader::read_sequence(input)?;
if !rest.is_empty() {
return None;
}
let (r, body) = read_scalar_in_range(body)?;
let (s, body) = read_scalar_in_range(body)?;
if !body.is_empty() {
return None;
}
Some((r, s))
}
fn read_scalar_in_range(input: &[u8]) -> Option<([u8; 32], &[u8])> {
let (bytes, rest) = reader::read_integer(input)?;
if bytes == [0x00] {
return None;
}
if bytes.len() > 32 {
return None;
}
let mut padded = [0u8; 32];
padded[32 - bytes.len()..].copy_from_slice(bytes);
Some((padded, rest))
}
#[cfg(test)]
mod tests {
use super::*;
use crypto_bigint::U256;
#[test]
fn round_trip_small() {
let r = crate::u256_to_be32(&U256::from_u64(0x1234));
let s = crate::u256_to_be32(&U256::from_u64(0x5678));
let der = encode_sig(&r, &s);
let (r2, s2) = decode_sig(&der).expect("round-trip");
assert_eq!(r2, r);
assert_eq!(s2, s);
}
#[test]
fn round_trip_large_with_high_bit() {
let r = crate::u256_to_be32(&U256::from_be_hex(
"FF00000000000000000000000000000000000000000000000000000000000001",
));
let s = crate::u256_to_be32(&U256::from_be_hex(
"8000000000000000000000000000000000000000000000000000000000000002",
));
let der = encode_sig(&r, &s);
let (r2, s2) = decode_sig(&der).expect("round-trip");
assert_eq!(r2, r);
assert_eq!(s2, s);
}
#[test]
fn malformed_returns_none() {
assert!(decode_sig(&[]).is_none());
assert!(decode_sig(&[0x30]).is_none()); assert!(decode_sig(&[0x31, 0x00]).is_none()); assert!(decode_sig(&[0x30, 0x05, 0x02, 0x01, 0x01]).is_none()); }
#[test]
fn rejects_non_canonical_leading_zero() {
let bad = [0x30, 0x07, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01];
assert!(
decode_sig(&bad).is_none(),
"non-canonical 00-pad on small int must be rejected"
);
}
#[test]
fn rejects_negative_integer_encoding() {
let bad = [0x30, 0x06, 0x02, 0x01, 0x80, 0x02, 0x01, 0x01];
assert!(
decode_sig(&bad).is_none(),
"high-bit-set first byte without 00 pad must be rejected"
);
}
#[test]
fn rejects_empty_integer() {
let bad = [0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01];
assert!(
decode_sig(&bad).is_none(),
"empty INTEGER content must be rejected"
);
}
#[test]
fn rejects_zero_scalar() {
let bad_r = [0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01];
assert!(decode_sig(&bad_r).is_none());
let bad_s = [0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00];
assert!(decode_sig(&bad_s).is_none());
}
}