blob: 78cf768dcc66fbad92716bacd75bcb18e098c13c [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>
vasilvv872e7a32019-03-12 16:42:44 -07006#include <string>
QUICHE teama6ef0a62019-03-07 20:34:33 -05007
8#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h"
9
10#include "third_party/boringssl/src/include/openssl/aead.h"
11#include "third_party/boringssl/src/include/openssl/err.h"
12#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
13#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
dmcardle904ef182019-12-13 08:34:33 -080014#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050015
16namespace 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
22static 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.
27static const size_t kBoxKeySize = 32;
28
29struct 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
35CryptoSecretBoxer::CryptoSecretBoxer() {}
36
37CryptoSecretBoxer::~CryptoSecretBoxer() {}
38
39// static
40size_t CryptoSecretBoxer::GetKeySize() {
41 return kBoxKeySize;
42}
43
44// kAEAD is the AEAD used for boxing: AES-256-GCM-SIV.
45static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv;
46
vasilvvc48c8712019-03-11 13:38:16 -070047void CryptoSecretBoxer::SetKeys(const std::vector<std::string>& keys) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050048 DCHECK(!keys.empty());
49 const EVP_AEAD* const aead = kAEAD();
50 std::unique_ptr<State> new_state(new State);
51
vasilvvc48c8712019-03-11 13:38:16 -070052 for (const std::string& key : keys) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050053 DCHECK_EQ(kBoxKeySize, key.size());
54 bssl::UniquePtr<EVP_AEAD_CTX> ctx(
55 EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()),
56 key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH));
57 if (!ctx) {
58 ERR_clear_error();
dschinazi87c39c12019-05-07 21:01:12 -070059 QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_init failed";
QUICHE teama6ef0a62019-03-07 20:34:33 -050060 return;
61 }
62
63 new_state->ctxs.push_back(std::move(ctx));
64 }
65
66 QuicWriterMutexLock l(&lock_);
67 state_ = std::move(new_state);
68}
69
vasilvvc48c8712019-03-11 13:38:16 -070070std::string CryptoSecretBoxer::Box(QuicRandom* rand,
dmcardle904ef182019-12-13 08:34:33 -080071 quiche::QuicheStringPiece plaintext) const {
QUICHE teama6ef0a62019-03-07 20:34:33 -050072 // The box is formatted as:
73 // 12 bytes of random nonce
74 // n bytes of ciphertext
75 // 16 bytes of authenticator
76 size_t out_len =
77 kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
78
vasilvvc48c8712019-03-11 13:38:16 -070079 std::string ret;
QUICHE teama6ef0a62019-03-07 20:34:33 -050080 ret.resize(out_len);
81 uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data()));
82
83 // Write kSIVNonceSize bytes of random nonce to the beginning of the output
84 // buffer.
85 rand->RandBytes(out, kSIVNonceSize);
86 const uint8_t* const nonce = out;
87 out += kSIVNonceSize;
88 out_len -= kSIVNonceSize;
89
90 size_t bytes_written;
91 {
92 QuicReaderMutexLock l(&lock_);
93 if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len,
94 nonce, kSIVNonceSize,
95 reinterpret_cast<const uint8_t*>(plaintext.data()),
96 plaintext.size(), nullptr, 0)) {
97 ERR_clear_error();
dschinazi87c39c12019-05-07 21:01:12 -070098 QUIC_LOG(DFATAL) << "EVP_AEAD_CTX_seal failed";
QUICHE teama6ef0a62019-03-07 20:34:33 -050099 return "";
100 }
101 }
102
103 DCHECK_EQ(out_len, bytes_written);
104 return ret;
105}
106
dmcardle904ef182019-12-13 08:34:33 -0800107bool CryptoSecretBoxer::Unbox(quiche::QuicheStringPiece in_ciphertext,
vasilvvc48c8712019-03-11 13:38:16 -0700108 std::string* out_storage,
dmcardle904ef182019-12-13 08:34:33 -0800109 quiche::QuicheStringPiece* out) const {
QUICHE teama6ef0a62019-03-07 20:34:33 -0500110 if (in_ciphertext.size() < kSIVNonceSize) {
111 return false;
112 }
113
114 const uint8_t* const nonce =
115 reinterpret_cast<const uint8_t*>(in_ciphertext.data());
116 const uint8_t* const ciphertext = nonce + kSIVNonceSize;
117 const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize;
118
119 out_storage->resize(ciphertext_len);
120
121 bool ok = false;
122 {
123 QuicReaderMutexLock l(&lock_);
124 for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
125 size_t bytes_written;
126 if (EVP_AEAD_CTX_open(ctx.get(),
127 reinterpret_cast<uint8_t*>(
128 const_cast<char*>(out_storage->data())),
129 &bytes_written, ciphertext_len, nonce,
130 kSIVNonceSize, ciphertext, ciphertext_len, nullptr,
131 0)) {
132 ok = true;
dmcardle904ef182019-12-13 08:34:33 -0800133 *out = quiche::QuicheStringPiece(out_storage->data(), bytes_written);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500134 break;
135 }
136
137 ERR_clear_error();
138 }
139 }
140
141 return ok;
142}
143
144} // namespace quic