capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

tls.h (12070B)


      1 // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #pragma once
     23 // This file implements TLS (aka SSL) encrypted networking. It is actually a wrapper, currently
     24 // around OpenSSL / BoringSSL / LibreSSL, but the interface is intended to remain
     25 // implementation-agnostic.
     26 //
     27 // Unlike OpenSSL's API, the API defined in this file is intended to be hard to use wrong. Good
     28 // ciphers and settings are used by default. Certificates validation is performed automatically
     29 // and cannot be bypassed.
     30 
     31 #include <kj/async-io.h>
     32 
     33 namespace kj {
     34 
     35 class TlsPrivateKey;
     36 class TlsCertificate;
     37 struct TlsKeypair;
     38 class TlsSniCallback;
     39 class TlsConnection;
     40 
     41 enum class TlsVersion {
     42   SSL_3,     // avoid; cryptographically broken
     43   TLS_1_0,
     44   TLS_1_1,
     45   TLS_1_2
     46 };
     47 
     48 class TlsContext {
     49   // TLS system. Allocate one of these, configure it with the proper keys and certificates (or
     50   // use the defaults), and then use it to wrap the standard KJ network interfaces in
     51   // implementations that transparently use TLS.
     52 
     53 public:
     54   struct Options {
     55     Options();
     56     // Initializes all values to reasonable defaults.
     57 
     58     bool useSystemTrustStore;
     59     // Whether or not to trust the system's default trust store. Default: true.
     60 
     61     bool verifyClients;
     62     // If true, when acting as a server, require the client to present a certificate. The
     63     // certificate must be signed by one of the trusted CAs, otherwise the client will be rejected.
     64     // (Typically you should set `useSystemTrustStore` false when using this flag, and specify
     65     // your specific trusted CAs in `trustedCertificates`.)
     66     // Default: false
     67 
     68     kj::ArrayPtr<const TlsCertificate> trustedCertificates;
     69     // Additional certificates which should be trusted. Default: none.
     70 
     71     TlsVersion minVersion;
     72     // Minimum version. Defaults to minimum version that hasn't been cryptographically broken.
     73     // If you override this, consider doing:
     74     //
     75     //     options.minVersion = kj::max(myVersion, options.minVersion);
     76 
     77     kj::StringPtr cipherList;
     78     // OpenSSL cipher list string. The default is a curated list designed to be compatible with
     79     // almost all software in curent use (specifically, based on Mozilla's "intermediate"
     80     // recommendations). The defaults will change in future versions of this library to account
     81     // for the latest cryptanalysis.
     82     //
     83     // Generally you should only specify your own `cipherList` if:
     84     // - You have extreme backwards-compatibility needs and wish to enable obsolete and/or broken
     85     //   algorithms.
     86     // - You need quickly to disable an algorithm recently discovered to be broken.
     87 
     88     kj::Maybe<const TlsKeypair&> defaultKeypair;
     89     // Default keypair to use for all connections. Required for servers; optional for clients.
     90 
     91     kj::Maybe<TlsSniCallback&> sniCallback;
     92     // Callback that can be used to choose a different key/certificate based on the specific
     93     // hostname requested by the client.
     94 
     95     kj::Maybe<kj::Timer&> timer;
     96     // The timer used for `acceptTimeout` below.
     97 
     98     kj::Maybe<kj::Duration> acceptTimeout;
     99     // Timeout applied to accepting a new TLS connection. `timer` is required if this is set.
    100   };
    101 
    102   TlsContext(Options options = Options());
    103   ~TlsContext() noexcept(false);
    104   KJ_DISALLOW_COPY(TlsContext);
    105 
    106   kj::Promise<kj::Own<kj::AsyncIoStream>> wrapServer(kj::Own<kj::AsyncIoStream> stream);
    107   // Upgrade a regular network stream to TLS and begin the initial handshake as the server. The
    108   // returned promise resolves when the handshake has completed successfully.
    109 
    110   kj::Promise<kj::Own<kj::AsyncIoStream>> wrapClient(
    111       kj::Own<kj::AsyncIoStream> stream, kj::StringPtr expectedServerHostname);
    112   // Upgrade a regular network stream to TLS and begin the initial handshake as a client. The
    113   // returned promise resolves when the handshake has completed successfully, including validating
    114   // the server's certificate.
    115   //
    116   // You must specify the server's hostname. This is used for two purposes:
    117   // 1. It is sent to the server in the initial handshake via the TLS SNI extension, so that a
    118   //    server serving multiple hosts knows which certificate to use.
    119   // 2. The server's certificate is validated against this hostname. If validation fails, the
    120   //    promise returned by wrapClient() will be broken; you'll never get a stream.
    121 
    122   kj::Promise<kj::AuthenticatedStream> wrapServer(kj::AuthenticatedStream stream);
    123   kj::Promise<kj::AuthenticatedStream> wrapClient(
    124       kj::AuthenticatedStream stream, kj::StringPtr expectedServerHostname);
    125   // Like wrapServer() and wrapClient(), but also produces information about the peer's
    126   // certificate (if any). The returned `peerIdentity` will be a `TlsPeerIdentity`.
    127 
    128   kj::Own<kj::ConnectionReceiver> wrapPort(kj::Own<kj::ConnectionReceiver> port);
    129   // Upgrade a ConnectionReceiver to one that automatically upgrades all accepted connections to
    130   // TLS (acting as the server).
    131 
    132   kj::Own<kj::Network> wrapNetwork(kj::Network& network);
    133   // Upgrade a Network to one that automatically upgrades all connections to TLS. The network will
    134   // only accept addresses of the form "hostname" and "hostname:port" (it does not accept raw IP
    135   // addresses). It will automatically use SNI and verify certificates based on these hostnames.
    136 
    137 private:
    138   void* ctx;  // actually type SSL_CTX, but we don't want to #include the OpenSSL headers here
    139   kj::Maybe<kj::Timer&> timer;
    140   kj::Maybe<kj::Duration> acceptTimeout;
    141 
    142   struct SniCallback;
    143 };
    144 
    145 class TlsPrivateKey {
    146   // A private key suitable for use in a TLS server.
    147 
    148 public:
    149   TlsPrivateKey(kj::ArrayPtr<const byte> asn1);
    150   // Parse a single binary (ASN1) private key. Supports PKCS8 keys as well as "traditional format"
    151   // RSA and DSA keys. Does not accept encrypted keys; it is the caller's responsibility to
    152   // decrypt.
    153 
    154   TlsPrivateKey(kj::StringPtr pem, kj::Maybe<kj::StringPtr> password = nullptr);
    155   // Parse a single PEM-encoded private key. Supports PKCS8 keys as well as "traditional format"
    156   // RSA and DSA keys. A password may optionally be provided and will be used if the key is
    157   // encrypted.
    158 
    159   ~TlsPrivateKey() noexcept(false);
    160 
    161   TlsPrivateKey(const TlsPrivateKey& other);
    162   TlsPrivateKey& operator=(const TlsPrivateKey& other);
    163   // Copy-by-refcount.
    164 
    165   inline TlsPrivateKey(TlsPrivateKey&& other): pkey(other.pkey) { other.pkey = nullptr; }
    166   inline TlsPrivateKey& operator=(TlsPrivateKey&& other) {
    167     pkey = other.pkey; other.pkey = nullptr;
    168     return *this;
    169   }
    170 
    171 private:
    172   void* pkey;  // actually type EVP_PKEY*
    173 
    174   friend class TlsContext;
    175 
    176   static int passwordCallback(char* buf, int size, int rwflag, void* u);
    177 };
    178 
    179 class TlsCertificate {
    180   // A TLS certificate, possibly with chained intermediate certificates.
    181 
    182 public:
    183   TlsCertificate(kj::ArrayPtr<const byte> asn1);
    184   // Parse a single binary (ASN1) X509 certificate.
    185 
    186   TlsCertificate(kj::ArrayPtr<const kj::ArrayPtr<const byte>> asn1);
    187   // Parse a chain of binary (ASN1) X509 certificates.
    188 
    189   TlsCertificate(kj::StringPtr pem);
    190   // Parse a PEM-encode X509 certificate or certificate chain. A chain can be constructed by
    191   // concatenating multiple PEM-encoded certificates, starting with the leaf certificate.
    192 
    193   ~TlsCertificate() noexcept(false);
    194 
    195   TlsCertificate(const TlsCertificate& other);
    196   TlsCertificate& operator=(const TlsCertificate& other);
    197   // Copy-by-refcount.
    198 
    199   inline TlsCertificate(TlsCertificate&& other) {
    200     memcpy(chain, other.chain, sizeof(chain));
    201     memset(other.chain, 0, sizeof(chain));
    202   }
    203   inline TlsCertificate& operator=(TlsCertificate&& other) {
    204     memcpy(chain, other.chain, sizeof(chain));
    205     memset(other.chain, 0, sizeof(chain));
    206     return *this;
    207   }
    208 
    209 private:
    210   void* chain[10];
    211   // Actually type X509*[10].
    212   //
    213   // Note that OpenSSL has a default maximum cert chain length of 10. Although configurable at
    214   // runtime, you'd actually have to convince the _peer_ to reconfigure, which is unlikely except
    215   // in specific use cases. So to avoid excess allocations we just assume a max of 10 certs.
    216   //
    217   // If this proves to be a problem, we should maybe use STACK_OF(X509) here, but stacks are not
    218   // refcounted -- the X509_chain_up_ref() function actually allocates a new stack and uprefs all
    219   // the certs.
    220 
    221   friend class TlsContext;
    222 };
    223 
    224 struct TlsKeypair {
    225   // A pair of a private key and a certificate, for use by a server.
    226 
    227   TlsPrivateKey privateKey;
    228   TlsCertificate certificate;
    229 };
    230 
    231 class TlsSniCallback {
    232   // Callback object to implement Server Name Indication, in which the server is able to decide
    233   // what key and certificate to use based on the hostname that the client is requesting.
    234   //
    235   // TODO(someday): Currently this callback is synchronous, because the OpenSSL API seems to be
    236   //   synchronous. Other people (e.g. Node) have figured out how to do it asynchronously, but
    237   //   it's unclear to me if and how this is possible while using the OpenSSL APIs. It looks like
    238   //   Node may be manually parsing the ClientHello message rather than relying on OpenSSL. We
    239   //   could do that but it's too much work for today.
    240 
    241 public:
    242   virtual kj::Maybe<TlsKeypair> getKey(kj::StringPtr hostname) = 0;
    243   // Get the key to use for `hostname`. Null return means use the default from
    244   // TlsContext::Options::defaultKeypair.
    245 };
    246 
    247 class TlsPeerIdentity final: public kj::PeerIdentity {
    248 public:
    249   KJ_DISALLOW_COPY(TlsPeerIdentity);
    250   ~TlsPeerIdentity() noexcept(false);
    251 
    252   kj::String toString() override;
    253 
    254   kj::PeerIdentity& getNetworkIdentity() { return *inner; }
    255   // Gets the PeerIdentity of the underlying network connection.
    256 
    257   bool hasCertificate() { return cert != nullptr; }
    258   // Did the peer even present a (trusted) certificate? Servers must always present certificates.
    259   // Clients need only present certificates when the `verifyClients` option is enabled.
    260   //
    261   // Methods of this class that read details of the certificate will throw exceptions when no
    262   // certificate was presented. We don't have them return `Maybe`s because most applications know
    263   // in advance whether or not a certificate should be present, so it would lead to lots of
    264   // `KJ_ASSERT_NONNULL`...
    265 
    266   kj::String getCommonName();
    267   // Get the authenticated common name from the certificate.
    268 
    269   bool matchesHostname(kj::StringPtr hostname);
    270   // Check if the certificate authenticates the given hostname, considering wildcards and SAN
    271   // extensions. If no certificate was provided, always returns false.
    272 
    273   // TODO(someday): Methods for other things. Match hostnames (i.e. evaluate wildcards and SAN)?
    274   //   Key fingerprint? Other certificate fields?
    275 
    276 private:
    277   void* cert;  // actually type X509*, but we don't want to #include the OpenSSL headers here.
    278   kj::Own<kj::PeerIdentity> inner;
    279 
    280 public:  // (not really public, only TlsConnection can call this)
    281   TlsPeerIdentity(void* cert, kj::Own<kj::PeerIdentity> inner, kj::Badge<TlsConnection>)
    282       : cert(cert), inner(kj::mv(inner)) {}
    283 };
    284 
    285 } // namespace kj