rcgen/
sign_algo.rs

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