Skip to main content

pkcs1/
params.rs

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