rcgen/
sign_algo.rs

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