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 "net/quic/crypto/aead_base_encrypter.h"
6
7#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <string.h>
10
11#include "base/memory/scoped_ptr.h"
12
13using base::StringPiece;
14
15namespace net {
16
17namespace {
18
19// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
20// stack.
21void DLogOpenSslErrors() {
22#ifdef NDEBUG
23  while (ERR_get_error()) {}
24#else
25  while (unsigned long error = ERR_get_error()) {
26    char buf[120];
27    ERR_error_string_n(error, buf, arraysize(buf));
28    DLOG(ERROR) << "OpenSSL error: " << buf;
29  }
30#endif
31}
32
33}  // namespace
34
35AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* aead_alg,
36                                     size_t key_size,
37                                     size_t auth_tag_size,
38                                     size_t nonce_prefix_size)
39    : aead_alg_(aead_alg),
40      key_size_(key_size),
41      auth_tag_size_(auth_tag_size),
42      nonce_prefix_size_(nonce_prefix_size) {
43  DCHECK_LE(key_size_, sizeof(key_));
44  DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
45}
46
47AeadBaseEncrypter::~AeadBaseEncrypter() {}
48
49bool AeadBaseEncrypter::SetKey(StringPiece key) {
50  DCHECK_EQ(key.size(), key_size_);
51  if (key.size() != key_size_) {
52    return false;
53  }
54  memcpy(key_, key.data(), key.size());
55
56  EVP_AEAD_CTX_cleanup(ctx_.get());
57
58  if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
59                         auth_tag_size_, NULL)) {
60    DLogOpenSslErrors();
61    return false;
62  }
63
64  return true;
65}
66
67bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
68  DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
69  if (nonce_prefix.size() != nonce_prefix_size_) {
70    return false;
71  }
72  memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
73  return true;
74}
75
76bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
77                                StringPiece associated_data,
78                                StringPiece plaintext,
79                                unsigned char* output) {
80  if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
81    return false;
82  }
83
84  size_t len;
85  if (!EVP_AEAD_CTX_seal(
86          ctx_.get(),
87          output,
88          &len,
89          plaintext.size() + auth_tag_size_,
90          reinterpret_cast<const uint8_t*>(nonce.data()),
91          nonce.size(),
92          reinterpret_cast<const uint8_t*>(plaintext.data()),
93          plaintext.size(),
94          reinterpret_cast<const uint8_t*>(associated_data.data()),
95          associated_data.size())) {
96    DLogOpenSslErrors();
97    return false;
98  }
99
100  return true;
101}
102
103QuicData* AeadBaseEncrypter::EncryptPacket(
104    QuicPacketSequenceNumber sequence_number,
105    StringPiece associated_data,
106    StringPiece plaintext) {
107  size_t ciphertext_size = GetCiphertextSize(plaintext.length());
108  scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
109
110  // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
111  // same sequence number twice.
112  uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
113  const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
114  DCHECK_LE(nonce_size, sizeof(nonce));
115  memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
116  memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
117  if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
118               associated_data, plaintext,
119               reinterpret_cast<unsigned char*>(ciphertext.get()))) {
120    return NULL;
121  }
122
123  return new QuicData(ciphertext.release(), ciphertext_size, true);
124}
125
126size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
127
128size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
129  return nonce_prefix_size_;
130}
131
132size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
133  return ciphertext_size - auth_tag_size_;
134}
135
136size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
137  return plaintext_size + auth_tag_size_;
138}
139
140StringPiece AeadBaseEncrypter::GetKey() const {
141  return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
142}
143
144StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
145  if (nonce_prefix_size_ == 0) {
146    return StringPiece();
147  }
148  return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
149                     nonce_prefix_size_);
150}
151
152}  // namespace net
153