blob: 79d0ec1a8a0b9b3dbc28e7b7ab97159d02bd2238 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
#include <openssl/evp.h>
#include <string.h>
#include "base/memory/scoped_ptr.h"
using base::StringPiece;
namespace net {
namespace {
const size_t kKeySize = 16;
const size_t kNoncePrefixSize = 4;
const size_t kAESNonceSize = 12;
} // namespace
Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() : last_seq_num_(0) {}
Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
// static
bool Aes128Gcm12Encrypter::IsSupported() { return true; }
bool Aes128Gcm12Encrypter::SetKey(StringPiece key) {
DCHECK_EQ(key.size(), sizeof(key_));
if (key.size() != sizeof(key_)) {
return false;
}
memcpy(key_, key.data(), key.size());
// Set the cipher type and the key.
if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_,
NULL) == 0) {
return false;
}
// Set the IV (nonce) length.
if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize,
NULL) == 0) {
return false;
}
return true;
}
bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) {
DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize);
if (nonce_prefix.size() != kNoncePrefixSize) {
return false;
}
COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length);
memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
return true;
}
bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce,
StringPiece associated_data,
StringPiece plaintext,
unsigned char* output) {
if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
return false;
}
// Set the IV (nonce).
if (EVP_EncryptInit_ex(
ctx_.get(), NULL, NULL, NULL,
reinterpret_cast<const unsigned char*>(nonce.data())) == 0) {
return false;
}
// If we pass a NULL, zero-length associated data to OpenSSL then it breaks.
// Thus we only set non-empty associated data.
if (!associated_data.empty()) {
// Set the associated data. The second argument (output buffer) must be
// NULL.
int unused_len;
if (EVP_EncryptUpdate(
ctx_.get(), NULL, &unused_len,
reinterpret_cast<const unsigned char*>(associated_data.data()),
associated_data.size()) == 0) {
return false;
}
}
int len;
if (EVP_EncryptUpdate(
ctx_.get(), output, &len,
reinterpret_cast<const unsigned char*>(plaintext.data()),
plaintext.size()) == 0) {
return false;
}
output += len;
if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) {
return false;
}
output += len;
if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize,
output) == 0) {
return false;
}
return true;
}
QuicData* Aes128Gcm12Encrypter::EncryptPacket(
QuicPacketSequenceNumber sequence_number,
StringPiece associated_data,
StringPiece plaintext) {
size_t ciphertext_size = GetCiphertextSize(plaintext.length());
scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
if (last_seq_num_ != 0 && sequence_number <= last_seq_num_) {
DLOG(FATAL) << "Sequence numbers regressed";
return NULL;
}
last_seq_num_ = sequence_number;
uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number));
if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
associated_data, plaintext,
reinterpret_cast<unsigned char*>(ciphertext.get()))) {
return NULL;
}
return new QuicData(ciphertext.release(), ciphertext_size, true);
}
size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; }
size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const {
return kNoncePrefixSize;
}
size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
return ciphertext_size - kAuthTagSize;
}
// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its
// corresponding plaintext.
size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const {
return plaintext_size + kAuthTagSize;
}
StringPiece Aes128Gcm12Encrypter::GetKey() const {
return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
}
StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const {
return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
kNoncePrefixSize);
}
} // namespace net