// 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 #include #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() {} 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(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(associated_data.data()), associated_data.size()) == 0) { return false; } } int len; if (EVP_EncryptUpdate( ctx_.get(), output, &len, reinterpret_cast(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 ciphertext(new char[ciphertext_size]); // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the // same sequence number twice. 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(nonce), sizeof(nonce)), associated_data, plaintext, reinterpret_cast(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(key_), sizeof(key_)); } StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { return StringPiece(reinterpret_cast(nonce_prefix_), kNoncePrefixSize); } } // namespace net