pkcs1/
params.rs

1//! PKCS#1 RSA parameters.
2
3use crate::{Error, Result};
4use der::{
5    asn1::{AnyRef, ContextSpecificRef, ObjectIdentifier},
6    oid::AssociatedOid,
7    Decode, DecodeValue, Encode, EncodeValue, FixedTag, Length, Reader, Sequence, Tag, TagMode,
8    TagNumber, Writer,
9};
10use spki::{AlgorithmIdentifier, AlgorithmIdentifierRef};
11
12const OID_SHA_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
13const OID_MGF_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8");
14const OID_PSPECIFIED: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.9");
15
16const SHA_1_AI: AlgorithmIdentifierRef<'_> = AlgorithmIdentifierRef {
17    oid: OID_SHA_1,
18    parameters: Some(AnyRef::NULL),
19};
20
21/// `TrailerField` as defined in [RFC 8017 Appendix 2.3].
22/// ```text
23/// TrailerField ::= INTEGER { trailerFieldBC(1) }
24/// ```
25/// [RFC 8017 Appendix 2.3]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.3
26#[derive(Clone, Debug, Copy, PartialEq, Eq)]
27#[repr(u8)]
28pub enum TrailerField {
29    /// the only supported value (0xbc, default)
30    BC = 1,
31}
32
33impl Default for TrailerField {
34    fn default() -> Self {
35        Self::BC
36    }
37}
38
39impl<'a> DecodeValue<'a> for TrailerField {
40    fn decode_value<R: Reader<'a>>(decoder: &mut R, header: der::Header) -> der::Result<Self> {
41        match u8::decode_value(decoder, header)? {
42            1 => Ok(TrailerField::BC),
43            _ => Err(Self::TAG.value_error()),
44        }
45    }
46}
47
48impl EncodeValue for TrailerField {
49    fn value_len(&self) -> der::Result<Length> {
50        Ok(Length::ONE)
51    }
52
53    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
54        (*self as u8).encode_value(writer)
55    }
56}
57
58impl FixedTag for TrailerField {
59    const TAG: Tag = Tag::Integer;
60}
61
62/// PKCS#1 RSASSA-PSS parameters as defined in [RFC 8017 Appendix 2.3]
63///
64/// ASN.1 structure containing a serialized RSASSA-PSS parameters:
65/// ```text
66/// RSASSA-PSS-params ::= SEQUENCE {
67///     hashAlgorithm      [0] HashAlgorithm      DEFAULT sha1,
68///     maskGenAlgorithm   [1] MaskGenAlgorithm   DEFAULT mgf1SHA1,
69///     saltLength         [2] INTEGER            DEFAULT 20,
70///     trailerField       [3] TrailerField       DEFAULT trailerFieldBC
71/// }
72/// HashAlgorithm ::= AlgorithmIdentifier
73/// MaskGenAlgorithm ::= AlgorithmIdentifier
74/// ```
75///
76/// [RFC 8017 Appendix 2.3]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.3
77#[derive(Clone, Debug, Eq, PartialEq)]
78pub struct RsaPssParams<'a> {
79    /// Hash Algorithm
80    pub hash: AlgorithmIdentifierRef<'a>,
81
82    /// Mask Generation Function (MGF)
83    pub mask_gen: AlgorithmIdentifier<AlgorithmIdentifierRef<'a>>,
84
85    /// Salt length
86    pub salt_len: u8,
87
88    /// Trailer field (i.e. [`TrailerField::BC`])
89    pub trailer_field: TrailerField,
90}
91
92impl<'a> RsaPssParams<'a> {
93    /// Default RSA PSS Salt length in RsaPssParams
94    pub const SALT_LEN_DEFAULT: u8 = 20;
95
96    /// Create new RsaPssParams for the provided digest and salt len
97    pub fn new<D>(salt_len: u8) -> Self
98    where
99        D: AssociatedOid,
100    {
101        Self {
102            hash: AlgorithmIdentifierRef {
103                oid: D::OID,
104                parameters: Some(AnyRef::NULL),
105            },
106            mask_gen: AlgorithmIdentifier {
107                oid: OID_MGF_1,
108                parameters: Some(AlgorithmIdentifierRef {
109                    oid: D::OID,
110                    parameters: Some(AnyRef::NULL),
111                }),
112            },
113            salt_len,
114            trailer_field: Default::default(),
115        }
116    }
117
118    fn context_specific_hash(&self) -> Option<ContextSpecificRef<'_, AlgorithmIdentifierRef<'a>>> {
119        if self.hash == SHA_1_AI {
120            None
121        } else {
122            Some(ContextSpecificRef {
123                tag_number: TagNumber::N0,
124                tag_mode: TagMode::Explicit,
125                value: &self.hash,
126            })
127        }
128    }
129
130    fn context_specific_mask_gen(
131        &self,
132    ) -> Option<ContextSpecificRef<'_, AlgorithmIdentifier<AlgorithmIdentifierRef<'a>>>> {
133        if self.mask_gen == default_mgf1_sha1() {
134            None
135        } else {
136            Some(ContextSpecificRef {
137                tag_number: TagNumber::N1,
138                tag_mode: TagMode::Explicit,
139                value: &self.mask_gen,
140            })
141        }
142    }
143
144    fn context_specific_salt_len(&self) -> Option<ContextSpecificRef<'_, u8>> {
145        if self.salt_len == RsaPssParams::SALT_LEN_DEFAULT {
146            None
147        } else {
148            Some(ContextSpecificRef {
149                tag_number: TagNumber::N2,
150                tag_mode: TagMode::Explicit,
151                value: &self.salt_len,
152            })
153        }
154    }
155
156    fn context_specific_trailer_field(&self) -> Option<ContextSpecificRef<'_, TrailerField>> {
157        if self.trailer_field == TrailerField::default() {
158            None
159        } else {
160            Some(ContextSpecificRef {
161                tag_number: TagNumber::N3,
162                tag_mode: TagMode::Explicit,
163                value: &self.trailer_field,
164            })
165        }
166    }
167}
168
169impl<'a> Default for RsaPssParams<'a> {
170    fn default() -> Self {
171        Self {
172            hash: SHA_1_AI,
173            mask_gen: default_mgf1_sha1(),
174            salt_len: RsaPssParams::SALT_LEN_DEFAULT,
175            trailer_field: Default::default(),
176        }
177    }
178}
179
180impl<'a> DecodeValue<'a> for RsaPssParams<'a> {
181    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
182        reader.read_nested(header.length, |reader| {
183            Ok(Self {
184                hash: reader
185                    .context_specific(TagNumber::N0, TagMode::Explicit)?
186                    .unwrap_or(SHA_1_AI),
187                mask_gen: reader
188                    .context_specific(TagNumber::N1, TagMode::Explicit)?
189                    .unwrap_or_else(default_mgf1_sha1),
190                salt_len: reader
191                    .context_specific(TagNumber::N2, TagMode::Explicit)?
192                    .unwrap_or(RsaPssParams::SALT_LEN_DEFAULT),
193                trailer_field: reader
194                    .context_specific(TagNumber::N3, TagMode::Explicit)?
195                    .unwrap_or_default(),
196            })
197        })
198    }
199}
200
201impl EncodeValue for RsaPssParams<'_> {
202    fn value_len(&self) -> der::Result<Length> {
203        self.context_specific_hash().encoded_len()?
204            + self.context_specific_mask_gen().encoded_len()?
205            + self.context_specific_salt_len().encoded_len()?
206            + self.context_specific_trailer_field().encoded_len()?
207    }
208
209    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
210        self.context_specific_hash().encode(writer)?;
211        self.context_specific_mask_gen().encode(writer)?;
212        self.context_specific_salt_len().encode(writer)?;
213        self.context_specific_trailer_field().encode(writer)?;
214        Ok(())
215    }
216}
217
218impl<'a> Sequence<'a> for RsaPssParams<'a> {}
219
220impl<'a> TryFrom<&'a [u8]> for RsaPssParams<'a> {
221    type Error = Error;
222
223    fn try_from(bytes: &'a [u8]) -> Result<Self> {
224        Ok(Self::from_der(bytes)?)
225    }
226}
227
228/// Default Mask Generation Function (MGF): SHA-1.
229fn default_mgf1_sha1<'a>() -> AlgorithmIdentifier<AlgorithmIdentifierRef<'a>> {
230    AlgorithmIdentifier::<AlgorithmIdentifierRef<'a>> {
231        oid: OID_MGF_1,
232        parameters: Some(SHA_1_AI),
233    }
234}
235
236/// PKCS#1 RSAES-OAEP parameters as defined in [RFC 8017 Appendix 2.1]
237///
238/// ASN.1 structure containing a serialized RSAES-OAEP parameters:
239/// ```text
240/// RSAES-OAEP-params ::= SEQUENCE {
241///     hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
242///     maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
243///     pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
244/// }
245/// HashAlgorithm ::= AlgorithmIdentifier
246/// MaskGenAlgorithm ::= AlgorithmIdentifier
247/// PSourceAlgorithm ::= AlgorithmIdentifier
248/// ```
249///
250/// [RFC 8017 Appendix 2.1]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.1
251#[derive(Clone, Debug, Eq, PartialEq)]
252pub struct RsaOaepParams<'a> {
253    /// Hash Algorithm
254    pub hash: AlgorithmIdentifierRef<'a>,
255
256    /// Mask Generation Function (MGF)
257    pub mask_gen: AlgorithmIdentifier<AlgorithmIdentifierRef<'a>>,
258
259    /// The source (and possibly the value) of the label L
260    pub p_source: AlgorithmIdentifierRef<'a>,
261}
262
263impl<'a> RsaOaepParams<'a> {
264    /// Create new RsaPssParams for the provided digest and default (empty) label
265    pub fn new<D>() -> Self
266    where
267        D: AssociatedOid,
268    {
269        Self::new_with_label::<D>(&[])
270    }
271
272    /// Create new RsaPssParams for the provided digest and specified label
273    pub fn new_with_label<D>(label: &'a impl AsRef<[u8]>) -> Self
274    where
275        D: AssociatedOid,
276    {
277        Self {
278            hash: AlgorithmIdentifierRef {
279                oid: D::OID,
280                parameters: Some(AnyRef::NULL),
281            },
282            mask_gen: AlgorithmIdentifier {
283                oid: OID_MGF_1,
284                parameters: Some(AlgorithmIdentifierRef {
285                    oid: D::OID,
286                    parameters: Some(AnyRef::NULL),
287                }),
288            },
289            p_source: pspecicied_algorithm_identifier(label),
290        }
291    }
292
293    fn context_specific_hash(&self) -> Option<ContextSpecificRef<'_, AlgorithmIdentifierRef<'a>>> {
294        if self.hash == SHA_1_AI {
295            None
296        } else {
297            Some(ContextSpecificRef {
298                tag_number: TagNumber::N0,
299                tag_mode: TagMode::Explicit,
300                value: &self.hash,
301            })
302        }
303    }
304
305    fn context_specific_mask_gen(
306        &self,
307    ) -> Option<ContextSpecificRef<'_, AlgorithmIdentifier<AlgorithmIdentifierRef<'a>>>> {
308        if self.mask_gen == default_mgf1_sha1() {
309            None
310        } else {
311            Some(ContextSpecificRef {
312                tag_number: TagNumber::N1,
313                tag_mode: TagMode::Explicit,
314                value: &self.mask_gen,
315            })
316        }
317    }
318
319    fn context_specific_p_source(
320        &self,
321    ) -> Option<ContextSpecificRef<'_, AlgorithmIdentifierRef<'a>>> {
322        if self.p_source == default_pempty_string() {
323            None
324        } else {
325            Some(ContextSpecificRef {
326                tag_number: TagNumber::N2,
327                tag_mode: TagMode::Explicit,
328                value: &self.p_source,
329            })
330        }
331    }
332}
333
334impl<'a> Default for RsaOaepParams<'a> {
335    fn default() -> Self {
336        Self {
337            hash: SHA_1_AI,
338            mask_gen: default_mgf1_sha1(),
339            p_source: default_pempty_string(),
340        }
341    }
342}
343
344impl<'a> DecodeValue<'a> for RsaOaepParams<'a> {
345    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
346        reader.read_nested(header.length, |reader| {
347            Ok(Self {
348                hash: reader
349                    .context_specific(TagNumber::N0, TagMode::Explicit)?
350                    .unwrap_or(SHA_1_AI),
351                mask_gen: reader
352                    .context_specific(TagNumber::N1, TagMode::Explicit)?
353                    .unwrap_or_else(default_mgf1_sha1),
354                p_source: reader
355                    .context_specific(TagNumber::N2, TagMode::Explicit)?
356                    .unwrap_or_else(default_pempty_string),
357            })
358        })
359    }
360}
361
362impl EncodeValue for RsaOaepParams<'_> {
363    fn value_len(&self) -> der::Result<Length> {
364        self.context_specific_hash().encoded_len()?
365            + self.context_specific_mask_gen().encoded_len()?
366            + self.context_specific_p_source().encoded_len()?
367    }
368
369    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
370        self.context_specific_hash().encode(writer)?;
371        self.context_specific_mask_gen().encode(writer)?;
372        self.context_specific_p_source().encode(writer)?;
373        Ok(())
374    }
375}
376
377impl<'a> Sequence<'a> for RsaOaepParams<'a> {}
378
379impl<'a> TryFrom<&'a [u8]> for RsaOaepParams<'a> {
380    type Error = Error;
381
382    fn try_from(bytes: &'a [u8]) -> Result<Self> {
383        Ok(Self::from_der(bytes)?)
384    }
385}
386
387fn pspecicied_algorithm_identifier(label: &impl AsRef<[u8]>) -> AlgorithmIdentifierRef<'_> {
388    AlgorithmIdentifierRef {
389        oid: OID_PSPECIFIED,
390        parameters: Some(
391            AnyRef::new(Tag::OctetString, label.as_ref()).expect("error creating OAEP params"),
392        ),
393    }
394}
395
396/// Default Source Algorithm, empty string
397fn default_pempty_string<'a>() -> AlgorithmIdentifierRef<'a> {
398    pspecicied_algorithm_identifier(&[])
399}