xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2013 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/core/crypto/crypto_secret_boxer.h"
6 
7 #include <cstdint>
8 #include <string>
9 
10 #include "absl/strings/string_view.h"
11 #include "openssl/aead.h"
12 #include "openssl/err.h"
13 #include "quiche/quic/core/crypto/quic_random.h"
14 #include "quiche/quic/platform/api/quic_logging.h"
15 
16 namespace quic {
17 
18 // kSIVNonceSize contains the number of bytes of nonce in each AES-GCM-SIV box.
19 // AES-GCM-SIV takes a 12-byte nonce and, since the messages are so small, each
20 // key is good for more than 2^64 source-address tokens. See table 1 of
21 // https://eprint.iacr.org/2017/168.pdf
22 static const size_t kSIVNonceSize = 12;
23 
24 // AES-GCM-SIV comes in AES-128 and AES-256 flavours. The AES-256 version is
25 // used here so that the key size matches the 256-bit XSalsa20 keys that we
26 // used to use.
27 static const size_t kBoxKeySize = 32;
28 
29 struct CryptoSecretBoxer::State {
30   // ctxs are the initialised AEAD contexts. These objects contain the
31   // scheduled AES state for each of the keys.
32   std::vector<bssl::UniquePtr<EVP_AEAD_CTX>> ctxs;
33 };
34 
CryptoSecretBoxer()35 CryptoSecretBoxer::CryptoSecretBoxer() {}
36 
~CryptoSecretBoxer()37 CryptoSecretBoxer::~CryptoSecretBoxer() {}
38 
39 // static
GetKeySize()40 size_t CryptoSecretBoxer::GetKeySize() { return kBoxKeySize; }
41 
42 // kAEAD is the AEAD used for boxing: AES-256-GCM-SIV.
43 static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv;
44 
SetKeys(const std::vector<std::string> & keys)45 bool CryptoSecretBoxer::SetKeys(const std::vector<std::string>& keys) {
46   if (keys.empty()) {
47     QUIC_LOG(DFATAL) << "No keys supplied!";
48     return false;
49   }
50   const EVP_AEAD* const aead = kAEAD();
51   std::unique_ptr<State> new_state(new State);
52 
53   for (const std::string& key : keys) {
54     QUICHE_DCHECK_EQ(kBoxKeySize, key.size());
55     bssl::UniquePtr<EVP_AEAD_CTX> ctx(
56         EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()),
57                          key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH));
58     if (!ctx) {
59       ERR_clear_error();
60       QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_init failed";
61       return false;
62     }
63 
64     new_state->ctxs.push_back(std::move(ctx));
65   }
66 
67   QuicWriterMutexLock l(&lock_);
68   state_ = std::move(new_state);
69   return true;
70 }
71 
Box(QuicRandom * rand,absl::string_view plaintext) const72 std::string CryptoSecretBoxer::Box(QuicRandom* rand,
73                                    absl::string_view plaintext) const {
74   // The box is formatted as:
75   //   12 bytes of random nonce
76   //   n bytes of ciphertext
77   //   16 bytes of authenticator
78   size_t out_len =
79       kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
80 
81   std::string ret;
82   ret.resize(out_len);
83   uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data()));
84 
85   // Write kSIVNonceSize bytes of random nonce to the beginning of the output
86   // buffer.
87   rand->RandBytes(out, kSIVNonceSize);
88   const uint8_t* const nonce = out;
89   out += kSIVNonceSize;
90   out_len -= kSIVNonceSize;
91 
92   size_t bytes_written;
93   {
94     QuicReaderMutexLock l(&lock_);
95     if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len,
96                            nonce, kSIVNonceSize,
97                            reinterpret_cast<const uint8_t*>(plaintext.data()),
98                            plaintext.size(), nullptr, 0)) {
99       ERR_clear_error();
100       QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_seal failed";
101       return "";
102     }
103   }
104 
105   QUICHE_DCHECK_EQ(out_len, bytes_written);
106   return ret;
107 }
108 
Unbox(absl::string_view in_ciphertext,std::string * out_storage,absl::string_view * out) const109 bool CryptoSecretBoxer::Unbox(absl::string_view in_ciphertext,
110                               std::string* out_storage,
111                               absl::string_view* out) const {
112   if (in_ciphertext.size() < kSIVNonceSize) {
113     return false;
114   }
115 
116   const uint8_t* const nonce =
117       reinterpret_cast<const uint8_t*>(in_ciphertext.data());
118   const uint8_t* const ciphertext = nonce + kSIVNonceSize;
119   const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize;
120 
121   out_storage->resize(ciphertext_len);
122 
123   bool ok = false;
124   {
125     QuicReaderMutexLock l(&lock_);
126     for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
127       size_t bytes_written;
128       if (EVP_AEAD_CTX_open(ctx.get(),
129                             reinterpret_cast<uint8_t*>(
130                                 const_cast<char*>(out_storage->data())),
131                             &bytes_written, ciphertext_len, nonce,
132                             kSIVNonceSize, ciphertext, ciphertext_len, nullptr,
133                             0)) {
134         ok = true;
135         *out = absl::string_view(out_storage->data(), bytes_written);
136         break;
137       }
138 
139       ERR_clear_error();
140     }
141   }
142 
143   return ok;
144 }
145 
146 }  // namespace quic
147