|  | // Copyright 2020 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "quic/tools/simple_ticket_crypter.h" | 
|  |  | 
|  | #include "third_party/boringssl/src/include/openssl/aead.h" | 
|  | #include "third_party/boringssl/src/include/openssl/rand.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr QuicTime::Delta kTicketKeyLifetime = | 
|  | QuicTime::Delta::FromSeconds(60 * 60 * 24 * 7); | 
|  |  | 
|  | // The format of an encrypted ticket is 1 byte for the key epoch, followed by | 
|  | // 16 bytes of IV, followed by the output from the AES-GCM Seal operation. The | 
|  | // seal operation has an overhead of 16 bytes for its auth tag. | 
|  | constexpr size_t kEpochSize = 1; | 
|  | constexpr size_t kIVSize = 16; | 
|  | constexpr size_t kAuthTagSize = 16; | 
|  |  | 
|  | // Offsets into the ciphertext to make message parsing easier. | 
|  | constexpr size_t kIVOffset = kEpochSize; | 
|  | constexpr size_t kMessageOffset = kIVOffset + kIVSize; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | SimpleTicketCrypter::SimpleTicketCrypter(QuicClock* clock) : clock_(clock) { | 
|  | RAND_bytes(&key_epoch_, 1); | 
|  | current_key_ = NewKey(); | 
|  | } | 
|  |  | 
|  | SimpleTicketCrypter::~SimpleTicketCrypter() = default; | 
|  |  | 
|  | size_t SimpleTicketCrypter::MaxOverhead() { | 
|  | return kEpochSize + kIVSize + kAuthTagSize; | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> SimpleTicketCrypter::Encrypt( | 
|  | absl::string_view in, absl::string_view encryption_key) { | 
|  | // This class is only used in Chromium, in which the |encryption_key| argument | 
|  | // will never be populated and an internally-cached key should be used for | 
|  | // encrypting tickets. | 
|  | QUICHE_DCHECK(encryption_key.empty()); | 
|  | MaybeRotateKeys(); | 
|  | std::vector<uint8_t> out(in.size() + MaxOverhead()); | 
|  | out[0] = key_epoch_; | 
|  | RAND_bytes(out.data() + kIVOffset, kIVSize); | 
|  | size_t out_len; | 
|  | const EVP_AEAD_CTX* ctx = current_key_->aead_ctx.get(); | 
|  | if (!EVP_AEAD_CTX_seal(ctx, out.data() + kMessageOffset, &out_len, | 
|  | out.size() - kMessageOffset, out.data() + kIVOffset, | 
|  | kIVSize, reinterpret_cast<const uint8_t*>(in.data()), | 
|  | in.size(), nullptr, 0)) { | 
|  | return std::vector<uint8_t>(); | 
|  | } | 
|  | out.resize(out_len + kMessageOffset); | 
|  | return out; | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> SimpleTicketCrypter::Decrypt(absl::string_view in) { | 
|  | MaybeRotateKeys(); | 
|  | if (in.size() < kMessageOffset) { | 
|  | return std::vector<uint8_t>(); | 
|  | } | 
|  | const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data()); | 
|  | std::vector<uint8_t> out(in.size() - kMessageOffset); | 
|  | size_t out_len; | 
|  | const EVP_AEAD_CTX* ctx = current_key_->aead_ctx.get(); | 
|  | if (input[0] != key_epoch_) { | 
|  | if (input[0] == static_cast<uint8_t>(key_epoch_ - 1) && previous_key_) { | 
|  | ctx = previous_key_->aead_ctx.get(); | 
|  | } else { | 
|  | return std::vector<uint8_t>(); | 
|  | } | 
|  | } | 
|  | if (!EVP_AEAD_CTX_open(ctx, out.data(), &out_len, out.size(), | 
|  | input + kIVOffset, kIVSize, input + kMessageOffset, | 
|  | in.size() - kMessageOffset, nullptr, 0)) { | 
|  | return std::vector<uint8_t>(); | 
|  | } | 
|  | out.resize(out_len); | 
|  | return out; | 
|  | } | 
|  |  | 
|  | void SimpleTicketCrypter::Decrypt( | 
|  | absl::string_view in, | 
|  | std::unique_ptr<quic::ProofSource::DecryptCallback> callback) { | 
|  | callback->Run(Decrypt(in)); | 
|  | } | 
|  |  | 
|  | void SimpleTicketCrypter::MaybeRotateKeys() { | 
|  | QuicTime now = clock_->ApproximateNow(); | 
|  | if (current_key_->expiration < now) { | 
|  | previous_key_ = std::move(current_key_); | 
|  | current_key_ = NewKey(); | 
|  | key_epoch_++; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SimpleTicketCrypter::Key> SimpleTicketCrypter::NewKey() { | 
|  | auto key = std::make_unique<SimpleTicketCrypter::Key>(); | 
|  | RAND_bytes(key->key, kKeySize); | 
|  | EVP_AEAD_CTX_init(key->aead_ctx.get(), EVP_aead_aes_128_gcm(), key->key, | 
|  | kKeySize, EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr); | 
|  | key->expiration = clock_->ApproximateNow() + kTicketKeyLifetime; | 
|  | return key; | 
|  | } | 
|  |  | 
|  | }  // namespace quic |