use crate::der::read_element;
use crate::{Error, Result};
use sha2::Digest;
const TAG_SEQUENCE: u8 = 0x30;
const TAG_INTEGER: u8 = 0x02;
const TAG_VERSION: u8 = 0xa0;
pub(crate) fn certificate_elements<'a>(
certificate_der: &'a [u8],
) -> Result<(
crate::der::Element<'a>,
crate::der::Element<'a>,
crate::der::Element<'a>,
)> {
let (outer, rest) = read_element(certificate_der).ok_or(Error::Malformed("certificate DER"))?;
if outer.tag != TAG_SEQUENCE || !rest.is_empty() {
return Err(Error::Malformed("certificate DER"));
}
let (tbs, rest) = read_element(outer.content).ok_or(Error::Malformed("TBSCertificate"))?;
if tbs.tag != TAG_SEQUENCE {
return Err(Error::Malformed("TBSCertificate"));
}
let (algorithm, rest) = read_element(rest).ok_or(Error::Malformed("signatureAlgorithm"))?;
let (signature, rest) = read_element(rest).ok_or(Error::Malformed("signatureValue"))?;
if !rest.is_empty() {
return Err(Error::Malformed("certificate DER"));
}
Ok((tbs, algorithm, signature))
}
pub fn entry_hash_from_certificate(
certificate_der: &[u8],
entry_extensions: &[u8],
) -> Result<[u8; 32]> {
if entry_extensions.len() > 0xffff {
return Err(Error::Malformed("entry extensions"));
}
let (tbs, _, _) = certificate_elements(certificate_der)?;
let mut hash = sha2::Sha256::new();
hash.update([0x00]);
hash.update((entry_extensions.len() as u16).to_be_bytes());
hash.update(entry_extensions);
hash.update([0x00, 0x01]);
let mut input = tbs.content;
if let Some((version, rest)) = read_element(input)
&& version.tag == TAG_VERSION
{
hash.update(version.encoded);
input = rest;
}
let serial = crate::der::next_element(&mut input, "serial number")?;
if serial.tag != TAG_INTEGER {
return Err(Error::Malformed("serial number"));
}
let skipped = crate::der::next_element(&mut input, "signature algorithm")?;
if skipped.tag != TAG_SEQUENCE {
return Err(Error::Malformed("signature algorithm"));
}
for field in ["issuer", "validity", "subject"] {
let element = crate::der::next_element(&mut input, field)?;
if element.tag != TAG_SEQUENCE {
return Err(Error::Malformed(field));
}
hash.update(element.encoded);
}
let spki = crate::der::next_element(&mut input, "subjectPublicKeyInfo")?;
if spki.tag != TAG_SEQUENCE {
return Err(Error::Malformed("subjectPublicKeyInfo"));
}
let (spki_algorithm, _) =
read_element(spki.content).ok_or(Error::Malformed("subjectPublicKeyAlgorithm"))?;
if spki_algorithm.tag != TAG_SEQUENCE {
return Err(Error::Malformed("subjectPublicKeyAlgorithm"));
}
hash.update(spki_algorithm.encoded);
let spki_hash: [u8; 32] = sha2::Sha256::digest(spki.encoded).into();
hash.update([0x04, 32]);
hash.update(spki_hash);
hash.update(input);
Ok(hash.finalize().into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn malformed_certificates_are_rejected() {
assert!(entry_hash_from_certificate(&[], &[0, 0]).is_err());
assert!(entry_hash_from_certificate(&[0x30, 0], &[0, 0]).is_err());
assert!(entry_hash_from_certificate(&[0x30, 3, 0x30, 0, 5], &[0, 0]).is_err());
assert!(entry_hash_from_certificate(&[0x30, 0], &[0]).is_err());
}
}