1use super::{algorithm, encoding, end, equal_oid, invalid, number, positive, sequence};
8use crate::{Error, ErrorCode, Octets, Result, signer::required};
9use structured_asn1::{Integer, Oid, Reader, Tag, universal};
10use structured_pkix::AlgorithmIdentifier;
11
12const FIELD: &str = "personal.protection";
13pub const PBES2: Oid<'static> = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 5, 13]);
15pub const PBMAC1: Oid<'static> = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 5, 14]);
17pub const PBKDF2: Oid<'static> = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 5, 12]);
19const SHA1: Oid<'static> = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 2, 7]);
20
21pub enum Salt<'a> {
23 Specified(Octets<'a>),
25 Source(AlgorithmIdentifier<'a>),
27}
28#[derive(Debug, Clone, Copy)]
30pub enum Prf<'a> {
31 DefaultSha1,
33 Written(AlgorithmIdentifier<'a>),
35}
36pub struct Pbkdf2<'a> {
38 pub salt: Salt<'a>,
40 pub iterations: Integer<'a>,
42 pub key_length: Option<Integer<'a>>,
44 pub prf: Prf<'a>,
46}
47impl<'a> Pbkdf2<'a> {
48 pub fn read(identifier: AlgorithmIdentifier<'a>, parent: &Reader<'a>) -> Result<Self> {
51 session(identifier, parent)?;
52 if !equal_oid(parent, identifier.algorithm(), PBKDF2)? {
53 return Err(Error::schema(
54 ErrorCode::AlgorithmSyntax,
55 FIELD,
56 identifier.offset(),
57 ));
58 }
59 let mut reader = parameters(identifier, parent)?;
60 let value = required(&mut reader, FIELD)?;
61 let mut inside = sequence(&reader, &value)?;
62 end(&reader)?;
63 let salt_element = required(&mut inside, FIELD)?;
64 let salt = if salt_element.tag() == Tag::constructed(universal::SEQUENCE) {
65 Salt::Source(
66 structured_pkix::algorithm::read(&inside, &salt_element, FIELD)
67 .map_err(Error::pkix)?,
68 )
69 } else {
70 Salt::Specified(Octets::read(&inside, &salt_element, FIELD)?)
71 };
72 let iterations = number(&mut inside)?;
73 positive(iterations, &value)?;
74 let mut next = inside.next_element().map_err(encoding)?;
75 let key_length =
76 if let Some(element) = next.filter(|v| v.tag() == Tag::primitive(universal::INTEGER)) {
77 let length = inside.integer(&element).map_err(encoding)?;
78 positive(length, &element)?;
79 next = inside.next_element().map_err(encoding)?;
80 Some(length)
81 } else {
82 None
83 };
84 let prf = if let Some(element) = next {
85 let written =
86 structured_pkix::algorithm::read(&inside, &element, FIELD).map_err(Error::pkix)?;
87 hmac_width(written, &inside)?;
88 if equal_oid(&inside, written.algorithm(), SHA1)? && written.has_null_parameter() {
89 if inside.encoding().is_der() {
93 return Err(invalid(ErrorCode::DefaultWritten, &element));
94 }
95 }
96 Prf::Written(written)
97 } else {
98 Prf::DefaultSha1
99 };
100 end(&inside)?;
101 Ok(Self {
102 salt,
103 iterations,
104 key_length,
105 prf,
106 })
107 }
108}
109
110pub struct Pair<'a> {
112 pub derivation: AlgorithmIdentifier<'a>,
114 pub scheme: AlgorithmIdentifier<'a>,
116 pub pbkdf2: Option<Pbkdf2<'a>>,
118}
119impl<'a> Pair<'a> {
120 pub fn read(identifier: AlgorithmIdentifier<'a>, parent: &Reader<'a>) -> Result<Self> {
122 session(identifier, parent)?;
123 if !equal_oid(parent, identifier.algorithm(), PBES2)?
124 && !equal_oid(parent, identifier.algorithm(), PBMAC1)?
125 {
126 return Err(Error::schema(
127 ErrorCode::AlgorithmSyntax,
128 FIELD,
129 identifier.offset(),
130 ));
131 }
132 let mut reader = parameters(identifier, parent)?;
133 let value = required(&mut reader, FIELD)?;
134 let mut inside = sequence(&reader, &value)?;
135 end(&reader)?;
136 let derivation = algorithm(&mut inside)?;
137 let scheme = algorithm(&mut inside)?;
138 end(&inside)?;
139 let pbkdf2 = if equal_oid(&inside, derivation.algorithm(), PBKDF2)? {
140 Some(Pbkdf2::read(derivation, &inside)?)
141 } else {
142 None
143 };
144 Ok(Self {
145 derivation,
146 scheme,
147 pbkdf2,
148 })
149 }
150}
151
152pub struct Legacy<'a> {
154 pub salt: Octets<'a>,
156 pub iterations: Integer<'a>,
158}
159#[expect(
161 clippy::large_enum_variant,
162 reason = "one protection descriptor is consumed inline; boxing PBES2 would add an allocation for every password operation"
163)]
164pub enum Protection<'a> {
165 Pbes2(Pair<'a>),
167 Pbes1(Legacy<'a>),
169 Pkcs12(Legacy<'a>),
171 Other(AlgorithmIdentifier<'a>),
173}
174impl<'a> Protection<'a> {
175 pub fn read(identifier: AlgorithmIdentifier<'a>, parent: &Reader<'a>) -> Result<Self> {
177 session(identifier, parent)?;
178 if equal_oid(parent, identifier.algorithm(), PBES2)? {
179 return Pair::read(identifier, parent).map(Self::Pbes2);
180 }
181 let mut pbes1 = false;
182 for last in [1, 3, 4, 6, 10, 11] {
183 pbes1 |= equal_oid(
184 parent,
185 identifier.algorithm(),
186 Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 5, last]),
187 )?;
188 }
189 let mut pkcs12 = false;
190 for last in 1..=6 {
191 pkcs12 |= equal_oid(
192 parent,
193 identifier.algorithm(),
194 Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 12, 1, last]),
195 )?;
196 }
197 if !pbes1 && !pkcs12 {
198 return Ok(Self::Other(identifier));
201 }
202 let mut reader = parameters(identifier, parent)?;
203 let value = required(&mut reader, FIELD)?;
204 let mut inside = sequence(&reader, &value)?;
205 end(&reader)?;
206 let salt_value = required(&mut inside, FIELD)?;
207 let salt = Octets::read(&inside, &salt_value, FIELD)?;
208 let iterations = number(&mut inside)?;
209 positive(iterations, &value)?;
210 end(&inside)?;
211 if pbes1 && salt.len() != 8 {
212 return Err(invalid(ErrorCode::MalformedValue, &salt_value));
213 }
214 let result = Legacy { salt, iterations };
215 Ok(if pbes1 {
216 Self::Pbes1(result)
217 } else {
218 Self::Pkcs12(result)
219 })
220 }
221}
222
223fn parameters<'a>(identifier: AlgorithmIdentifier<'a>, parent: &Reader<'a>) -> Result<Reader<'a>> {
224 identifier
225 .parameters_reader_in(parent, FIELD)
226 .map_err(Error::pkix)?
227 .ok_or_else(|| Error::schema(ErrorCode::MissingField, FIELD, identifier.offset()))
228}
229
230fn session(identifier: AlgorithmIdentifier<'_>, parent: &Reader<'_>) -> Result<()> {
231 if !identifier.same_session(parent) {
232 return Err(Error::pkix(structured_pkix::Error::new(
233 structured_pkix::ErrorCode::ForeignReader,
234 FIELD,
235 identifier.offset(),
236 )));
237 }
238 Ok(())
239}
240
241pub(super) fn hmac_width(
242 identifier: AlgorithmIdentifier<'_>,
243 parent: &Reader<'_>,
244) -> Result<Option<usize>> {
245 session(identifier, parent)?;
246 let bytes = identifier.algorithm().as_bytes();
247 parent.charge(bytes.len() as u64 + 1).map_err(encoding)?;
248 let width = match bytes {
249 [42, 134, 72, 134, 247, 13, 2, 7] => 20,
250 [42, 134, 72, 134, 247, 13, 2, 8 | 12] => 28,
251 [42, 134, 72, 134, 247, 13, 2, 9 | 13] => 32,
252 [42, 134, 72, 134, 247, 13, 2, 10] => 48,
253 [42, 134, 72, 134, 247, 13, 2, 11] => 64,
254 _ => return Ok(None),
255 };
256 if identifier.parameters().is_some() && !identifier.has_null_parameter() {
261 return Err(Error::schema(
262 ErrorCode::AlgorithmSyntax,
263 FIELD,
264 identifier.offset(),
265 ));
266 }
267 Ok(Some(width))
268}