use crate::{
asn1_signature::{self, Asn1Signature},
Curve, FixedSignature,
};
use core::{marker::PhantomData, ops::Add};
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use signature::Signature;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum Asn1Tag {
Integer = 0x02,
Sequence = 0x30,
}
pub struct ScalarPair<'a, C: Curve + 'a> {
r: &'a [u8],
s: &'a [u8],
curve: PhantomData<C>,
}
impl<'a, C: Curve + 'a> ScalarPair<'a, C>
where
asn1_signature::MaxSize<C::ScalarSize>: ArrayLength<u8>,
<C::ScalarSize as Add>::Output: ArrayLength<u8> + Add<asn1_signature::MaxOverhead>,
{
pub(crate) fn from_asn1_signature(signature: &'a Asn1Signature<C>) -> Option<Self> {
let mut bytes = signature.as_bytes();
if bytes[0] != Asn1Tag::Sequence as u8 {
return None;
}
let mut zlen = bytes[1] as usize;
if zlen > 0x80 {
if zlen != 0x81 {
return None;
}
zlen = bytes[2] as usize;
if zlen != bytes.len().checked_sub(3).unwrap() {
return None;
}
bytes = &bytes[3..];
} else {
if zlen != bytes.len().checked_sub(2).unwrap() {
return None;
}
bytes = &bytes[2..];
}
let (mut r, bytes) = Self::asn1_int_parse(bytes)?;
let (mut s, bytes) = Self::asn1_int_parse(bytes)?;
if !bytes.is_empty() {
return None;
}
let scalar_size = C::ScalarSize::to_usize();
if r.len() > scalar_size {
if r.len() != scalar_size.checked_add(1).unwrap() {
return None;
}
if r[0] != 0 {
return None;
}
r = &r[1..];
}
if s.len() > scalar_size {
if s.len() != scalar_size.checked_add(1).unwrap() {
return None;
}
if s[0] != 0 {
return None;
}
s = &s[1..];
}
while !r.is_empty() && r[0] == 0 {
r = &r[1..];
}
while !s.is_empty() && s[0] == 0 {
s = &s[1..];
}
Some(Self {
r,
s,
curve: PhantomData,
})
}
pub(crate) fn from_fixed_signature(signature: &'a FixedSignature<C>) -> Self {
let scalar_size = C::ScalarSize::to_usize();
Self {
r: &signature.as_ref()[..scalar_size],
s: &signature.as_ref()[scalar_size..],
curve: PhantomData,
}
}
pub(crate) fn to_asn1_signature(&self) -> Asn1Signature<C> {
let rlen = Self::asn1_int_length(self.r);
let slen = Self::asn1_int_length(self.s);
let mut bytes = GenericArray::default();
bytes[0] = Asn1Tag::Sequence as u8;
let zlen = rlen.checked_add(slen).unwrap().checked_add(4).unwrap();
let mut offset = if zlen >= 0x80 {
bytes[1] = 0x81;
bytes[2] = zlen as u8;
3
} else {
bytes[1] = zlen as u8;
2
};
Self::asn1_int_serialize(self.r, &mut bytes[offset..], rlen);
offset = offset.checked_add(2).unwrap().checked_add(rlen).unwrap();
Self::asn1_int_serialize(self.s, &mut bytes[offset..], slen);
Asn1Signature {
bytes,
length: offset.checked_add(2).unwrap().checked_add(slen).unwrap(),
}
}
pub(crate) fn to_fixed_signature(&self) -> FixedSignature<C> {
let mut bytes = GenericArray::default();
let scalar_size = C::ScalarSize::to_usize();
let rbegin = scalar_size.checked_sub(self.r.len()).unwrap();
bytes.as_mut_slice()[rbegin..scalar_size].copy_from_slice(self.r);
let sbegin = bytes.len().checked_sub(self.s.len()).unwrap();
bytes.as_mut_slice()[sbegin..].copy_from_slice(self.s);
FixedSignature::from(bytes)
}
fn asn1_int_length(mut x: &[u8]) -> usize {
while !x.is_empty() && x[0] == 0 {
x = &x[1..];
}
if x.is_empty() || x[0] >= 0x80 {
x.len().checked_add(1).unwrap()
} else {
x.len()
}
}
fn asn1_int_parse(bytes: &[u8]) -> Option<(&[u8], &[u8])> {
if bytes.len() < 3 {
return None;
}
if bytes[0] != Asn1Tag::Integer as u8 {
return None;
}
let len = bytes[1] as usize;
if len >= 0x80 || len.checked_add(2).unwrap() > bytes.len() {
return None;
}
let integer = &bytes[2..len.checked_add(2).unwrap()];
let remaining = &bytes[len.checked_add(2).unwrap()..];
Some((integer, remaining))
}
fn asn1_int_serialize(scalar: &[u8], out: &mut [u8], len: usize) {
out[0] = Asn1Tag::Integer as u8;
out[1] = len as u8;
if len > C::ScalarSize::to_usize() {
out[2] = 0x00;
out[3..C::ScalarSize::to_usize().checked_add(3).unwrap()].copy_from_slice(scalar);
} else {
out[2..len.checked_add(2).unwrap()]
.copy_from_slice(&scalar[C::ScalarSize::to_usize().checked_sub(len).unwrap()..]);
}
}
}
impl<'a, C: Curve> From<&'a Asn1Signature<C>> for FixedSignature<C>
where
asn1_signature::MaxSize<C::ScalarSize>: ArrayLength<u8>,
<C::ScalarSize as Add>::Output: ArrayLength<u8> + Add<asn1_signature::MaxOverhead>,
{
fn from(asn1_signature: &Asn1Signature<C>) -> FixedSignature<C> {
ScalarPair::from_asn1_signature(asn1_signature)
.unwrap()
.to_fixed_signature()
}
}
impl<'a, C: Curve> From<&'a FixedSignature<C>> for Asn1Signature<C>
where
asn1_signature::MaxSize<C::ScalarSize>: ArrayLength<u8>,
<C::ScalarSize as Add>::Output: ArrayLength<u8> + Add<asn1_signature::MaxOverhead>,
{
fn from(fixed_signature: &FixedSignature<C>) -> Self {
ScalarPair::from_fixed_signature(fixed_signature).to_asn1_signature()
}
}
#[cfg(all(test, feature = "test-vectors"))]
mod tests {
use crate::{
curve::nistp256::{Asn1Signature, FixedSignature},
test_vectors::nistp256::SHA256_FIXED_SIZE_TEST_VECTORS,
};
use signature::Signature;
#[test]
fn test_fixed_to_asn1_signature_roundtrip() {
for vector in SHA256_FIXED_SIZE_TEST_VECTORS {
let fixed_signature = FixedSignature::from_bytes(&vector.sig).unwrap();
let asn1_signature = Asn1Signature::from(&fixed_signature);
let fixed_signature2 = FixedSignature::from(&asn1_signature);
assert_eq!(fixed_signature, fixed_signature2);
}
}
}