Skip to content

Commit

Permalink
Add ChaCha20Poly1305Encrypter, based on
Browse files Browse the repository at this point in the history
draft-agl-tls-chacha20poly1305-04 and identified by the tag CC12.
        
Add the base class AeadBaseEncrypter for Aes128Gcm12Encrypter and
ChaCha20Poly1305Encrypter. Both subclasses can be defined by
configuring the base class with four parameters.

Merge internal CL: 62224774

Add ChaCha20Poly1305Decrypter, based on
draft-agl-tls-chacha20poly1305-04 and identified by the tag CC12.
This continues the previous CL that added ChaCha20Poly1305Encrypter.

Add the base class AeadBaseDecrypter for Aes128Gcm12Decrypter and
ChaCha20Poly1305Decrypter. Both subclasses can be defined by
configuring the base class with four parameters.

Merge internal CL: 62637549

Note: The chacha20_poly1305_*_nss.cc files will be implemented in
a follow-up CL. Six files created with "svn copy" were added
separately in https://codereview.chromium.org/196743005/ (r256650).

R=agl@chromium.org,rtenneti@chromium.org
BUG=none
TEST=net_unittests

Review URL: https://codereview.chromium.org/189893002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256837 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
wtc@chromium.org committed Mar 13, 2014
1 parent 438c69e commit 37e7e47
Show file tree
Hide file tree
Showing 20 changed files with 671 additions and 670 deletions.
22 changes: 22 additions & 0 deletions net/net.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,12 @@
'quic/congestion_control/tcp_receiver.h',
'quic/congestion_control/time_loss_algorithm.cc',
'quic/congestion_control/time_loss_algorithm.h',
'quic/crypto/aead_base_decrypter.h',
'quic/crypto/aead_base_decrypter_nss.cc',
'quic/crypto/aead_base_decrypter_openssl.cc',
'quic/crypto/aead_base_encrypter.h',
'quic/crypto/aead_base_encrypter_nss.cc',
'quic/crypto/aead_base_encrypter_openssl.cc',
'quic/crypto/aes_128_gcm_12_decrypter.h',
'quic/crypto/aes_128_gcm_12_decrypter_nss.cc',
'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
Expand All @@ -794,6 +800,12 @@
'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc',
'quic/crypto/cert_compressor.cc',
'quic/crypto/cert_compressor.h',
'quic/crypto/chacha20_poly1305_decrypter.h',
'quic/crypto/chacha20_poly1305_decrypter_nss.cc',
'quic/crypto/chacha20_poly1305_decrypter_openssl.cc',
'quic/crypto/chacha20_poly1305_encrypter.h',
'quic/crypto/chacha20_poly1305_encrypter_nss.cc',
'quic/crypto/chacha20_poly1305_encrypter_openssl.cc',
'quic/crypto/channel_id.cc',
'quic/crypto/channel_id.h',
'quic/crypto/channel_id_nss.cc',
Expand Down Expand Up @@ -1374,8 +1386,12 @@
'cert/x509_util_nss.h',
'ocsp/nss_ocsp.cc',
'ocsp/nss_ocsp.h',
'quic/crypto/aead_base_decrypter_nss.cc',
'quic/crypto/aead_base_encrypter_nss.cc',
'quic/crypto/aes_128_gcm_12_decrypter_nss.cc',
'quic/crypto/aes_128_gcm_12_encrypter_nss.cc',
'quic/crypto/chacha20_poly1305_decrypter_nss.cc',
'quic/crypto/chacha20_poly1305_encrypter_nss.cc',
'quic/crypto/channel_id_nss.cc',
'quic/crypto/p256_key_exchange_nss.cc',
'socket/nss_ssl_util.cc',
Expand Down Expand Up @@ -1409,8 +1425,12 @@
'cert/x509_certificate_openssl.cc',
'cert/x509_util_openssl.cc',
'cert/x509_util_openssl.h',
'quic/crypto/aead_base_decrypter_openssl.cc',
'quic/crypto/aead_base_encrypter_openssl.cc',
'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
'quic/crypto/aes_128_gcm_12_encrypter_openssl.cc',
'quic/crypto/chacha20_poly1305_decrypter_openssl.cc',
'quic/crypto/chacha20_poly1305_encrypter_openssl.cc',
'quic/crypto/channel_id_openssl.cc',
'quic/crypto/p256_key_exchange_openssl.cc',
'quic/crypto/scoped_evp_aead_ctx.cc',
Expand Down Expand Up @@ -1866,6 +1886,8 @@
'quic/crypto/aes_128_gcm_12_decrypter_test.cc',
'quic/crypto/aes_128_gcm_12_encrypter_test.cc',
'quic/crypto/cert_compressor_test.cc',
'quic/crypto/chacha20_poly1305_decrypter_test.cc',
'quic/crypto/chacha20_poly1305_encrypter_test.cc',
'quic/crypto/channel_id_test.cc',
'quic/crypto/common_cert_set_test.cc',
'quic/crypto/crypto_framer_test.cc',
Expand Down
47 changes: 9 additions & 38 deletions net/quic/crypto/aes_128_gcm_12_decrypter.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,17 @@
#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_

#include <string>

#include "base/compiler_specific.h"
#include "net/quic/crypto/quic_decrypter.h"

#if defined(USE_OPENSSL)
#include "net/quic/crypto/scoped_evp_aead_ctx.h"
#endif
#include "net/quic/crypto/aead_base_decrypter.h"

namespace net {

namespace test {
class Aes128Gcm12DecrypterPeer;
} // namespace test

// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
// calling QuicDecrypter::Create(kAESG).
//
// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
// of the nonce is four bytes.
class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter {
public:
enum {
// Authentication tags are truncated to 96 bits.
Expand All @@ -36,31 +25,13 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter {
Aes128Gcm12Decrypter();
virtual ~Aes128Gcm12Decrypter();

// Returns true if the underlying crypto library supports AES GCM.
static bool IsSupported();

// QuicDecrypter implementation
virtual bool SetKey(base::StringPiece key) OVERRIDE;
virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
virtual bool Decrypt(base::StringPiece nonce,
base::StringPiece associated_data,
base::StringPiece ciphertext,
unsigned char* output,
size_t* output_length) OVERRIDE;
virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
base::StringPiece associated_data,
base::StringPiece ciphertext) OVERRIDE;
virtual base::StringPiece GetKey() const OVERRIDE;
virtual base::StringPiece GetNoncePrefix() const OVERRIDE;

private:
// The 128-bit AES key.
unsigned char key_[16];
// The nonce prefix.
unsigned char nonce_prefix_[4];

#if defined(USE_OPENSSL)
ScopedEVPAEADCtx ctx_;
#if !defined(USE_OPENSSL)
protected:
// AeadBaseDecrypter methods:
virtual void FillAeadParams(base::StringPiece nonce,
base::StringPiece associated_data,
size_t auth_tag_size,
AeadParams* aead_params) const OVERRIDE;
#endif
};

Expand Down
185 changes: 19 additions & 166 deletions net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"

#include <nss.h>
#include <pk11pub.h>
#include <secerr.h>

#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
#include "crypto/ghash.h"
#include "crypto/scoped_nss_types.h"

Expand All @@ -23,31 +21,8 @@ namespace net {

namespace {

// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR
// and GCM types, so define them here.
#if !defined(CKM_AES_CTR)
#define CKM_AES_CTR 0x00001086
#define CKM_AES_GCM 0x00001087

struct CK_AES_CTR_PARAMS {
CK_ULONG ulCounterBits;
CK_BYTE cb[16];
};

struct CK_GCM_PARAMS {
CK_BYTE_PTR pIv;
CK_ULONG ulIvLen;
CK_BYTE_PTR pAAD;
CK_ULONG ulAADLen;
CK_ULONG ulTagBits;
};
#endif // CKM_AES_CTR

typedef SECStatus
(*PK11_DecryptFunction)(
PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
unsigned char* out, unsigned int* outLen, unsigned int maxLen,
const unsigned char* enc, unsigned encLen);
const size_t kKeySize = 16;
const size_t kNoncePrefixSize = 4;

// On Linux, dynamically link against the system version of libnss3.so. In
// order to continue working on systems without up-to-date versions of NSS,
Expand All @@ -61,10 +36,6 @@ class GcmSupportChecker {
return pk11_decrypt_func_;
}

static CK_MECHANISM_TYPE aes_key_mechanism() {
return aes_key_mechanism_;
}

private:
friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;

Expand All @@ -80,33 +51,19 @@ class GcmSupportChecker {
// AES-GCM directly. This was introduced in NSS 3.15.
pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT,
"PK11_Decrypt");
if (pk11_decrypt_func_ == NULL) {
aes_key_mechanism_ = CKM_AES_ECB;
}
#endif
}

// |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt.
static PK11_DecryptFunction pk11_decrypt_func_;

// The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of
// NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in
// NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15.
static CK_MECHANISM_TYPE aes_key_mechanism_;
};

// static
PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = NULL;

// static
CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM;

base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
LAZY_INSTANCE_INITIALIZER;

const size_t kNoncePrefixSize = 4;
const size_t kAESNonceSize = 12;

// Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using
// CKM_AES_CTR and the GaloisHash class.
SECStatus My_Decrypt(PK11SymKey* key,
Expand Down Expand Up @@ -250,134 +207,30 @@ SECStatus My_Decrypt(PK11SymKey* key,

} // namespace

Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {
Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
: AeadBaseDecrypter(CKM_AES_GCM, My_Decrypt, kKeySize, kAuthTagSize,
kNoncePrefixSize) {
COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
nonce_prefix_size_too_big);
ignore_result(g_gcm_support_checker.Get());
}

Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}

// static
bool Aes128Gcm12Decrypter::IsSupported() {
// NSS 3.15 supports CKM_AES_GCM directly.
// NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM.
// Versions earlier than NSS 3.14 are not supported.
return NSS_VersionCheck("3.14") != PR_FALSE;
}

bool Aes128Gcm12Decrypter::SetKey(StringPiece key) {
DCHECK_EQ(key.size(), sizeof(key_));
if (key.size() != sizeof(key_)) {
return false;
}
memcpy(key_, key.data(), key.size());
return true;
}

bool Aes128Gcm12Decrypter::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 Aes128Gcm12Decrypter::Decrypt(StringPiece nonce,
StringPiece associated_data,
StringPiece ciphertext,
uint8* output,
size_t* output_length) {
if (ciphertext.length() < kAuthTagSize ||
nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) {
return false;
}
// NSS 3.14.x incorrectly requires an output buffer at least as long as
// the ciphertext (NSS bug
// https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
// QuicDecrypter::Decrypt() specifies that |output| must be as long as
// |ciphertext| on entry.
size_t plaintext_size = ciphertext.length() - kAuthTagSize;

// Import key_ into NSS.
SECItem key_item;
key_item.type = siBuffer;
key_item.data = key_;
key_item.len = sizeof(key_);
PK11SlotInfo* slot = PK11_GetInternalSlot();
// The exact value of the |origin| argument doesn't matter to NSS as long as
// it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
// placeholder.
crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey(
slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap,
CKA_DECRYPT, &key_item, NULL));
PK11_FreeSlot(slot);
slot = NULL;
if (!aes_key) {
DVLOG(1) << "PK11_ImportSymKey failed";
return false;
}

CK_GCM_PARAMS gcm_params = {0};
gcm_params.pIv =
void Aes128Gcm12Decrypter::FillAeadParams(StringPiece nonce,
StringPiece associated_data,
size_t auth_tag_size,
AeadParams* aead_params) const {
aead_params->len = sizeof(aead_params->data.gcm_params);
CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
gcm_params->pIv =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
gcm_params.ulIvLen = nonce.size();
gcm_params.pAAD =
gcm_params->ulIvLen = nonce.size();
gcm_params->pAAD =
reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
gcm_params.ulAADLen = associated_data.size();
gcm_params.ulTagBits = kAuthTagSize * 8;

SECItem param;
param.type = siBuffer;
param.data = reinterpret_cast<unsigned char*>(&gcm_params);
param.len = sizeof(gcm_params);

unsigned int output_len;
if (My_Decrypt(aes_key.get(), CKM_AES_GCM, &param,
output, &output_len, ciphertext.length(),
reinterpret_cast<const unsigned char*>(ciphertext.data()),
ciphertext.length()) != SECSuccess) {
return false;
}

if (output_len != plaintext_size) {
DVLOG(1) << "Wrong output length";
return false;
}
*output_length = output_len;
return true;
}

QuicData* Aes128Gcm12Decrypter::DecryptPacket(
QuicPacketSequenceNumber sequence_number,
StringPiece associated_data,
StringPiece ciphertext) {
if (ciphertext.length() < kAuthTagSize) {
return NULL;
}
size_t plaintext_size;
scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);

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 (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)),
associated_data, ciphertext,
reinterpret_cast<uint8*>(plaintext.get()),
&plaintext_size)) {
return NULL;
}
return new QuicData(plaintext.release(), plaintext_size, true);
}

StringPiece Aes128Gcm12Decrypter::GetKey() const {
return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_));
}

StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const {
return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
kNoncePrefixSize);
gcm_params->ulAADLen = associated_data.size();
gcm_params->ulTagBits = auth_tag_size * 8;
}

} // namespace net
Loading

0 comments on commit 37e7e47

Please sign in to comment.