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/crypto_secret_boxer.h"
6
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "net/quic/crypto/crypto_protocol.h"
10#include "net/quic/crypto/quic_decrypter.h"
11#include "net/quic/crypto/quic_encrypter.h"
12#include "net/quic/crypto/quic_random.h"
13
14using base::StringPiece;
15using std::string;
16
17namespace net {
18
19// Defined kKeySize for GetKeySize() and SetKey().
20static const size_t kKeySize = 16;
21
22// kBoxNonceSize contains the number of bytes of nonce that we use in each box.
23// TODO(rtenneti): Add support for kBoxNonceSize to be 16 bytes.
24//
25// From agl@:
26//   96-bit nonces are on the edge. An attacker who can collect 2^41
27//   source-address tokens has a 1% chance of finding a duplicate.
28//
29//   The "average" DDoS is now 32.4M PPS. That's 2^25 source-address tokens
30//   per second. So one day of that DDoS botnot would reach the 1% mark.
31//
32//   It's not terrible, but it's not a "forget about it" margin.
33static const size_t kBoxNonceSize = 12;
34
35// static
36size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; }
37
38void CryptoSecretBoxer::SetKey(StringPiece key) {
39  DCHECK_EQ(kKeySize, key.size());
40  key_ = key.as_string();
41}
42
43string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
44  scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG));
45  if (!encrypter->SetKey(key_)) {
46    DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed.";
47    return string();
48  }
49  size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
50
51  string ret;
52  const size_t len = kBoxNonceSize + ciphertext_size;
53  ret.resize(len);
54  char* data = &ret[0];
55
56  // Generate nonce.
57  rand->RandBytes(data, kBoxNonceSize);
58  memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size());
59
60  if (!encrypter->Encrypt(StringPiece(data, kBoxNonceSize), StringPiece(),
61                          plaintext, reinterpret_cast<unsigned char*>(
62                                         data + kBoxNonceSize))) {
63    DLOG(DFATAL) << "CryptoSecretBoxer's Encrypt failed.";
64    return string();
65  }
66
67  return ret;
68}
69
70bool CryptoSecretBoxer::Unbox(StringPiece ciphertext,
71                              string* out_storage,
72                              StringPiece* out) const {
73  if (ciphertext.size() < kBoxNonceSize) {
74    return false;
75  }
76
77  char nonce[kBoxNonceSize];
78  memcpy(nonce, ciphertext.data(), kBoxNonceSize);
79  ciphertext.remove_prefix(kBoxNonceSize);
80
81  size_t len = ciphertext.size();
82  out_storage->resize(len);
83  char* data = const_cast<char*>(out_storage->data());
84
85  scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG));
86  if (!decrypter->SetKey(key_)) {
87    DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed.";
88    return false;
89  }
90  if (!decrypter->Decrypt(StringPiece(nonce, kBoxNonceSize), StringPiece(),
91                          ciphertext, reinterpret_cast<unsigned char*>(data),
92                          &len)) {
93    return false;
94  }
95
96  out->set(data, len);
97  return true;
98}
99
100}  // namespace net
101