2 * Copyright (c) 2015, Facebook, Inc.
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.
10 #include <folly/wangle/ssl/TLSTicketKeyManager.h>
12 #include <folly/wangle/ssl/SSLStats.h>
13 #include <folly/wangle/ssl/SSLUtil.h>
15 #include <folly/String.h>
16 #include <openssl/aes.h>
17 #include <openssl/rand.h>
18 #include <openssl/ssl.h>
19 #include <folly/io/async/AsyncTimeout.h>
21 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
26 const int kTLSTicketKeyNameLen = 4;
27 const int kTLSTicketKeySaltLen = 12;
34 // TLSTicketKeyManager Implementation
35 int32_t TLSTicketKeyManager::sExDataIndex_ = -1;
37 TLSTicketKeyManager::TLSTicketKeyManager(SSLContext* ctx, SSLStats* stats)
41 SSLUtil::getSSLCtxExIndex(&sExDataIndex_);
42 SSL_CTX_set_ex_data(ctx_->getSSLCtx(), sExDataIndex_, this);
45 TLSTicketKeyManager::~TLSTicketKeyManager() {
49 TLSTicketKeyManager::callback(SSL* ssl, unsigned char* keyName,
51 EVP_CIPHER_CTX* cipherCtx,
52 HMAC_CTX* hmacCtx, int encrypt) {
53 TLSTicketKeyManager* manager = nullptr;
54 SSL_CTX* ctx = SSL_get_SSL_CTX(ssl);
55 manager = (TLSTicketKeyManager *)SSL_CTX_get_ex_data(ctx, sExDataIndex_);
57 if (manager == nullptr) {
58 LOG(FATAL) << "Null TLSTicketKeyManager in callback" ;
61 return manager->processTicket(ssl, keyName, iv, cipherCtx, hmacCtx, encrypt);
65 TLSTicketKeyManager::processTicket(SSL* ssl, unsigned char* keyName,
67 EVP_CIPHER_CTX* cipherCtx,
68 HMAC_CTX* hmacCtx, int encrypt) {
69 uint8_t salt[kTLSTicketKeySaltLen];
70 uint8_t* saltptr = nullptr;
71 uint8_t output[SHA256_DIGEST_LENGTH];
72 uint8_t* hmacKey = nullptr;
73 uint8_t* aesKey = nullptr;
74 TLSTicketKeySource* key = nullptr;
78 key = findEncryptionKey();
80 // no keys available to encrypt
81 VLOG(2) << "No TLS ticket key found";
84 VLOG(4) << "Encrypting new ticket with key name=" <<
85 SSLUtil::hexlify(key->keyName_);
87 // Get a random salt and write out key name
88 RAND_pseudo_bytes(salt, (int)sizeof(salt));
89 memcpy(keyName, key->keyName_.data(), kTLSTicketKeyNameLen);
90 memcpy(keyName + kTLSTicketKeyNameLen, salt, kTLSTicketKeySaltLen);
92 // Create the unique keys by hashing with the salt
93 makeUniqueKeys(key->keySource_, sizeof(key->keySource_), salt, output);
94 // This relies on the fact that SHA256 has 32 bytes of output
95 // and that AES-128 keys are 16 bytes
97 aesKey = output + SHA256_DIGEST_LENGTH / 2;
99 // Initialize iv and cipher/mac CTX
100 RAND_pseudo_bytes(iv, AES_BLOCK_SIZE);
101 HMAC_Init_ex(hmacCtx, hmacKey, SHA256_DIGEST_LENGTH / 2,
102 EVP_sha256(), nullptr);
103 EVP_EncryptInit_ex(cipherCtx, EVP_aes_128_cbc(), nullptr, aesKey, iv);
107 key = findDecryptionKey(keyName);
108 if (key == nullptr) {
109 // no ticket found for decryption - will issue a new ticket
111 string skeyName((char *)keyName, kTLSTicketKeyNameLen);
112 VLOG(4) << "Can't find ticket key with name=" <<
113 SSLUtil::hexlify(skeyName)<< ", will generate new ticket";
118 VLOG(4) << "Decrypting ticket with key name=" <<
119 SSLUtil::hexlify(key->keyName_);
121 // Reconstruct the unique key via the salt
122 saltptr = keyName + kTLSTicketKeyNameLen;
123 makeUniqueKeys(key->keySource_, sizeof(key->keySource_), saltptr, output);
125 aesKey = output + SHA256_DIGEST_LENGTH / 2;
127 // Initialize cipher/mac CTX
128 HMAC_Init_ex(hmacCtx, hmacKey, SHA256_DIGEST_LENGTH / 2,
129 EVP_sha256(), nullptr);
130 EVP_DecryptInit_ex(cipherCtx, EVP_aes_128_cbc(), nullptr, aesKey, iv);
135 // result records whether a ticket key was found to decrypt this ticket,
136 // not wether the session was re-used.
138 stats_->recordTLSTicket(encrypt, result);
145 TLSTicketKeyManager::setTLSTicketKeySeeds(
146 const std::vector<std::string>& oldSeeds,
147 const std::vector<std::string>& currentSeeds,
148 const std::vector<std::string>& newSeeds) {
154 ticketSeeds_.clear();
155 const std::vector<string> *seedList = &oldSeeds;
156 for (uint32_t i = 0; i < 3; i++) {
157 TLSTicketSeedType type = (TLSTicketSeedType)i;
158 if (type == SEED_CURRENT) {
159 seedList = ¤tSeeds;
160 } else if (type == SEED_NEW) {
161 seedList = &newSeeds;
164 for (const auto& seedInput: *seedList) {
165 TLSTicketSeed* seed = insertSeed(seedInput, type);
166 if (seed == nullptr) {
170 insertNewKey(seed, 1, nullptr);
174 VLOG(2) << "One or more seeds failed to decode";
177 if (ticketKeys_.size() == 0 || activeKeys_.size() == 0) {
178 LOG(WARNING) << "No keys configured, falling back to default";
179 SSL_CTX_set_tlsext_ticket_key_cb(ctx_->getSSLCtx(), nullptr);
182 SSL_CTX_set_tlsext_ticket_key_cb(ctx_->getSSLCtx(),
183 TLSTicketKeyManager::callback);
189 TLSTicketKeyManager::makeKeyName(TLSTicketSeed* seed, uint32_t n,
190 unsigned char* nameBuf) {
194 SHA256_Update(&ctx, seed->seedName_, sizeof(seed->seedName_));
195 SHA256_Update(&ctx, &n, sizeof(n));
196 SHA256_Final(nameBuf, &ctx);
197 return string((char *)nameBuf, kTLSTicketKeyNameLen);
200 TLSTicketKeyManager::TLSTicketKeySource*
201 TLSTicketKeyManager::insertNewKey(TLSTicketSeed* seed, uint32_t hashCount,
202 TLSTicketKeySource* prevKey) {
203 unsigned char nameBuf[SHA256_DIGEST_LENGTH];
204 std::unique_ptr<TLSTicketKeySource> newKey(new TLSTicketKeySource);
206 // This function supports hash chaining but it is not currently used.
208 if (prevKey != nullptr) {
209 hashNth(prevKey->keySource_, sizeof(prevKey->keySource_),
210 newKey->keySource_, 1);
212 // can't go backwards or the current is missing, start from the beginning
213 hashNth((unsigned char *)seed->seed_.data(), seed->seed_.length(),
214 newKey->keySource_, hashCount);
217 newKey->hashCount_ = hashCount;
218 newKey->keyName_ = makeKeyName(seed, hashCount, nameBuf);
219 newKey->type_ = seed->type_;
220 auto it = ticketKeys_.insert(std::make_pair(newKey->keyName_,
223 auto key = it.first->second.get();
224 if (key->type_ == SEED_CURRENT) {
225 activeKeys_.push_back(key);
227 VLOG(4) << "Adding key for " << hashCount << " type=" <<
228 (uint32_t)key->type_ << " Name=" << SSLUtil::hexlify(key->keyName_);
234 TLSTicketKeyManager::hashNth(const unsigned char* input, size_t input_len,
235 unsigned char* output, uint32_t n) {
237 for (uint32_t i = 0; i < n; i++) {
238 SHA256(input, input_len, output);
240 input_len = SHA256_DIGEST_LENGTH;
244 TLSTicketKeyManager::TLSTicketSeed *
245 TLSTicketKeyManager::insertSeed(const string& seedInput,
246 TLSTicketSeedType type) {
247 TLSTicketSeed* seed = nullptr;
250 if (!folly::unhexlify<string, string>(seedInput, seedOutput)) {
251 LOG(WARNING) << "Failed to decode seed type=" << (uint32_t)type <<
252 " seed=" << seedInput;
256 seed = new TLSTicketSeed();
257 seed->seed_ = seedOutput;
259 SHA256((unsigned char *)seedOutput.data(), seedOutput.length(),
261 ticketSeeds_.push_back(std::unique_ptr<TLSTicketSeed>(seed));
266 TLSTicketKeyManager::TLSTicketKeySource *
267 TLSTicketKeyManager::findEncryptionKey() {
268 TLSTicketKeySource* result = nullptr;
269 // call to rand here is a bit hokey since it's not cryptographically
270 // random, and is predictably seeded with 0. However, activeKeys_
271 // is probably not going to have very many keys in it, and most
273 size_t numKeys = activeKeys_.size();
275 result = activeKeys_[rand_r(&randState_) % numKeys];
280 TLSTicketKeyManager::TLSTicketKeySource *
281 TLSTicketKeyManager::findDecryptionKey(unsigned char* keyName) {
282 string name((char *)keyName, kTLSTicketKeyNameLen);
283 TLSTicketKeySource* key = nullptr;
284 TLSTicketKeyMap::iterator mapit = ticketKeys_.find(name);
285 if (mapit != ticketKeys_.end()) {
286 key = mapit->second.get();
292 TLSTicketKeyManager::makeUniqueKeys(unsigned char* parentKey,
295 unsigned char* output) {
298 SHA256_Init(&hash_ctx);
299 SHA256_Update(&hash_ctx, parentKey, keyLen);
300 SHA256_Update(&hash_ctx, salt, kTLSTicketKeySaltLen);
301 SHA256_Final(output, &hash_ctx);