webrtc_unreliable/
crypto.rs

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
use std::{fmt::Write as _, sync::Arc};

use openssl::{
    asn1::Asn1Time,
    error::ErrorStack,
    hash::MessageDigest,
    nid::Nid,
    pkey::PKey,
    rsa::Rsa,
    ssl::{SslAcceptor, SslMethod, SslVerifyMode},
    x509::{X509NameBuilder, X509},
};

/// A TLS private / public key pair and certificate.
#[derive(Clone)]
pub struct SslConfig {
    pub(crate) fingerprint: String,
    pub(crate) ssl_acceptor: Arc<SslAcceptor>,
}

impl SslConfig {
    /// Generates an anonymous private / public key pair and self-signed certificate.
    ///
    /// The certificate can be self-signed because the trust in the `webrtc-unreliable` server comes
    /// form the certificate fingerprint embedded in the session response. If the session response
    /// descriptor is deliviered over a trusted channel (such as HTTPS with a valid server
    /// certificate), the client will verify that the self-signed certificate matches
    /// the fingerprint, and so the resulting DTLS connection will have the same level of
    /// authentication.
    ///
    /// Client connections are assumed to be anonymous and are unverified, authentication can be
    /// handled through the resulting WebRTC data channel.
    pub fn create() -> Result<SslConfig, ErrorStack> {
        const X509_DAYS_NOT_BEFORE: u32 = 0;
        const X509_DAYS_NOT_AFTER: u32 = 365;

        // TODO: Let the user pick the crypto settings?
        let rsa = Rsa::generate(4096)?;
        let key = PKey::from_rsa(rsa)?;
        let x509_sign_digest = MessageDigest::sha256();

        // TODO: Fingerprint digest is hard-coded to 'sha-256' in SDP.
        let x509_fingerprint_digest = MessageDigest::sha256();

        let mut name_builder = X509NameBuilder::new()?;
        name_builder.append_entry_by_nid(Nid::COMMONNAME, "webrtc-unreliable")?;
        let name = name_builder.build();

        let mut x509_builder = X509::builder()?;
        x509_builder.set_version(2)?;
        x509_builder.set_subject_name(&name)?;
        x509_builder.set_issuer_name(&name)?;
        let not_before = Asn1Time::days_from_now(X509_DAYS_NOT_BEFORE)?;
        let not_after = Asn1Time::days_from_now(X509_DAYS_NOT_AFTER)?;
        x509_builder.set_not_before(&not_before)?;
        x509_builder.set_not_after(&not_after)?;
        x509_builder.set_pubkey(&key)?;
        x509_builder.sign(&key, x509_sign_digest)?;
        let x509 = x509_builder.build();

        let x509_digest = x509.digest(x509_fingerprint_digest)?;
        let mut fingerprint = String::new();
        for i in 0..x509_digest.len() {
            write!(fingerprint, "{:02X}", x509_digest[i]).unwrap();
            if i != x509_digest.len() - 1 {
                write!(fingerprint, ":").unwrap();
            }
        }

        let mut ssl_acceptor_builder = SslAcceptor::mozilla_intermediate(SslMethod::dtls())?;

        // `webrtc-unreliable` does not bother to verify client certificates because it is designed
        // to be used as a dedicated server with arbitrary clients.  The client will verify the
        // server's certificate via the fingerprint provided inside the SDP descriptor, so if the
        // descriptor is delivered over a verified channel (such as HTTPS with a valid server
        // certificate), the resulting DTLS connection should have the same level of verification.
        // This should prevent MITM attacks against the DTLS connection (tricking the client to
        // connect to some other server than the verified one).  Client authentication (such as
        // username / password) can then be handled through the resulting WebRTC data channel.
        //
        // TODO: Somebody who is actually good at this stuff should verify this.
        ssl_acceptor_builder.set_verify(SslVerifyMode::NONE);

        ssl_acceptor_builder.set_private_key(&key)?;
        ssl_acceptor_builder.set_certificate(&x509)?;
        let ssl_acceptor = Arc::new(ssl_acceptor_builder.build());

        Ok(SslConfig {
            fingerprint,
            ssl_acceptor,
        })
    }
}