use super::AlgorithmIdentifier;
use crate::error::{Error, Result};
use crate::oid;
use structured_asn1::Oid;
use structured_asn1::digest::{Digest, Resolution};
use structured_asn1::memory::Work;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Algorithm {
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
Sha512_224,
Sha512_256,
}
impl Algorithm {
#[must_use]
pub const fn width(self) -> usize {
match self {
Self::Sha1 => 20,
Self::Sha224 | Self::Sha512_224 => 28,
Self::Sha256 | Self::Sha512_256 => 32,
Self::Sha384 => 48,
Self::Sha512 => 64,
}
}
#[must_use]
pub const fn identifier(self) -> Oid<'static> {
match self {
Self::Sha1 => oid::SHA1,
Self::Sha224 => oid::SHA224,
Self::Sha256 => oid::SHA256,
Self::Sha384 => oid::SHA384,
Self::Sha512 => oid::SHA512,
Self::Sha512_224 => oid::SHA512_224,
Self::Sha512_256 => oid::SHA512_256,
}
}
}
pub trait AsnDigest<'a>: Digest<'a> {
fn identity(&self, algorithm: Algorithm) -> Option<Self::Identity>;
}
pub fn algorithm<W: Work>(
identifier: &AlgorithmIdentifier<'_>,
execution: &W,
) -> Result<Resolution<Algorithm>> {
let written = identifier.algorithm().as_bytes();
execution
.charge_work(written.len() as u64 + 1)
.map_err(|reason| Error::resource(reason, identifier.field, identifier.offset))?;
let algorithm = match written {
[0x2b, 0x0e, 0x03, 0x02, 0x1a] => Algorithm::Sha1,
[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, last] => match last {
0x01 => Algorithm::Sha256,
0x02 => Algorithm::Sha384,
0x03 => Algorithm::Sha512,
0x04 => Algorithm::Sha224,
0x05 => Algorithm::Sha512_224,
0x06 => Algorithm::Sha512_256,
_ => return Ok(Resolution::Unsupported),
},
_ => return Ok(Resolution::Unsupported),
};
if identifier.parameters().is_some() && !identifier.has_null_parameter() {
return Ok(Resolution::Malformed);
}
Ok(Resolution::Supported(algorithm))
}
pub fn resolve<'a, D: AsnDigest<'a> + ?Sized, W: Work>(
digest: &D,
identifier: &AlgorithmIdentifier<'_>,
execution: &W,
) -> Result<Resolution<D::Identity>> {
Ok(match algorithm(identifier, execution)? {
Resolution::Supported(algorithm) => supported(digest, algorithm),
Resolution::Unsupported => Resolution::Unsupported,
Resolution::Malformed => Resolution::Malformed,
})
}
pub fn supported<'a, D: AsnDigest<'a> + ?Sized>(
digest: &D,
algorithm: Algorithm,
) -> Resolution<D::Identity> {
digest
.identity(algorithm)
.map_or(Resolution::Unsupported, Resolution::Supported)
}