use alloc::vec::Vec;
use crypto_bigint::U256;
use super::{reader, writer};
#[must_use]
pub fn encode_sig(r: &U256, s: &U256) -> Vec<u8> {
let r_be = r.to_be_bytes();
let s_be = s.to_be_bytes();
let mut body = Vec::with_capacity(72);
writer::write_integer(&mut body, &r_be);
writer::write_integer(&mut body, &s_be);
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<(U256, U256)> {
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<(U256, &[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((U256::from_be_slice(&padded), rest))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_small() {
let r = U256::from_u64(0x1234);
let s = 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 =
U256::from_be_hex("FF00000000000000000000000000000000000000000000000000000000000001");
let s =
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());
}
}