rsa/
padding.rs

1//! Supported padding schemes.
2
3use alloc::boxed::Box;
4use alloc::string::{String, ToString};
5use core::fmt;
6
7use digest::{Digest, DynDigest};
8use pkcs8::AssociatedOid;
9
10use crate::pkcs1v15;
11
12/// Available padding schemes.
13pub enum PaddingScheme {
14    /// Encryption and Decryption using PKCS1v15 padding.
15    PKCS1v15Encrypt,
16
17    /// Sign and Verify using PKCS1v15 padding.
18    PKCS1v15Sign {
19        /// Length of hash to use.
20        hash_len: Option<usize>,
21
22        /// Prefix.
23        prefix: Box<[u8]>,
24    },
25
26    /// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
27    ///
28    /// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`,
29    ///   where `k` is the size of the RSA modulus.
30    /// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2).
31    /// - `label` is optional data that can be associated with the message.
32    ///
33    /// The two hash functions can, but don't need to be the same.
34    ///
35    /// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest).
36    /// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`.
37    OAEP {
38        /// Digest type to use.
39        digest: Box<dyn DynDigest + Send + Sync>,
40
41        /// Digest to use for Mask Generation Function (MGF).
42        mgf_digest: Box<dyn DynDigest + Send + Sync>,
43
44        /// Optional label.
45        label: Option<String>,
46    },
47
48    /// Sign and Verify using PSS padding.
49    PSS {
50        /// Digest type to use.
51        digest: Box<dyn DynDigest + Send + Sync>,
52
53        /// Salt length.
54        salt_len: Option<usize>,
55    },
56}
57
58impl fmt::Debug for PaddingScheme {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            PaddingScheme::PKCS1v15Encrypt => write!(f, "PaddingScheme::PKCS1v15Encrypt"),
62            PaddingScheme::PKCS1v15Sign { prefix, .. } => {
63                write!(f, "PaddingScheme::PKCS1v15Sign({:?})", prefix)
64            }
65            PaddingScheme::OAEP { ref label, .. } => {
66                // TODO: How to print the digest name?
67                write!(f, "PaddingScheme::OAEP({:?})", label)
68            }
69            PaddingScheme::PSS { ref salt_len, .. } => {
70                // TODO: How to print the digest name?
71                write!(f, "PaddingScheme::PSS(salt_len: {:?})", salt_len)
72            }
73        }
74    }
75}
76
77impl PaddingScheme {
78    /// Create new PKCS#1 v1.5 encryption padding.
79    pub fn new_pkcs1v15_encrypt() -> Self {
80        PaddingScheme::PKCS1v15Encrypt
81    }
82
83    /// Create new PKCS#1 v1.5 padding for computing a raw signature.
84    ///
85    /// This sets `hash_len` to `None` and uses an empty `prefix`.
86    pub fn new_pkcs1v15_sign_raw() -> Self {
87        PaddingScheme::PKCS1v15Sign {
88            hash_len: None,
89            prefix: Box::new([]),
90        }
91    }
92
93    /// Create new PKCS#1 v1.5 padding for the given digest.
94    ///
95    /// The digest must have an [`AssociatedOid`]. Make sure to enable the `oid`
96    /// feature of the relevant digest crate.
97    pub fn new_pkcs1v15_sign<D>() -> Self
98    where
99        D: Digest + AssociatedOid,
100    {
101        PaddingScheme::PKCS1v15Sign {
102            hash_len: Some(<D as Digest>::output_size()),
103            prefix: pkcs1v15::generate_prefix::<D>().into_boxed_slice(),
104        }
105    }
106
107    /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for the default (empty) label, and `U` as the hash function for MGF1.
108    /// If a label is needed use `PaddingScheme::new_oaep_with_label` or `PaddingScheme::new_oaep_with_mgf_hash_with_label`.
109    ///
110    /// # Example
111    /// ```
112    /// use sha1::Sha1;
113    /// use sha2::Sha256;
114    /// use rsa::{BigUint, RsaPublicKey, PaddingScheme, PublicKey};
115    /// use base64ct::{Base64, Encoding};
116    ///
117    /// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
118    /// let e = Base64::decode_vec("AQAB").unwrap();
119    ///
120    /// let mut rng = rand::thread_rng();
121    /// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
122    /// let padding = PaddingScheme::new_oaep_with_mgf_hash::<Sha256, Sha1>();
123    /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
124    /// ```
125    pub fn new_oaep_with_mgf_hash<
126        T: 'static + Digest + DynDigest + Send + Sync,
127        U: 'static + Digest + DynDigest + Send + Sync,
128    >() -> Self {
129        PaddingScheme::OAEP {
130            digest: Box::new(T::new()),
131            mgf_digest: Box::new(U::new()),
132            label: None,
133        }
134    }
135
136    /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for both the default (empty) label and for MGF1.
137    ///
138    /// # Example
139    /// ```
140    /// use sha1::Sha1;
141    /// use sha2::Sha256;
142    /// use rsa::{BigUint, RsaPublicKey, PaddingScheme, PublicKey};
143    /// use base64ct::{Base64, Encoding};
144    ///
145    /// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
146    /// let e = Base64::decode_vec("AQAB").unwrap();
147    ///
148    /// let mut rng = rand::thread_rng();
149    /// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
150    /// let padding = PaddingScheme::new_oaep::<Sha256>();
151    /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
152    /// ```
153    pub fn new_oaep<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
154        PaddingScheme::OAEP {
155            digest: Box::new(T::new()),
156            mgf_digest: Box::new(T::new()),
157            label: None,
158        }
159    }
160
161    /// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for the label, and `U` as the hash function for MGF1.
162    pub fn new_oaep_with_mgf_hash_with_label<
163        T: 'static + Digest + DynDigest + Send + Sync,
164        U: 'static + Digest + DynDigest + Send + Sync,
165        S: AsRef<str>,
166    >(
167        label: S,
168    ) -> Self {
169        PaddingScheme::OAEP {
170            digest: Box::new(T::new()),
171            mgf_digest: Box::new(U::new()),
172            label: Some(label.as_ref().to_string()),
173        }
174    }
175
176    /// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for both the label and for MGF1.
177    pub fn new_oaep_with_label<T: 'static + Digest + DynDigest + Send + Sync, S: AsRef<str>>(
178        label: S,
179    ) -> Self {
180        PaddingScheme::OAEP {
181            digest: Box::new(T::new()),
182            mgf_digest: Box::new(T::new()),
183            label: Some(label.as_ref().to_string()),
184        }
185    }
186
187    /// New PSS padding for the given digest.
188    pub fn new_pss<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
189        PaddingScheme::PSS {
190            digest: Box::new(T::new()),
191            salt_len: None,
192        }
193    }
194
195    /// New PSS padding for the given digest with a salt value of the given length.
196    pub fn new_pss_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(len: usize) -> Self {
197        PaddingScheme::PSS {
198            digest: Box::new(T::new()),
199            salt_len: Some(len),
200        }
201    }
202}