1use crate::algorithm::{self, AlgorithmIdentifier};
9use crate::error::{Error, ErrorCode, Located, Result};
10use structured_asn1::memory::Work;
11use structured_asn1::{BitString, Element, Oid, Reader, Tag, universal};
12
13pub mod dsa;
14pub mod ec;
15pub mod ml_dsa;
16pub mod rfc8410;
17pub mod rsa;
18
19#[derive(Debug, Clone, Copy)]
21pub struct PublicKeyInfo<'a> {
22 algorithm: AlgorithmIdentifier<'a>,
23 key: BitString<'a>,
24 der: &'a [u8],
25 key_offset: u64,
27}
28
29impl<'a> PublicKeyInfo<'a> {
30 #[must_use]
32 pub fn algorithm(self) -> AlgorithmIdentifier<'a> {
33 self.algorithm
34 }
35
36 #[must_use]
38 pub fn key(self) -> BitString<'a> {
39 self.key
40 }
41
42 #[must_use]
45 pub fn key_octets(self) -> Option<&'a [u8]> {
46 self.key.octets()
47 }
48
49 #[must_use]
51 pub fn key_offset(self) -> u64 {
52 self.key_offset
53 }
54
55 pub(crate) fn whole_octets(self, field: &'static str) -> Result<&'a [u8]> {
57 self.key_octets()
58 .ok_or_else(|| Error::new(ErrorCode::MalformedValue, field, self.key_offset))
59 }
60
61 #[must_use]
67 pub fn as_bytes(self) -> &'a [u8] {
68 self.der
69 }
70 pub fn read_element(
81 parent: &Reader<'a>,
82 element: &Element<'a>,
83 field: &'static str,
84 ) -> Result<PublicKeyInfo<'a>> {
85 let fail = |code| Error::new(code, field, element.span().start);
86
87 if element.tag() != Tag::constructed(universal::SEQUENCE) {
88 return Err(fail(ErrorCode::Encoding(
89 structured_asn1::ErrorCode::UnexpectedTag,
90 )));
91 }
92
93 let mut inside = parent.enter(element).field(field)?;
94 let algorithm_element = inside
95 .next_element()
96 .field(field)?
97 .ok_or_else(|| fail(ErrorCode::MissingField))?;
98 let algorithm = algorithm::read(&inside, &algorithm_element, field)?;
99 let key_element = inside
100 .expect(Tag::primitive(universal::BIT_STRING))
101 .field(field)?;
102 let key = inside.bit_string(&key_element).field(field)?;
103
104 if !inside.is_empty() {
105 return Err(fail(ErrorCode::TrailingData));
106 }
107
108 Ok(PublicKeyInfo {
109 algorithm,
110 key,
111 der: element.as_bytes(),
112 key_offset: key_element.contents_span().start + 1,
114 })
115 }
116}
117
118pub(crate) fn same_oid<W: Work>(
120 left: Oid<'_>,
121 right: Oid<'_>,
122 execution: &W,
123 field: &'static str,
124 at: u64,
125) -> Result<bool> {
126 let charge = |amount| {
127 execution
128 .charge_work(amount)
129 .map_err(|reason| Error::resource(reason, field, at))
130 };
131 charge(1)?;
132 if left.as_bytes().len() != right.as_bytes().len() {
133 return Ok(false);
134 }
135 charge(left.as_bytes().len() as u64)?;
136 Ok(left == right)
137}
138
139#[cfg(test)]
140mod tests;