use super::{AlgorithmIdentifier, digest, ml_dsa, pss};
use crate::{Error, ErrorCode, Result};
use structured_asn1::memory::Work;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Algorithm<'a, 'parameters> {
RsaPkcs1v15(digest::Algorithm),
RsaPss(&'parameters pss::Parameters<'a>),
Dsa(digest::Algorithm),
Ecdsa(digest::Algorithm),
Ed25519,
Ed448,
MlDsa(ml_dsa::ParameterSet),
}
#[derive(Clone, Copy)]
enum Parameters {
Absent,
Null,
NullOrAbsent,
}
pub fn with_algorithm<'a, W: Work, R>(
identifier: AlgorithmIdentifier<'a>,
execution: &W,
visitor: impl for<'parameters> FnOnce(Option<Algorithm<'a, 'parameters>>) -> R,
) -> Result<R> {
use digest::Algorithm as Hash;
let bytes = identifier.algorithm().as_bytes();
identifier
.query_in(execution)
.charge_work(bytes.len() as u64 + 1)
.map_err(|reason| Error::resource(reason, identifier.field, identifier.offset()))?;
let (algorithm, parameters) = match bytes {
[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 1, 1, last] => {
let (hash, parameters) = match last {
5 => (Hash::Sha1, Parameters::Null),
11 => (Hash::Sha256, Parameters::NullOrAbsent),
12 => (Hash::Sha384, Parameters::NullOrAbsent),
13 => (Hash::Sha512, Parameters::NullOrAbsent),
14 => (Hash::Sha224, Parameters::NullOrAbsent),
15 => (Hash::Sha512_224, Parameters::Null),
16 => (Hash::Sha512_256, Parameters::Null),
10 => {
let value = pss::read_known(identifier, execution)?.ok_or_else(|| {
Error::new(
ErrorCode::MissingField,
identifier.field,
identifier.offset(),
)
})?;
return Ok(visitor(Some(Algorithm::RsaPss(&value))));
}
_ => return Ok(visitor(None)),
};
(Algorithm::RsaPkcs1v15(hash), parameters)
}
[0x2a, 0x86, 0x48, 0xce, 0x38, 4, 3] => (Algorithm::Dsa(Hash::Sha1), Parameters::Absent),
[0x2a, 0x86, 0x48, 0xce, 0x3d, 4, 1] => (Algorithm::Ecdsa(Hash::Sha1), Parameters::Absent),
[0x2a, 0x86, 0x48, 0xce, 0x3d, 4, 3, last] => {
let hash = match last {
1 => Hash::Sha224,
2 => Hash::Sha256,
3 => Hash::Sha384,
4 => Hash::Sha512,
_ => return Ok(visitor(None)),
};
(Algorithm::Ecdsa(hash), Parameters::Absent)
}
[0x60, 0x86, 0x48, 1, 0x65, 3, 4, 3, last] => {
match last {
1 => (Algorithm::Dsa(Hash::Sha224), Parameters::Absent),
2 => (Algorithm::Dsa(Hash::Sha256), Parameters::Absent),
9..=16 => {
let hash = match last {
9 | 13 => Hash::Sha3_224,
10 | 14 => Hash::Sha3_256,
11 | 15 => Hash::Sha3_384,
_ => Hash::Sha3_512,
};
if *last <= 12 {
(Algorithm::Ecdsa(hash), Parameters::Absent)
} else {
(Algorithm::RsaPkcs1v15(hash), Parameters::Null)
}
}
_ => {
if let Some(set) = ml_dsa::from_arc(*last) {
ml_dsa::require_absent(identifier)?;
return Ok(visitor(Some(Algorithm::MlDsa(set))));
}
return Ok(visitor(None));
}
}
}
[0x2b, 0x65, 112] => (Algorithm::Ed25519, Parameters::Absent),
[0x2b, 0x65, 113] => (Algorithm::Ed448, Parameters::Absent),
_ => return Ok(visitor(None)),
};
check_parameters(identifier, parameters)?;
Ok(visitor(Some(algorithm)))
}
fn check_parameters(identifier: AlgorithmIdentifier<'_>, parameters: Parameters) -> Result<()> {
let present = identifier.parameters_nested();
let valid = match parameters {
Parameters::Absent => present.is_none(),
Parameters::Null => identifier.has_null_parameter(),
Parameters::NullOrAbsent => present.is_none() || identifier.has_null_parameter(),
};
if !valid {
let at = present.map_or(identifier.offset(), super::Nested::offset);
let code = if present.is_some() {
ErrorCode::MalformedValue
} else {
ErrorCode::MissingField
};
return Err(Error::new(code, identifier.field, at));
}
Ok(())
}