Skip to main content

pkcs1/
params.rs

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