use crate::{
crypto::SigningPublicKey, error::parser::OfflineSignatureParseError, runtime::Runtime,
};
use nom::{
bytes::complete::take,
number::complete::{be_u16, be_u32},
Err, IResult,
};
const SIGNATURE_KIND_EDDSA_SHA512_ED25519: u16 = 0x0007;
const SIGNATURE_KIND_ECDSA_SHA256_P256: u16 = 0x0001;
pub struct OfflineSignature;
impl OfflineSignature {
pub fn parse_frame<'a, R: Runtime>(
input: &'a [u8],
key: &SigningPublicKey,
) -> IResult<&'a [u8], SigningPublicKey, OfflineSignatureParseError> {
let signed_segment = input;
let (rest, expires) = be_u32(input)?;
if R::time_since_epoch().as_secs() > expires as u64 {
return Err(Err::Error(OfflineSignatureParseError::Expired));
}
let (rest, signature_kind) = be_u16(rest)?;
let (rest, verifying_key, verifying_key_len) = match signature_kind {
SIGNATURE_KIND_EDDSA_SHA512_ED25519 => {
let (rest, key) = take(32usize)(rest)?;
let verifying_key = SigningPublicKey::from_bytes(
&TryInto::<[u8; 32]>::try_into(key).expect("to succeed"),
)
.ok_or(Err::Error(OfflineSignatureParseError::InvalidPublicKey))?;
(rest, verifying_key, 32usize)
}
SIGNATURE_KIND_ECDSA_SHA256_P256 => {
let (rest, key) = take(64usize)(rest)?;
let verifying_key = SigningPublicKey::p256(key)
.ok_or(Err::Error(OfflineSignatureParseError::InvalidPublicKey))?;
(rest, verifying_key, 64usize)
}
_ => {
return Err(Err::Error(
OfflineSignatureParseError::UnsupportedPublicKey(signature_kind),
));
}
};
let (rest, signature) = take(verifying_key.signature_len())(rest)?;
key.verify(&signed_segment[..(6 + verifying_key_len)], signature)
.map_err(|_| Err::Error(OfflineSignatureParseError::InvalidSignature))?;
Ok((rest, verifying_key))
}
}