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