pkcs1/
params.rs

1//! PKCS#1 RSA parameters.
2
3use crate::{Error, Result};
4use der::asn1::{AnyRef, ObjectIdentifier};
5use der::{
6    asn1::ContextSpecificRef, Decode, DecodeValue, Encode, EncodeValue, FixedTag, Reader, Sequence,
7    Tag, TagMode, TagNumber, Writer,
8};
9use spki::AlgorithmIdentifier;
10
11const OID_SHA_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
12const OID_MGF_1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8");
13const OID_PSPECIFIED: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.9");
14
15// TODO(tarcieri): make `AlgorithmIdentifier` generic around params; use `OID_SHA_1`
16const SEQ_OID_SHA_1_DER: &[u8] = &[0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a];
17
18const SHA_1_AI: AlgorithmIdentifier<'_> = AlgorithmIdentifier {
19    oid: OID_SHA_1,
20    parameters: None,
21};
22
23const SALT_LEN_DEFAULT: u8 = 20;
24
25/// `TrailerField` as defined in [RFC 8017 Appendix 2.3].
26/// ```text
27/// TrailerField ::= INTEGER { trailerFieldBC(1) }
28/// ```
29/// [RFC 8017 Appendix 2.3]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.3
30#[derive(Clone, Debug, Copy, PartialEq, Eq)]
31#[repr(u8)]
32pub enum TrailerField {
33    /// the only supported value (0xbc, default)
34    BC = 1,
35}
36
37impl Default for TrailerField {
38    fn default() -> Self {
39        Self::BC
40    }
41}
42
43impl<'a> DecodeValue<'a> for TrailerField {
44    fn decode_value<R: Reader<'a>>(decoder: &mut R, header: der::Header) -> der::Result<Self> {
45        match u8::decode_value(decoder, header)? {
46            1 => Ok(TrailerField::BC),
47            _ => Err(Self::TAG.value_error()),
48        }
49    }
50}
51
52impl EncodeValue for TrailerField {
53    fn value_len(&self) -> der::Result<der::Length> {
54        Ok(der::Length::ONE)
55    }
56
57    fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> {
58        (*self as u8).encode_value(writer)
59    }
60}
61
62impl FixedTag for TrailerField {
63    const TAG: Tag = Tag::Integer;
64}
65
66/// PKCS#1 RSASSA-PSS parameters as defined in [RFC 8017 Appendix 2.3]
67///
68/// ASN.1 structure containing a serialized RSASSA-PSS parameters:
69/// ```text
70/// RSASSA-PSS-params ::= SEQUENCE {
71///     hashAlgorithm      [0] HashAlgorithm      DEFAULT sha1,
72///     maskGenAlgorithm   [1] MaskGenAlgorithm   DEFAULT mgf1SHA1,
73///     saltLength         [2] INTEGER            DEFAULT 20,
74///     trailerField       [3] TrailerField       DEFAULT trailerFieldBC
75/// }
76/// HashAlgorithm ::= AlgorithmIdentifier
77/// MaskGenAlgorithm ::= AlgorithmIdentifier
78/// ```
79///
80/// [RFC 8017 Appendix 2.3]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.3
81#[derive(Clone, Debug, Eq, PartialEq)]
82pub struct RsaPssParams<'a> {
83    /// Hash Algorithm
84    pub hash: AlgorithmIdentifier<'a>,
85
86    /// Mask Generation Function (MGF)
87    pub mask_gen: AlgorithmIdentifier<'a>,
88
89    /// Salt length
90    pub salt_len: u8,
91
92    /// Trailer field (i.e. [`TrailerField::BC`])
93    pub trailer_field: TrailerField,
94}
95
96impl<'a> Default for RsaPssParams<'a> {
97    fn default() -> Self {
98        Self {
99            hash: SHA_1_AI,
100            mask_gen: default_mgf1_sha1(),
101            salt_len: SALT_LEN_DEFAULT,
102            trailer_field: Default::default(),
103        }
104    }
105}
106
107impl<'a> DecodeValue<'a> for RsaPssParams<'a> {
108    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
109        reader.read_nested(header.length, |reader| {
110            Ok(Self {
111                hash: reader
112                    .context_specific(TagNumber::N0, TagMode::Explicit)?
113                    .unwrap_or(SHA_1_AI),
114                mask_gen: reader
115                    .context_specific(TagNumber::N1, TagMode::Explicit)?
116                    .unwrap_or_else(default_mgf1_sha1),
117                salt_len: reader
118                    .context_specific(TagNumber::N2, TagMode::Explicit)?
119                    .unwrap_or(SALT_LEN_DEFAULT),
120                trailer_field: reader
121                    .context_specific(TagNumber::N3, TagMode::Explicit)?
122                    .unwrap_or_default(),
123            })
124        })
125    }
126}
127
128impl<'a> Sequence<'a> for RsaPssParams<'a> {
129    fn fields<F, T>(&self, f: F) -> der::Result<T>
130    where
131        F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
132    {
133        f(&[
134            &if self.hash == SHA_1_AI {
135                None
136            } else {
137                Some(ContextSpecificRef {
138                    tag_number: TagNumber::N0,
139                    tag_mode: TagMode::Explicit,
140                    value: &self.hash,
141                })
142            },
143            &if self.mask_gen == default_mgf1_sha1() {
144                None
145            } else {
146                Some(ContextSpecificRef {
147                    tag_number: TagNumber::N1,
148                    tag_mode: TagMode::Explicit,
149                    value: &self.mask_gen,
150                })
151            },
152            &if self.salt_len == SALT_LEN_DEFAULT {
153                None
154            } else {
155                Some(ContextSpecificRef {
156                    tag_number: TagNumber::N2,
157                    tag_mode: TagMode::Explicit,
158                    value: &self.salt_len,
159                })
160            },
161            &if self.trailer_field == TrailerField::default() {
162                None
163            } else {
164                Some(ContextSpecificRef {
165                    tag_number: TagNumber::N3,
166                    tag_mode: TagMode::Explicit,
167                    value: &self.trailer_field,
168                })
169            },
170        ])
171    }
172}
173
174impl<'a> TryFrom<&'a [u8]> for RsaPssParams<'a> {
175    type Error = Error;
176
177    fn try_from(bytes: &'a [u8]) -> Result<Self> {
178        Ok(Self::from_der(bytes)?)
179    }
180}
181
182/// Default Mask Generation Function (MGF): SHA-1.
183fn default_mgf1_sha1<'a>() -> AlgorithmIdentifier<'a> {
184    AlgorithmIdentifier {
185        oid: OID_MGF_1,
186        parameters: Some(
187            AnyRef::new(Tag::Sequence, SEQ_OID_SHA_1_DER)
188                .expect("error creating default MGF1 params"),
189        ),
190    }
191}
192
193/// PKCS#1 RSAES-OAEP parameters as defined in [RFC 8017 Appendix 2.1]
194///
195/// ASN.1 structure containing a serialized RSAES-OAEP parameters:
196/// ```text
197/// RSAES-OAEP-params ::= SEQUENCE {
198///     hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
199///     maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
200///     pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
201/// }
202/// HashAlgorithm ::= AlgorithmIdentifier
203/// MaskGenAlgorithm ::= AlgorithmIdentifier
204/// PSourceAlgorithm ::= AlgorithmIdentifier
205/// ```
206///
207/// [RFC 8017 Appendix 2.1]: https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.1
208#[derive(Clone, Debug, Eq, PartialEq)]
209pub struct RsaOaepParams<'a> {
210    /// Hash Algorithm
211    pub hash: AlgorithmIdentifier<'a>,
212
213    /// Mask Generation Function (MGF)
214    pub mask_gen: AlgorithmIdentifier<'a>,
215
216    /// The source (and possibly the value) of the label L
217    pub p_source: AlgorithmIdentifier<'a>,
218}
219
220impl<'a> Default for RsaOaepParams<'a> {
221    fn default() -> Self {
222        Self {
223            hash: SHA_1_AI,
224            mask_gen: default_mgf1_sha1(),
225            p_source: default_pempty_string(),
226        }
227    }
228}
229
230impl<'a> DecodeValue<'a> for RsaOaepParams<'a> {
231    fn decode_value<R: Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
232        reader.read_nested(header.length, |reader| {
233            Ok(Self {
234                hash: reader
235                    .context_specific(TagNumber::N0, TagMode::Explicit)?
236                    .unwrap_or(SHA_1_AI),
237                mask_gen: reader
238                    .context_specific(TagNumber::N1, TagMode::Explicit)?
239                    .unwrap_or_else(default_mgf1_sha1),
240                p_source: reader
241                    .context_specific(TagNumber::N2, TagMode::Explicit)?
242                    .unwrap_or_else(default_pempty_string),
243            })
244        })
245    }
246}
247
248impl<'a> Sequence<'a> for RsaOaepParams<'a> {
249    fn fields<F, T>(&self, f: F) -> der::Result<T>
250    where
251        F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
252    {
253        f(&[
254            &if self.hash == SHA_1_AI {
255                None
256            } else {
257                Some(ContextSpecificRef {
258                    tag_number: TagNumber::N0,
259                    tag_mode: TagMode::Explicit,
260                    value: &self.hash,
261                })
262            },
263            &if self.mask_gen == default_mgf1_sha1() {
264                None
265            } else {
266                Some(ContextSpecificRef {
267                    tag_number: TagNumber::N1,
268                    tag_mode: TagMode::Explicit,
269                    value: &self.mask_gen,
270                })
271            },
272            &if self.p_source == default_pempty_string() {
273                None
274            } else {
275                Some(ContextSpecificRef {
276                    tag_number: TagNumber::N2,
277                    tag_mode: TagMode::Explicit,
278                    value: &self.p_source,
279                })
280            },
281        ])
282    }
283}
284
285impl<'a> TryFrom<&'a [u8]> for RsaOaepParams<'a> {
286    type Error = Error;
287
288    fn try_from(bytes: &'a [u8]) -> Result<Self> {
289        Ok(Self::from_der(bytes)?)
290    }
291}
292
293/// Default Source Algorithm, empty string
294fn default_pempty_string<'a>() -> AlgorithmIdentifier<'a> {
295    AlgorithmIdentifier {
296        oid: OID_PSPECIFIED,
297        parameters: Some(
298            AnyRef::new(Tag::OctetString, &[]).expect("error creating default OAEP params"),
299        ),
300    }
301}