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
7 #include "openssl/aead.h"
8 #include "openssl/rand.h"
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
SimpleTicketCrypter(QuicClock * clock)30 SimpleTicketCrypter::SimpleTicketCrypter(QuicClock* clock) : clock_(clock) {
31 RAND_bytes(&key_epoch_, 1);
32 current_key_ = NewKey();
33 }
34
35 SimpleTicketCrypter::~SimpleTicketCrypter() = default;
36
MaxOverhead()37 size_t SimpleTicketCrypter::MaxOverhead() {
38 return kEpochSize + kIVSize + kAuthTagSize;
39 }
40
Encrypt(absl::string_view in,absl::string_view encryption_key)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
Decrypt(absl::string_view in)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
Decrypt(absl::string_view in,std::shared_ptr<quic::ProofSource::DecryptCallback> callback)88 void SimpleTicketCrypter::Decrypt(
89 absl::string_view in,
90 std::shared_ptr<quic::ProofSource::DecryptCallback> callback) {
91 callback->Run(Decrypt(in));
92 }
93
MaybeRotateKeys()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
NewKey()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
113