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
12#[derive(Debug, Snafu)]
13#[snafu(module)]
14pub enum SignError {
15    #[snafu(display("failed to sign DHTTP identity data"))]
16    Identity {
17        source: dhttp_identity::identity::SignError,
18    },
19}
20
21#[derive(Debug, Snafu)]
22#[snafu(module)]
23pub enum VerifyError {
24    #[snafu(display("failed to verify DHTTP identity signature"))]
25    Identity {
26        source: dhttp_identity::identity::VerifyError,
27    },
28    #[snafu(display("unsupported signature scheme {scheme:?}"))]
29    UnsupportedScheme { scheme: SignatureScheme },
30    #[snafu(display("invalid certificate: {details}"))]
31    InvalidCertificate { details: String },
32    #[snafu(display("invalid PEM"))]
33    InvalidPem { source: std::io::Error },
34    #[snafu(display("invalid base64"))]
35    InvalidBase64 { source: base64::DecodeError },
36    #[snafu(display("io error"))]
37    Io { source: std::io::Error },
38}
39
40#[derive(Debug, Snafu)]
41#[snafu(module)]
42pub enum SignatureSchemeError {
43    #[snafu(display("unsupported public key type"))]
44    UnsupportedKey,
45}
46
47pub fn sign_with_key(key: &(impl SigningKey + ?Sized), data: &[u8]) -> Result<Vec<u8>, SignError> {
48    dhttp_identity::identity::sign_with_key(key, data).context(sign_error::IdentitySnafu)
49}
50
51pub(crate) fn signature_scheme(
52    spki: SubjectPublicKeyInfoDer<'_>,
53) -> Result<SignatureScheme, SignatureSchemeError> {
54    let Ok((_remain, spki)) = SubjectPublicKeyInfo::from_der(spki.as_ref()) else {
55        return signature_scheme_error::UnsupportedKeySnafu.fail();
56    };
57
58    if spki.algorithm.algorithm == OID_SIG_ED25519 {
59        return Ok(SignatureScheme::ED25519);
60    }
61
62    if spki.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
63        return Ok(SignatureScheme::RSA_PSS_SHA512);
64    }
65
66    if spki.algorithm.algorithm != OID_KEY_TYPE_EC_PUBLIC_KEY {
67        return signature_scheme_error::UnsupportedKeySnafu.fail();
68    }
69
70    let Some(curve) = spki
71        .algorithm
72        .parameters
73        .as_ref()
74        .and_then(|parameters| parameters.as_oid().ok())
75    else {
76        return signature_scheme_error::UnsupportedKeySnafu.fail();
77    };
78
79    if curve == OID_EC_P256 {
80        Ok(SignatureScheme::ECDSA_NISTP256_SHA256)
81    } else if curve == OID_NIST_EC_P384 {
82        Ok(SignatureScheme::ECDSA_NISTP384_SHA384)
83    } else {
84        signature_scheme_error::UnsupportedKeySnafu.fail()
85    }
86}
87
88pub(crate) fn verify(
89    spki: SubjectPublicKeyInfoDer,
90    scheme: SignatureScheme,
91    data: &[u8],
92    signature: &[u8],
93) -> Result<bool, VerifyError> {
94    let algorithm: &'static dyn ring::signature::VerificationAlgorithm = match scheme {
95        SignatureScheme::ECDSA_NISTP384_SHA384 => &ring::signature::ECDSA_P384_SHA384_ASN1,
96        SignatureScheme::ECDSA_NISTP256_SHA256 => &ring::signature::ECDSA_P256_SHA256_ASN1,
97        SignatureScheme::ED25519 => &ring::signature::ED25519,
98        SignatureScheme::RSA_PSS_SHA512 => &ring::signature::RSA_PSS_2048_8192_SHA512,
99        _ => return verify_error::UnsupportedSchemeSnafu { scheme }.fail(),
100    };
101
102    let public_key = match SubjectPublicKeyInfo::from_der(spki.as_ref()) {
103        Ok((_remain, spki)) => spki.subject_public_key,
104        Err(_) => return Ok(false),
105    };
106
107    Ok(
108        ring::signature::UnparsedPublicKey::new(algorithm, public_key)
109            .verify(data, signature)
110            .is_ok(),
111    )
112}