1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright 2015-2018 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// https://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! DNS over TLS server implementation for Rustls

use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use std::sync::Arc;

use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::{self, ServerConfig};

use crate::error::{ProtoError, ProtoResult};

/// Read the certificate from the specified path.
///
/// If the password is specified, then it will be used to decode the Certificate
pub fn read_cert(cert_path: &Path) -> ProtoResult<Vec<CertificateDer<'static>>> {
    let mut cert_file = File::open(cert_path)
        .map_err(|e| format!("error opening cert file: {cert_path:?}: {e}"))?;

    let mut reader = BufReader::new(&mut cert_file);
    rustls_pemfile::certs(&mut reader)
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| {
            ProtoError::from(format!(
                "failed to read certs from: {}",
                cert_path.display()
            ))
        })
}

/// Reads a private key from a PEM-encoded file
///
/// ## Accepted formats
///
/// - A Sec1-encoded plaintext private key; as specified in RFC5915
/// - A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC3447
/// - DER-encoded plaintext private key; as specified in PKCS#8/RFC5958
///
/// ## Errors
///
/// Returns a [ProtoError] in either cases:
///
/// - Unable to open key at given `path`
/// - Encountered an IO error
/// - Unable to read key: either no key or no key found in the right format
pub fn read_key(path: &Path) -> ProtoResult<PrivateKeyDer<'static>> {
    let mut file = BufReader::new(File::open(path)?);
    match rustls_pemfile::private_key(&mut file) {
        Ok(Some(key)) => Ok(key),
        Ok(None) => Err(format!("no keys available in: {}", path.display()).into()),
        Err(e) => Err(e.into()),
    }
}

/// Reads a private key from a DER-encoded file
///
/// ## Accepted formats
///
/// - A Sec1-encoded plaintext private key; as specified in RFC5915
/// - A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC3447
/// - DER-encoded plaintext private key; as specified in PKCS#8/RFC5958
///
/// ## Errors
///
/// Returns a [ProtoError] in either cases:
///
/// - Unable to open key at given `path`
/// - Encountered an IO error
/// - Unable to read key: either no key or no key found in the right format
pub fn read_key_from_der(path: &Path) -> ProtoResult<PrivateKeyDer<'static>> {
    let mut file = File::open(path)?;
    let mut buf = Vec::new();
    file.read_to_end(&mut buf)?;

    Ok(PrivateKeyDer::try_from(buf)?)
}

/// Construct the new Acceptor with the associated pkcs12 data
pub fn new_acceptor(
    cert: Vec<CertificateDer<'static>>,
    key: PrivateKeyDer<'static>,
) -> Result<ServerConfig, rustls::Error> {
    let mut config =
        ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
            .with_safe_default_protocol_versions()?
            .with_no_client_auth()
            .with_single_cert(cert, key)?;

    config.alpn_protocols = vec![b"h2".to_vec()];
    Ok(config)
}