use super::{EcdsaSignatureError, JwsAlgorithm};
pub fn ecdsa_signature_from_der(
alg: JwsAlgorithm,
der: &[u8],
) -> Result<Vec<u8>, EcdsaSignatureError> {
let size = component_size(alg)?;
let (r, s) = parse(der)?;
let mut signature = vec![0; 2 * size];
for (component, out) in [r, s].into_iter().zip(signature.chunks_exact_mut(size)) {
if component.len() > size {
return Err(EcdsaSignatureError::ComponentTooLong {
alg,
len: component.len(),
});
}
out[size - component.len()..].copy_from_slice(component);
}
normalize(alg, signature)
}
pub fn normalize_ecdsa_signature(
alg: JwsAlgorithm,
signature: &[u8],
) -> Result<Vec<u8>, EcdsaSignatureError> {
let size = component_size(alg)?;
if signature.len() != 2 * size {
return Err(EcdsaSignatureError::InvalidLength {
alg,
expected: 2 * size,
actual: signature.len(),
});
}
normalize(alg, signature.to_vec())
}
fn component_size(alg: JwsAlgorithm) -> Result<usize, EcdsaSignatureError> {
match alg {
JwsAlgorithm::ES256 | JwsAlgorithm::ES256K => Ok(32),
JwsAlgorithm::ES384 => Ok(48),
JwsAlgorithm::EdDSA | JwsAlgorithm::RS256 | JwsAlgorithm::PS256 => {
Err(EcdsaSignatureError::NotEcdsa(alg))
}
}
}
fn normalize(alg: JwsAlgorithm, signature: Vec<u8>) -> Result<Vec<u8>, EcdsaSignatureError> {
if alg != JwsAlgorithm::ES256K {
return Ok(signature);
}
#[cfg(feature = "secp256k1")]
{
let parsed = k256::ecdsa::Signature::from_slice(&signature)
.map_err(|_| EcdsaSignatureError::OutOfRange(alg))?;
Ok(parsed.normalize_s().to_bytes().to_vec())
}
#[cfg(not(feature = "secp256k1"))]
{
let _ = signature;
Err(EcdsaSignatureError::Unsupported(alg))
}
}
fn parse(der: &[u8]) -> Result<(&[u8], &[u8]), EcdsaSignatureError> {
let (sequence, rest) = read(der, SEQUENCE)?;
if !rest.is_empty() {
return Err(EcdsaSignatureError::InvalidDer(
"trailing bytes after the sequence",
));
}
let (r, rest) = integer(sequence)?;
let (s, rest) = integer(rest)?;
if !rest.is_empty() {
return Err(EcdsaSignatureError::InvalidDer(
"more than two elements in the sequence",
));
}
Ok((r, s))
}
const SEQUENCE: u8 = 0x30;
const INTEGER: u8 = 0x02;
fn integer(input: &[u8]) -> Result<(&[u8], &[u8]), EcdsaSignatureError> {
let (content, rest) = read(input, INTEGER)?;
let magnitude = match content {
[] => return Err(EcdsaSignatureError::InvalidDer("empty integer")),
[first, ..] if first & 0x80 != 0 => {
return Err(EcdsaSignatureError::InvalidDer("negative integer"));
}
[0] => return Err(EcdsaSignatureError::InvalidDer("zero integer")),
[0, second, ..] if second & 0x80 == 0 => {
return Err(EcdsaSignatureError::InvalidDer(
"integer with a superfluous leading zero",
));
}
[0, magnitude @ ..] | magnitude => magnitude,
};
Ok((magnitude, rest))
}
fn read(input: &[u8], tag: u8) -> Result<(&[u8], &[u8]), EcdsaSignatureError> {
let (&found, input) = input
.split_first()
.ok_or(EcdsaSignatureError::InvalidDer("truncated element"))?;
if found != tag {
return Err(EcdsaSignatureError::InvalidDer(if tag == SEQUENCE {
"not a sequence"
} else {
"not an integer"
}));
}
let (&first, input) = input
.split_first()
.ok_or(EcdsaSignatureError::InvalidDer("truncated length"))?;
let (len, input) = match first {
0x00..=0x7f => (usize::from(first), input),
0x81 => match input.split_first() {
Some((&len, input)) if len >= 0x80 => (usize::from(len), input),
Some(_) => {
return Err(EcdsaSignatureError::InvalidDer(
"length not minimally encoded",
));
}
None => return Err(EcdsaSignatureError::InvalidDer("truncated length")),
},
0x80 => return Err(EcdsaSignatureError::InvalidDer("indefinite length")),
_ => {
return Err(EcdsaSignatureError::InvalidDer(
"length too long for a signature",
));
}
};
if input.len() < len {
return Err(EcdsaSignatureError::InvalidDer("truncated element"));
}
Ok(input.split_at(len))
}