1use rustls::{SignatureScheme, pki_types::SubjectPublicKeyInfoDer, sign::SigningKey};
2use snafu::{ResultExt, Snafu};
3use x509_parser::{
4 oid_registry::{
5 OID_EC_P256, OID_KEY_TYPE_EC_PUBLIC_KEY, OID_NIST_EC_P384, OID_PKCS1_RSAENCRYPTION,
6 OID_SIG_ED25519,
7 },
8 prelude::FromDer,
9 x509::SubjectPublicKeyInfo,
10};
11
12pub const SIGNATURE_SCHEME_PREFERENCE: &[SignatureScheme] = &[
13 SignatureScheme::ED25519,
14 SignatureScheme::ECDSA_NISTP256_SHA256,
15 SignatureScheme::ECDSA_NISTP384_SHA384,
16 SignatureScheme::RSA_PSS_SHA256,
17 SignatureScheme::RSA_PSS_SHA384,
18 SignatureScheme::RSA_PSS_SHA512,
19 SignatureScheme::RSA_PKCS1_SHA256,
20 SignatureScheme::RSA_PKCS1_SHA384,
21 SignatureScheme::RSA_PKCS1_SHA512,
22];
23
24pub fn signature_schemes_for_algorithm(
25 algorithm: rustls::SignatureAlgorithm,
26) -> impl Iterator<Item = SignatureScheme> {
27 SIGNATURE_SCHEME_PREFERENCE
28 .iter()
29 .copied()
30 .filter(move |scheme| match algorithm {
31 rustls::SignatureAlgorithm::ED25519 => *scheme == SignatureScheme::ED25519,
32 rustls::SignatureAlgorithm::ECDSA => matches!(
33 scheme,
34 SignatureScheme::ECDSA_NISTP256_SHA256 | SignatureScheme::ECDSA_NISTP384_SHA384
35 ),
36 rustls::SignatureAlgorithm::RSA => matches!(
37 scheme,
38 SignatureScheme::RSA_PSS_SHA256
39 | SignatureScheme::RSA_PSS_SHA384
40 | SignatureScheme::RSA_PSS_SHA512
41 | SignatureScheme::RSA_PKCS1_SHA256
42 | SignatureScheme::RSA_PKCS1_SHA384
43 | SignatureScheme::RSA_PKCS1_SHA512
44 ),
45 _ => true,
46 })
47}
48
49pub fn alg_name_for_scheme(scheme: SignatureScheme) -> Option<&'static str> {
50 match scheme {
51 SignatureScheme::ED25519 => Some("ed25519"),
52 SignatureScheme::ECDSA_NISTP256_SHA256 => Some("ecdsa-p256-sha256"),
53 SignatureScheme::ECDSA_NISTP384_SHA384 => Some("ecdsa-p384-sha384"),
54 SignatureScheme::RSA_PSS_SHA256 => Some("rsa-pss-sha256"),
55 SignatureScheme::RSA_PSS_SHA384 => Some("rsa-pss-sha384"),
56 SignatureScheme::RSA_PSS_SHA512 => Some("rsa-pss-sha512"),
57 SignatureScheme::RSA_PKCS1_SHA256 => Some("rsa-v1_5-sha256"),
58 SignatureScheme::RSA_PKCS1_SHA384 => Some("rsa-v1_5-sha384"),
59 SignatureScheme::RSA_PKCS1_SHA512 => Some("rsa-v1_5-sha512"),
60 _ => None,
61 }
62}
63
64pub fn scheme_for_alg_name(alg: &str) -> Option<SignatureScheme> {
65 match alg {
66 "ed25519" => Some(SignatureScheme::ED25519),
67 "ecdsa-p256-sha256" => Some(SignatureScheme::ECDSA_NISTP256_SHA256),
68 "ecdsa-p384-sha384" => Some(SignatureScheme::ECDSA_NISTP384_SHA384),
69 "rsa-pss-sha256" => Some(SignatureScheme::RSA_PSS_SHA256),
70 "rsa-pss-sha384" => Some(SignatureScheme::RSA_PSS_SHA384),
71 "rsa-pss-sha512" => Some(SignatureScheme::RSA_PSS_SHA512),
72 "rsa-v1_5-sha256" => Some(SignatureScheme::RSA_PKCS1_SHA256),
73 "rsa-v1_5-sha384" => Some(SignatureScheme::RSA_PKCS1_SHA384),
74 "rsa-v1_5-sha512" => Some(SignatureScheme::RSA_PKCS1_SHA512),
75 _ => None,
76 }
77}
78
79pub fn canonical_scheme_for_spki(spki: SubjectPublicKeyInfoDer<'_>) -> Option<SignatureScheme> {
80 let Ok((_remain, spki)) = SubjectPublicKeyInfo::from_der(spki.as_ref()) else {
81 return None;
82 };
83
84 if spki.algorithm.algorithm == OID_SIG_ED25519 {
85 return Some(SignatureScheme::ED25519);
86 }
87
88 if spki.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
89 return Some(SignatureScheme::RSA_PSS_SHA512);
90 }
91
92 if spki.algorithm.algorithm != OID_KEY_TYPE_EC_PUBLIC_KEY {
93 return None;
94 }
95
96 let curve = spki
97 .algorithm
98 .parameters
99 .as_ref()
100 .and_then(|parameters| parameters.as_oid().ok())?;
101
102 if curve == OID_EC_P256 {
103 Some(SignatureScheme::ECDSA_NISTP256_SHA256)
104 } else if curve == OID_NIST_EC_P384 {
105 Some(SignatureScheme::ECDSA_NISTP384_SHA384)
106 } else {
107 None
108 }
109}
110
111#[derive(Debug, Snafu)]
112#[snafu(module)]
113pub enum SignError {
114 #[snafu(display("failed to sign DHTTP identity data"))]
115 Identity {
116 source: dhttp_identity::identity::SignError,
117 },
118}
119
120#[derive(Debug, Snafu)]
121#[snafu(module)]
122pub enum VerifyError {
123 #[snafu(display("failed to verify DHTTP identity signature"))]
124 Identity {
125 source: dhttp_identity::identity::VerifyError,
126 },
127 #[snafu(display("unsupported signature scheme {scheme:?}"))]
128 UnsupportedScheme { scheme: SignatureScheme },
129 #[snafu(display("invalid certificate: {details}"))]
130 InvalidCertificate { details: String },
131 #[snafu(display("invalid PEM"))]
132 InvalidPem { source: std::io::Error },
133 #[snafu(display("invalid base64"))]
134 InvalidBase64 { source: base64::DecodeError },
135 #[snafu(display("io error"))]
136 Io { source: std::io::Error },
137}
138
139#[derive(Debug, Snafu)]
140#[snafu(module)]
141pub enum SignatureSchemeError {
142 #[snafu(display("unsupported public key type"))]
143 UnsupportedKey,
144}
145
146pub fn sign_with_key(key: &(impl SigningKey + ?Sized), data: &[u8]) -> Result<Vec<u8>, SignError> {
147 dhttp_identity::identity::sign_with_key(key, data).context(sign_error::IdentitySnafu)
148}
149
150#[allow(dead_code)]
151pub(crate) fn signature_scheme(
152 spki: SubjectPublicKeyInfoDer<'_>,
153) -> Result<SignatureScheme, SignatureSchemeError> {
154 let Ok((_remain, spki)) = SubjectPublicKeyInfo::from_der(spki.as_ref()) else {
155 return signature_scheme_error::UnsupportedKeySnafu.fail();
156 };
157
158 if spki.algorithm.algorithm == OID_SIG_ED25519 {
159 return Ok(SignatureScheme::ED25519);
160 }
161
162 if spki.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
163 return Ok(SignatureScheme::RSA_PSS_SHA512);
164 }
165
166 if spki.algorithm.algorithm != OID_KEY_TYPE_EC_PUBLIC_KEY {
167 return signature_scheme_error::UnsupportedKeySnafu.fail();
168 }
169
170 let Some(curve) = spki
171 .algorithm
172 .parameters
173 .as_ref()
174 .and_then(|parameters| parameters.as_oid().ok())
175 else {
176 return signature_scheme_error::UnsupportedKeySnafu.fail();
177 };
178
179 if curve == OID_EC_P256 {
180 Ok(SignatureScheme::ECDSA_NISTP256_SHA256)
181 } else if curve == OID_NIST_EC_P384 {
182 Ok(SignatureScheme::ECDSA_NISTP384_SHA384)
183 } else {
184 signature_scheme_error::UnsupportedKeySnafu.fail()
185 }
186}
187
188pub(crate) fn verify(
189 spki: SubjectPublicKeyInfoDer,
190 scheme: SignatureScheme,
191 data: &[u8],
192 signature: &[u8],
193) -> Result<bool, VerifyError> {
194 let algorithm: &'static dyn ring::signature::VerificationAlgorithm = match scheme {
195 SignatureScheme::ECDSA_NISTP384_SHA384 => &ring::signature::ECDSA_P384_SHA384_ASN1,
196 SignatureScheme::ECDSA_NISTP256_SHA256 => &ring::signature::ECDSA_P256_SHA256_ASN1,
197 SignatureScheme::ED25519 => &ring::signature::ED25519,
198 SignatureScheme::RSA_PKCS1_SHA256 => &ring::signature::RSA_PKCS1_2048_8192_SHA256,
199 SignatureScheme::RSA_PKCS1_SHA384 => &ring::signature::RSA_PKCS1_2048_8192_SHA384,
200 SignatureScheme::RSA_PKCS1_SHA512 => &ring::signature::RSA_PKCS1_2048_8192_SHA512,
201 SignatureScheme::RSA_PSS_SHA256 => &ring::signature::RSA_PSS_2048_8192_SHA256,
202 SignatureScheme::RSA_PSS_SHA384 => &ring::signature::RSA_PSS_2048_8192_SHA384,
203 SignatureScheme::RSA_PSS_SHA512 => &ring::signature::RSA_PSS_2048_8192_SHA512,
204 _ => return verify_error::UnsupportedSchemeSnafu { scheme }.fail(),
205 };
206
207 let public_key = match SubjectPublicKeyInfo::from_der(spki.as_ref()) {
208 Ok((_remain, spki)) => spki.subject_public_key,
209 Err(_) => return Ok(false),
210 };
211
212 Ok(
213 ring::signature::UnparsedPublicKey::new(algorithm, public_key)
214 .verify(data, signature)
215 .is_ok(),
216 )
217}