use super::{
ALT_SIGNATURE_ALGORITHM, ALT_SIGNATURE_VALUE, FIELD, Fault, SUBJECT_ALT_PUBLIC_KEY_INFO,
terminal,
};
use crate::algorithm::{self, AlgorithmIdentifier};
use crate::error::Located;
use crate::signature::SignatureValue;
use crate::{Error, ErrorCode, Extensions, PublicKeyInfo, Result};
use structured_asn1::memory::Work;
use structured_asn1::{Element, ScopedReader, Span};
#[derive(Debug)]
pub struct Component<'a, T> {
span: Option<Span>,
critical: Option<bool>,
bytes: Option<&'a [u8]>,
value: core::result::Result<Option<T>, Fault>,
field: &'static str,
at: u64,
}
impl<'a, T: Copy> Component<'a, T> {
pub(super) fn absent(field: &'static str) -> Self {
Self {
span: None,
critical: None,
bytes: None,
value: Ok(None),
field,
at: 0,
}
}
#[must_use]
pub fn span(&self) -> Option<Span> {
self.span
}
#[must_use]
pub fn critical(&self) -> Option<bool> {
self.critical
}
#[must_use]
pub fn as_bytes(&self) -> Option<&'a [u8]> {
self.bytes
}
pub fn reading(&self) -> core::result::Result<Option<T>, Fault> {
self.value
}
pub(super) fn require(&self) -> Result<T> {
match self.value {
Ok(Some(value)) => Ok(value),
Ok(None) => Err(Error::new(ErrorCode::MissingField, self.field, self.at)),
Err(fault) => Err(fault.error()),
}
}
pub(super) fn read(
span: Span,
critical: Option<bool>,
bytes: &'a [u8],
field: &'static str,
action: impl FnOnce() -> Result<T>,
) -> Result<Self> {
let value = match action() {
Ok(value) => Ok(Some(value)),
Err(error) if terminal(&error) => return Err(error),
Err(error) => Err(Fault::of(&error)),
};
Ok(Self {
span: Some(span),
critical,
bytes: Some(bytes),
value,
field,
at: span.start,
})
}
}
#[derive(Debug)]
pub struct Components<'a> {
pub key: Component<'a, PublicKeyInfo<'a>>,
pub algorithm: Component<'a, AlgorithmIdentifier<'a>>,
pub signature: Component<'a, SignatureValue<'a>>,
}
impl<'a> Components<'a> {
pub(super) fn empty(at: u64) -> Self {
let mut result = Self {
key: Component::absent("hybrid.subjectAltPublicKeyInfo"),
algorithm: Component::absent("hybrid.altSignatureAlgorithm"),
signature: Component::absent("hybrid.altSignatureValue"),
};
result.key.at = at;
result.algorithm.at = at;
result.signature.at = at;
result
}
pub(super) fn signature_fields(
&self,
) -> Result<(AlgorithmIdentifier<'a>, SignatureValue<'a>, Span)> {
let algorithm = self.algorithm.require()?;
let signature = self.signature.require()?;
Ok((
algorithm,
signature,
self.signature
.span
.expect("a decoded signature has a source"),
))
}
}
pub(super) fn extensions<'a, W: Work>(
extensions: &Extensions<'a>,
work: &W,
at: u64,
) -> Result<Components<'a>> {
work.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, at))?;
let mut output = Components::empty(at);
for extension in extensions.iter(work) {
let extension = extension?;
let kind = extension.kind();
super::identifier_work(kind, work, extension.span().start)?;
if kind == SUBJECT_ALT_PUBLIC_KEY_INFO {
output.key = Component::read(
extension.span(),
Some(extension.is_critical()),
extension.value(),
"hybrid.subjectAltPublicKeyInfo",
|| {
let mut reader = extension.reader_scoped(work).field(FIELD)?;
let element = one(&mut reader)?;
PublicKeyInfo::read_element(&reader, &element, FIELD)
},
)?;
} else if kind == ALT_SIGNATURE_ALGORITHM {
output.algorithm = Component::read(
extension.span(),
Some(extension.is_critical()),
extension.value(),
"hybrid.altSignatureAlgorithm",
|| {
let mut reader = extension.reader_scoped(work).field(FIELD)?;
let element = one(&mut reader)?;
algorithm::read(&reader, &element, FIELD)
},
)?;
} else if kind == ALT_SIGNATURE_VALUE {
output.signature = Component::read(
extension.span(),
Some(extension.is_critical()),
extension.value(),
"hybrid.altSignatureValue",
|| {
let mut reader = extension.reader_scoped(work).field(FIELD)?;
let value = SignatureValue::read(&mut reader, FIELD)?;
end(&reader)?;
Ok(value)
},
)?;
}
}
Ok(output)
}
pub(super) fn one<'a>(reader: &mut ScopedReader<'a, '_>) -> Result<Element<'a>> {
let value = reader
.next_element()
.field(FIELD)?
.ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, reader.offset()))?;
end(reader)?;
Ok(value)
}
pub(super) fn end(reader: &ScopedReader<'_, '_>) -> Result<()> {
if !reader.is_empty() {
return Err(Error::new(ErrorCode::TrailingData, FIELD, reader.offset()));
}
Ok(())
}