Skip to main content

ddns/core/parser/
sigin.rs

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}