Bence Béky | bac0405 | 2022-04-07 15:44:29 -0400 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "quiche/quic/tools/simple_ticket_crypter.h" |
| 6 | |
vasilvv | daa2fda | 2022-04-11 14:08:36 -0700 | [diff] [blame] | 7 | #include "openssl/aead.h" |
| 8 | #include "openssl/rand.h" |
Bence Béky | bac0405 | 2022-04-07 15:44:29 -0400 | [diff] [blame] | 9 | |
| 10 | namespace quic { |
| 11 | |
| 12 | namespace { |
| 13 | |
| 14 | constexpr QuicTime::Delta kTicketKeyLifetime = |
| 15 | QuicTime::Delta::FromSeconds(60 * 60 * 24 * 7); |
| 16 | |
| 17 | // The format of an encrypted ticket is 1 byte for the key epoch, followed by |
| 18 | // 16 bytes of IV, followed by the output from the AES-GCM Seal operation. The |
| 19 | // seal operation has an overhead of 16 bytes for its auth tag. |
| 20 | constexpr size_t kEpochSize = 1; |
| 21 | constexpr size_t kIVSize = 16; |
| 22 | constexpr size_t kAuthTagSize = 16; |
| 23 | |
| 24 | // Offsets into the ciphertext to make message parsing easier. |
| 25 | constexpr size_t kIVOffset = kEpochSize; |
| 26 | constexpr size_t kMessageOffset = kIVOffset + kIVSize; |
| 27 | |
| 28 | } // namespace |
| 29 | |
| 30 | SimpleTicketCrypter::SimpleTicketCrypter(QuicClock* clock) : clock_(clock) { |
| 31 | RAND_bytes(&key_epoch_, 1); |
| 32 | current_key_ = NewKey(); |
| 33 | } |
| 34 | |
| 35 | SimpleTicketCrypter::~SimpleTicketCrypter() = default; |
| 36 | |
| 37 | size_t SimpleTicketCrypter::MaxOverhead() { |
| 38 | return kEpochSize + kIVSize + kAuthTagSize; |
| 39 | } |
| 40 | |
| 41 | std::vector<uint8_t> SimpleTicketCrypter::Encrypt( |
| 42 | absl::string_view in, absl::string_view encryption_key) { |
| 43 | // This class is only used in Chromium, in which the |encryption_key| argument |
| 44 | // will never be populated and an internally-cached key should be used for |
| 45 | // encrypting tickets. |
| 46 | QUICHE_DCHECK(encryption_key.empty()); |
| 47 | MaybeRotateKeys(); |
| 48 | std::vector<uint8_t> out(in.size() + MaxOverhead()); |
| 49 | out[0] = key_epoch_; |
| 50 | RAND_bytes(out.data() + kIVOffset, kIVSize); |
| 51 | size_t out_len; |
| 52 | const EVP_AEAD_CTX* ctx = current_key_->aead_ctx.get(); |
| 53 | if (!EVP_AEAD_CTX_seal(ctx, out.data() + kMessageOffset, &out_len, |
| 54 | out.size() - kMessageOffset, out.data() + kIVOffset, |
| 55 | kIVSize, reinterpret_cast<const uint8_t*>(in.data()), |
| 56 | in.size(), nullptr, 0)) { |
| 57 | return std::vector<uint8_t>(); |
| 58 | } |
| 59 | out.resize(out_len + kMessageOffset); |
| 60 | return out; |
| 61 | } |
| 62 | |
| 63 | std::vector<uint8_t> SimpleTicketCrypter::Decrypt(absl::string_view in) { |
| 64 | MaybeRotateKeys(); |
| 65 | if (in.size() < kMessageOffset) { |
| 66 | return std::vector<uint8_t>(); |
| 67 | } |
| 68 | const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data()); |
| 69 | std::vector<uint8_t> out(in.size() - kMessageOffset); |
| 70 | size_t out_len; |
| 71 | const EVP_AEAD_CTX* ctx = current_key_->aead_ctx.get(); |
| 72 | if (input[0] != key_epoch_) { |
| 73 | if (input[0] == static_cast<uint8_t>(key_epoch_ - 1) && previous_key_) { |
| 74 | ctx = previous_key_->aead_ctx.get(); |
| 75 | } else { |
| 76 | return std::vector<uint8_t>(); |
| 77 | } |
| 78 | } |
| 79 | if (!EVP_AEAD_CTX_open(ctx, out.data(), &out_len, out.size(), |
| 80 | input + kIVOffset, kIVSize, input + kMessageOffset, |
| 81 | in.size() - kMessageOffset, nullptr, 0)) { |
| 82 | return std::vector<uint8_t>(); |
| 83 | } |
| 84 | out.resize(out_len); |
| 85 | return out; |
| 86 | } |
| 87 | |
| 88 | void SimpleTicketCrypter::Decrypt( |
| 89 | absl::string_view in, |
QUICHE team | 5fab28c | 2022-05-10 12:08:49 -0700 | [diff] [blame] | 90 | std::shared_ptr<quic::ProofSource::DecryptCallback> callback) { |
Bence Béky | bac0405 | 2022-04-07 15:44:29 -0400 | [diff] [blame] | 91 | callback->Run(Decrypt(in)); |
| 92 | } |
| 93 | |
| 94 | void SimpleTicketCrypter::MaybeRotateKeys() { |
| 95 | QuicTime now = clock_->ApproximateNow(); |
| 96 | if (current_key_->expiration < now) { |
| 97 | previous_key_ = std::move(current_key_); |
| 98 | current_key_ = NewKey(); |
| 99 | key_epoch_++; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | std::unique_ptr<SimpleTicketCrypter::Key> SimpleTicketCrypter::NewKey() { |
| 104 | auto key = std::make_unique<SimpleTicketCrypter::Key>(); |
| 105 | RAND_bytes(key->key, kKeySize); |
| 106 | EVP_AEAD_CTX_init(key->aead_ctx.get(), EVP_aead_aes_128_gcm(), key->key, |
| 107 | kKeySize, EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr); |
| 108 | key->expiration = clock_->ApproximateNow() + kTicketKeyLifetime; |
| 109 | return key; |
| 110 | } |
| 111 | |
| 112 | } // namespace quic |