copy wangle back into folly
[folly.git] / folly / wangle / ssl / TLSTicketKeyManager.h
1 /*
2  *  Copyright (c) 2015, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree. An additional grant
7  *  of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
10 #pragma once
11
12 #include <folly/io/async/SSLContext.h>
13 #include <folly/io/async/EventBase.h>
14
15 namespace folly {
16
17 #ifndef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
18 class TLSTicketKeyManager {};
19 #else
20 class SSLStats;
21 /**
22  * The TLSTicketKeyManager handles TLS ticket key encryption and decryption in
23  * a way that facilitates sharing the ticket keys across a range of servers.
24  * Hash chaining is employed to achieve frequent key rotation with minimal
25  * configuration change.  The scheme is as follows:
26  *
27  * The manager is supplied with three lists of seeds (old, current and new).
28  * The config should be updated with new seeds periodically (e.g., daily).
29  * 3 config changes are recommended to achieve the smoothest seed rotation
30  * eg:
31  *     1. Introduce new seed in the push prior to rotation
32  *     2. Rotation push
33  *     3. Remove old seeds in the push following rotation
34  *
35  * Multiple seeds are supported but only a single seed is required.
36  *
37  * Generating encryption keys from the seed works as follows.  For a given
38  * seed, hash forward N times where N is currently the constant 1.
39  * This is the base key.  The name of the base key is the first 4
40  * bytes of hash(hash(seed), N).  This is copied into the first 4 bytes of the
41  * TLS ticket key name field.
42  *
43  * For each new ticket encryption, the manager generates a random 12 byte salt.
44  * Hash the salt and the base key together to form the encryption key for
45  * that ticket.  The salt is included in the ticket's 'key name' field so it
46  * can be used to derive the decryption key.  The salt is copied into the second
47  * 8 bytes of the TLS ticket key name field.
48  *
49  * A key is valid for decryption for the lifetime of the instance.
50  * Sessions will be valid for less time than that, which results in an extra
51  * symmetric decryption to discover the session is expired.
52  *
53  * A TLSTicketKeyManager should be used in only one thread, and should have
54  * a 1:1 relationship with the SSLContext provided.
55  *
56  */
57 class TLSTicketKeyManager : private boost::noncopyable {
58  public:
59
60   explicit TLSTicketKeyManager(folly::SSLContext* ctx,
61                                SSLStats* stats);
62
63   virtual ~TLSTicketKeyManager();
64
65   /**
66    * SSL callback to set up encryption/decryption context for a TLS Ticket Key.
67    *
68    * This will be supplied to the SSL library via
69    * SSL_CTX_set_tlsext_ticket_key_cb.
70    */
71   static int callback(SSL* ssl, unsigned char* keyName,
72                       unsigned char* iv,
73                       EVP_CIPHER_CTX* cipherCtx,
74                       HMAC_CTX* hmacCtx, int encrypt);
75
76   /**
77    * Initialize the manager with three sets of seeds.  There must be at least
78    * one current seed, or the manager will revert to the default SSL behavior.
79    *
80    * @param oldSeeds Seeds previously used which can still decrypt.
81    * @param currentSeeds Seeds to use for new ticket encryptions.
82    * @param newSeeds Seeds which will be used soon, can be used to decrypt
83    *                 in case some servers in the cluster have already rotated.
84    */
85   bool setTLSTicketKeySeeds(const std::vector<std::string>& oldSeeds,
86                             const std::vector<std::string>& currentSeeds,
87                             const std::vector<std::string>& newSeeds);
88
89  private:
90   enum TLSTicketSeedType {
91     SEED_OLD = 0,
92     SEED_CURRENT,
93     SEED_NEW
94   };
95
96   /* The seeds supplied by the configuration */
97   struct TLSTicketSeed {
98     std::string seed_;
99     TLSTicketSeedType type_;
100     unsigned char seedName_[SHA256_DIGEST_LENGTH];
101   };
102
103   struct TLSTicketKeySource {
104     int32_t hashCount_;
105     std::string keyName_;
106     TLSTicketSeedType type_;
107     unsigned char keySource_[SHA256_DIGEST_LENGTH];
108   };
109
110   /**
111    * Method to setup encryption/decryption context for a TLS Ticket Key
112    *
113    * OpenSSL documentation is thin on the return value semantics.
114    *
115    * For encrypt=1, return < 0 on error, >= 0 for successfully initialized
116    * For encrypt=0, return < 0 on error, 0 on key not found
117    *                 1 on key found, 2 renew_ticket
118    *
119    * renew_ticket means a new ticket will be issued.  We could return this value
120    * when receiving a ticket encrypted with a key derived from an OLD seed.
121    * However, session_timeout seconds after deploying with a seed
122    * rotated from  CURRENT -> OLD, there will be no valid tickets outstanding
123    * encrypted with the old key.  This grace period means no unnecessary
124    * handshakes will be performed.  If the seed is believed compromised, it
125    * should NOT be configured as an OLD seed.
126    */
127   int processTicket(SSL* ssl, unsigned char* keyName,
128                     unsigned char* iv,
129                     EVP_CIPHER_CTX* cipherCtx,
130                     HMAC_CTX* hmacCtx, int encrypt);
131
132   // Creates the name for the nth key generated from seed
133   std::string makeKeyName(TLSTicketSeed* seed, uint32_t n,
134                           unsigned char* nameBuf);
135
136   /**
137    * Creates the key hashCount hashes from the given seed and inserts it in
138    * ticketKeys.  A naked pointer to the key is returned for additional
139    * processing if needed.
140    */
141   TLSTicketKeySource* insertNewKey(TLSTicketSeed* seed, uint32_t hashCount,
142                                    TLSTicketKeySource* prevKeySource);
143
144   /**
145    * hashes input N times placing result in output, which must be at least
146    * SHA256_DIGEST_LENGTH long.
147    */
148   void hashNth(const unsigned char* input, size_t input_len,
149                unsigned char* output, uint32_t n);
150
151   /**
152    * Adds the given seed to the manager
153    */
154   TLSTicketSeed* insertSeed(const std::string& seedInput,
155                             TLSTicketSeedType type);
156
157   /**
158    * Locate a key for encrypting a new ticket
159    */
160   TLSTicketKeySource* findEncryptionKey();
161
162   /**
163    * Locate a key for decrypting a ticket with the given keyName
164    */
165   TLSTicketKeySource* findDecryptionKey(unsigned char* keyName);
166
167   /**
168    * Derive a unique key from the parent key and the salt via hashing
169    */
170   void makeUniqueKeys(unsigned char* parentKey, size_t keyLen,
171                       unsigned char* salt, unsigned char* output);
172
173   /**
174    * For standalone decryption utility
175    */
176   friend int decrypt_fb_ticket(folly::TLSTicketKeyManager* manager,
177                                const std::string& testTicket,
178                                SSL_SESSION **psess);
179
180   typedef std::vector<std::unique_ptr<TLSTicketSeed>> TLSTicketSeedList;
181   typedef std::map<std::string, std::unique_ptr<TLSTicketKeySource> >
182     TLSTicketKeyMap;
183   typedef std::vector<TLSTicketKeySource *> TLSActiveKeyList;
184
185   TLSTicketSeedList ticketSeeds_;
186   // All key sources that can be used for decryption
187   TLSTicketKeyMap ticketKeys_;
188   // Key sources that can be used for encryption
189   TLSActiveKeyList activeKeys_;
190
191   folly::SSLContext* ctx_;
192   uint32_t randState_;
193   SSLStats* stats_{nullptr};
194
195   static int32_t sExDataIndex_;
196 };
197 #endif
198 }