rcgen/
sign_algo.rs

1use std::fmt;
2use std::hash::{Hash, Hasher};
3
4#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
5use aws_lc_rs::unstable::signature::{
6	PqdsaSigningAlgorithm, ML_DSA_44_SIGNING, ML_DSA_65_SIGNING, ML_DSA_87_SIGNING,
7};
8use yasna::models::ObjectIdentifier;
9use yasna::{DERWriter, Tag};
10
11#[cfg(feature = "crypto")]
12use crate::ring_like::signature::{self, EcdsaSigningAlgorithm, EdDSAParameters, RsaEncoding};
13use crate::Error;
14
15#[cfg(feature = "crypto")]
16#[derive(Clone, Copy, Debug)]
17pub(crate) enum SignAlgo {
18	EcDsa(&'static EcdsaSigningAlgorithm),
19	EdDsa(&'static EdDSAParameters),
20	#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
21	PqDsa(&'static PqdsaSigningAlgorithm),
22	Rsa(&'static dyn RsaEncoding),
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub(crate) enum SignatureAlgorithmParams {
27	/// Omit the parameters
28	None,
29	/// Write null parameters
30	Null,
31	/// RSASSA-PSS-params as per RFC 4055
32	RsaPss {
33		hash_algorithm: &'static [u64],
34		salt_length: u64,
35	},
36}
37
38/// Signature algorithm type
39#[derive(Clone)]
40pub struct SignatureAlgorithm {
41	oids_sign_alg: &'static [&'static [u64]],
42	#[cfg(feature = "crypto")]
43	pub(crate) sign_alg: SignAlgo,
44	oid_components: &'static [u64],
45	params: SignatureAlgorithmParams,
46}
47
48impl fmt::Debug for SignatureAlgorithm {
49	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50		use algo::*;
51		if self == &PKCS_RSA_SHA256 {
52			write!(f, "PKCS_RSA_SHA256")
53		} else if self == &PKCS_RSA_SHA384 {
54			write!(f, "PKCS_RSA_SHA384")
55		} else if self == &PKCS_RSA_SHA512 {
56			write!(f, "PKCS_RSA_SHA512")
57		} else if self == &PKCS_RSA_PSS_SHA256 {
58			write!(f, "PKCS_RSA_PSS_SHA256")
59		} else if self == &PKCS_ECDSA_P256_SHA256 {
60			write!(f, "PKCS_ECDSA_P256_SHA256")
61		} else if self == &PKCS_ECDSA_P384_SHA384 {
62			write!(f, "PKCS_ECDSA_P384_SHA384")
63		} else if self == &PKCS_ED25519 {
64			write!(f, "PKCS_ED25519")
65		} else {
66			#[cfg(feature = "aws_lc_rs")]
67			if self == &PKCS_ECDSA_P521_SHA256 {
68				return write!(f, "PKCS_ECDSA_P521_SHA256");
69			}
70			#[cfg(feature = "aws_lc_rs")]
71			if self == &PKCS_ECDSA_P521_SHA384 {
72				return write!(f, "PKCS_ECDSA_P521_SHA384");
73			}
74			#[cfg(feature = "aws_lc_rs")]
75			if self == &PKCS_ECDSA_P521_SHA512 {
76				return write!(f, "PKCS_ECDSA_P521_SHA512");
77			}
78
79			write!(f, "Unknown")
80		}
81	}
82}
83
84impl PartialEq for SignatureAlgorithm {
85	fn eq(&self, other: &Self) -> bool {
86		(self.oids_sign_alg, self.oid_components) == (other.oids_sign_alg, other.oid_components)
87	}
88}
89
90impl Eq for SignatureAlgorithm {}
91
92/// The `Hash` trait is not derived, but implemented according to impl of the `PartialEq` trait
93impl Hash for SignatureAlgorithm {
94	fn hash<H: Hasher>(&self, state: &mut H) {
95		// see SignatureAlgorithm::eq(), just this field is compared
96		self.oids_sign_alg.hash(state);
97	}
98}
99impl SignatureAlgorithm {
100	pub(crate) fn iter() -> std::slice::Iter<'static, &'static SignatureAlgorithm> {
101		use algo::*;
102		static ALGORITHMS: &[&SignatureAlgorithm] = &[
103			&PKCS_RSA_SHA256,
104			&PKCS_RSA_SHA384,
105			&PKCS_RSA_SHA512,
106			//&PKCS_RSA_PSS_SHA256,
107			&PKCS_ECDSA_P256_SHA256,
108			&PKCS_ECDSA_P384_SHA384,
109			#[cfg(feature = "aws_lc_rs")]
110			&PKCS_ECDSA_P521_SHA256,
111			#[cfg(feature = "aws_lc_rs")]
112			&PKCS_ECDSA_P521_SHA384,
113			#[cfg(feature = "aws_lc_rs")]
114			&PKCS_ECDSA_P521_SHA512,
115			&PKCS_ED25519,
116		];
117		ALGORITHMS.iter()
118	}
119
120	/// Retrieve the SignatureAlgorithm for the provided OID
121	pub fn from_oid(oid: &[u64]) -> Result<&'static SignatureAlgorithm, Error> {
122		for algo in Self::iter() {
123			if algo.oid_components == oid {
124				return Ok(algo);
125			}
126		}
127		Err(Error::UnsupportedSignatureAlgorithm)
128	}
129}
130
131/// The list of supported signature algorithms
132pub(crate) mod algo {
133	use super::*;
134	use crate::oid::*;
135
136	/// RSA signing with PKCS#1 1.5 padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055)
137	pub static PKCS_RSA_SHA256: SignatureAlgorithm = SignatureAlgorithm {
138		oids_sign_alg: &[RSA_ENCRYPTION],
139		#[cfg(feature = "crypto")]
140		sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA256),
141		// sha256WithRSAEncryption in RFC 4055
142		oid_components: &[1, 2, 840, 113549, 1, 1, 11],
143		params: SignatureAlgorithmParams::Null,
144	};
145
146	/// RSA signing with PKCS#1 1.5 padding and SHA-384 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055)
147	pub static PKCS_RSA_SHA384: SignatureAlgorithm = SignatureAlgorithm {
148		oids_sign_alg: &[RSA_ENCRYPTION],
149		#[cfg(feature = "crypto")]
150		sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA384),
151		// sha384WithRSAEncryption in RFC 4055
152		oid_components: &[1, 2, 840, 113549, 1, 1, 12],
153		params: SignatureAlgorithmParams::Null,
154	};
155
156	/// RSA signing with PKCS#1 1.5 padding and SHA-512 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055)
157	pub static PKCS_RSA_SHA512: SignatureAlgorithm = SignatureAlgorithm {
158		oids_sign_alg: &[RSA_ENCRYPTION],
159		#[cfg(feature = "crypto")]
160		sign_alg: SignAlgo::Rsa(&signature::RSA_PKCS1_SHA512),
161		// sha512WithRSAEncryption in RFC 4055
162		oid_components: &[1, 2, 840, 113549, 1, 1, 13],
163		params: SignatureAlgorithmParams::Null,
164	};
165
166	// TODO: not really sure whether the certs we generate actually work.
167	// Both openssl and webpki reject them. It *might* be possible that openssl
168	// accepts the certificate if the key is a proper RSA-PSS key, but ring doesn't
169	// support those: https://github.com/briansmith/ring/issues/1353
170	//
171	/// RSA signing with PKCS#1 2.1 RSASSA-PSS padding and SHA-256 hashing as per [RFC 4055](https://tools.ietf.org/html/rfc4055)
172	pub(crate) static PKCS_RSA_PSS_SHA256: SignatureAlgorithm = SignatureAlgorithm {
173		// We could also use RSA_ENCRYPTION here, but it's recommended
174		// to use ID-RSASSA-PSS if possible.
175		oids_sign_alg: &[RSASSA_PSS],
176		#[cfg(feature = "crypto")]
177		sign_alg: SignAlgo::Rsa(&signature::RSA_PSS_SHA256),
178		oid_components: RSASSA_PSS, //&[1, 2, 840, 113549, 1, 1, 13],
179		// rSASSA-PSS-SHA256-Params in RFC 4055
180		params: SignatureAlgorithmParams::RsaPss {
181			// id-sha256 in https://datatracker.ietf.org/doc/html/rfc4055#section-2.1
182			hash_algorithm: &[2, 16, 840, 1, 101, 3, 4, 2, 1],
183			salt_length: 20,
184		},
185	};
186
187	/// ECDSA signing using the P-256 curves and SHA-256 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2)
188	pub static PKCS_ECDSA_P256_SHA256: SignatureAlgorithm = SignatureAlgorithm {
189		oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_256_R1],
190		#[cfg(feature = "crypto")]
191		sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P256_SHA256_ASN1_SIGNING),
192		// ecdsa-with-SHA256 in RFC 5758
193		oid_components: &[1, 2, 840, 10045, 4, 3, 2],
194		params: SignatureAlgorithmParams::None,
195	};
196
197	/// ECDSA signing using the P-384 curves and SHA-384 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2)
198	pub static PKCS_ECDSA_P384_SHA384: SignatureAlgorithm = SignatureAlgorithm {
199		oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_384_R1],
200		#[cfg(feature = "crypto")]
201		sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P384_SHA384_ASN1_SIGNING),
202		// ecdsa-with-SHA384 in RFC 5758
203		oid_components: &[1, 2, 840, 10045, 4, 3, 3],
204		params: SignatureAlgorithmParams::None,
205	};
206
207	/// ECDSA signing using the P-521 curves and SHA-256 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2)
208	///
209	/// Note that this algorithm is not widely supported, and is not supported in TLS 1.3.
210	///
211	/// Only supported with the `aws_lc_rs` backend.
212	#[cfg(feature = "aws_lc_rs")]
213	pub static PKCS_ECDSA_P521_SHA256: SignatureAlgorithm = SignatureAlgorithm {
214		oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1],
215		#[cfg(feature = "crypto")]
216		sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA256_ASN1_SIGNING),
217		// ecdsa-with-SHA256 in RFC 5758
218		oid_components: &[1, 2, 840, 10045, 4, 3, 2],
219		params: SignatureAlgorithmParams::None,
220	};
221
222	/// ECDSA signing using the P-521 curves and SHA-384 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2)
223	///
224	/// Note that this algorithm is not widely supported, and is not supported in TLS 1.3.
225	///
226	/// Only supported with the `aws_lc_rs` backend.
227	#[cfg(feature = "aws_lc_rs")]
228	pub static PKCS_ECDSA_P521_SHA384: SignatureAlgorithm = SignatureAlgorithm {
229		oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1],
230		#[cfg(feature = "crypto")]
231		sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA384_ASN1_SIGNING),
232		// ecdsa-with-SHA384 in RFC 5758
233		oid_components: &[1, 2, 840, 10045, 4, 3, 3],
234		params: SignatureAlgorithmParams::None,
235	};
236
237	/// ECDSA signing using the P-521 curves and SHA-512 hashing as per [RFC 5758](https://tools.ietf.org/html/rfc5758#section-3.2)
238	///
239	/// Only supported with the `aws_lc_rs` backend.
240	#[cfg(feature = "aws_lc_rs")]
241	pub static PKCS_ECDSA_P521_SHA512: SignatureAlgorithm = SignatureAlgorithm {
242		oids_sign_alg: &[EC_PUBLIC_KEY, EC_SECP_521_R1],
243		#[cfg(feature = "crypto")]
244		sign_alg: SignAlgo::EcDsa(&signature::ECDSA_P521_SHA512_ASN1_SIGNING),
245		// ecdsa-with-SHA512 in RFC 5758
246		oid_components: &[1, 2, 840, 10045, 4, 3, 4],
247		params: SignatureAlgorithmParams::None,
248	};
249
250	/// ED25519 curve signing as per [RFC 8410](https://tools.ietf.org/html/rfc8410)
251	pub static PKCS_ED25519: SignatureAlgorithm = SignatureAlgorithm {
252		// id-Ed25519 in RFC 8410
253		oids_sign_alg: &[&[1, 3, 101, 112]],
254		#[cfg(feature = "crypto")]
255		sign_alg: SignAlgo::EdDsa(&signature::ED25519),
256		// id-Ed25519 in RFC 8410
257		oid_components: &[1, 3, 101, 112],
258		params: SignatureAlgorithmParams::None,
259	};
260
261	/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>.
262	#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
263	pub static PKCS_ML_DSA_44: SignatureAlgorithm = SignatureAlgorithm {
264		oids_sign_alg: &[ML_DSA_44],
265		#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
266		sign_alg: SignAlgo::PqDsa(&ML_DSA_44_SIGNING),
267		oid_components: ML_DSA_44,
268		params: SignatureAlgorithmParams::None,
269	};
270
271	/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>.
272	#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
273	pub static PKCS_ML_DSA_65: SignatureAlgorithm = SignatureAlgorithm {
274		oids_sign_alg: &[ML_DSA_65],
275		#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
276		sign_alg: SignAlgo::PqDsa(&ML_DSA_65_SIGNING),
277		oid_components: ML_DSA_65,
278		params: SignatureAlgorithmParams::None,
279	};
280
281	/// ML-DSA-44 signing as per <https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-12.html#name-identifiers>.
282	#[cfg(all(feature = "aws_lc_rs_unstable", not(feature = "fips")))]
283	pub static PKCS_ML_DSA_87: SignatureAlgorithm = SignatureAlgorithm {
284		oids_sign_alg: &[ML_DSA_87],
285		#[cfg(all(feature = "crypto", feature = "aws_lc_rs_unstable"))]
286		sign_alg: SignAlgo::PqDsa(&ML_DSA_87_SIGNING),
287		oid_components: ML_DSA_87,
288		params: SignatureAlgorithmParams::None,
289	};
290}
291// Signature algorithm IDs as per https://tools.ietf.org/html/rfc4055
292impl SignatureAlgorithm {
293	fn alg_ident_oid(&self) -> ObjectIdentifier {
294		ObjectIdentifier::from_slice(self.oid_components)
295	}
296	fn write_params(&self, writer: &mut yasna::DERWriterSeq) {
297		match self.params {
298			SignatureAlgorithmParams::None => (),
299			SignatureAlgorithmParams::Null => {
300				writer.next().write_null();
301			},
302			SignatureAlgorithmParams::RsaPss {
303				hash_algorithm,
304				salt_length,
305			} => {
306				writer.next().write_sequence(|writer| {
307					// https://datatracker.ietf.org/doc/html/rfc4055#section-3.1
308
309					let oid = ObjectIdentifier::from_slice(hash_algorithm);
310					// hashAlgorithm
311					writer.next().write_tagged(Tag::context(0), |writer| {
312						writer.write_sequence(|writer| {
313							writer.next().write_oid(&oid);
314						});
315					});
316					// maskGenAlgorithm
317					writer.next().write_tagged(Tag::context(1), |writer| {
318						writer.write_sequence(|writer| {
319							// id-mgf1 in RFC 4055
320							const ID_MGF1: &[u64] = &[1, 2, 840, 113549, 1, 1, 8];
321							let oid = ObjectIdentifier::from_slice(ID_MGF1);
322							writer.next().write_oid(&oid);
323							writer.next().write_sequence(|writer| {
324								let oid = ObjectIdentifier::from_slice(hash_algorithm);
325								writer.next().write_oid(&oid);
326								writer.next().write_null();
327							});
328						});
329					});
330					// saltLength
331					writer.next().write_tagged(Tag::context(2), |writer| {
332						writer.write_u64(salt_length);
333					});
334					// We *must* omit the trailerField element as per RFC 4055 section 3.1
335				})
336			},
337		}
338	}
339	/// Writes the algorithm identifier as it appears inside a signature
340	pub(crate) fn write_alg_ident(&self, writer: DERWriter) {
341		writer.write_sequence(|writer| {
342			writer.next().write_oid(&self.alg_ident_oid());
343			self.write_params(writer);
344		});
345	}
346	/// Writes the algorithm identifier as it appears inside subjectPublicKeyInfo
347	pub(crate) fn write_oids_sign_alg(&self, writer: DERWriter) {
348		writer.write_sequence(|writer| {
349			for oid in self.oids_sign_alg {
350				let oid = ObjectIdentifier::from_slice(oid);
351				writer.next().write_oid(&oid);
352			}
353			self.write_params(writer);
354		});
355	}
356}