Home | History | Annotate | Download | only in crypto
      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/aes_128_gcm_12_encrypter.h"
      6 
      7 #include <pk11pub.h>
      8 #include <secerr.h>
      9 
     10 #include "base/lazy_instance.h"
     11 #include "crypto/ghash.h"
     12 #include "crypto/scoped_nss_types.h"
     13 
     14 #if defined(USE_NSS)
     15 #include <dlfcn.h>
     16 #endif
     17 
     18 using base::StringPiece;
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 const size_t kKeySize = 16;
     25 const size_t kNoncePrefixSize = 4;
     26 
     27 // On Linux, dynamically link against the system version of libnss3.so. In
     28 // order to continue working on systems without up-to-date versions of NSS,
     29 // lookup PK11_Encrypt with dlsym.
     30 
     31 // GcmSupportChecker is a singleton which caches the results of runtime symbol
     32 // resolution of PK11_Encrypt.
     33 class GcmSupportChecker {
     34  public:
     35   static PK11_EncryptFunction pk11_encrypt_func() {
     36     return pk11_encrypt_func_;
     37   }
     38 
     39  private:
     40   friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
     41 
     42   GcmSupportChecker() {
     43 #if !defined(USE_NSS)
     44     // Using a bundled version of NSS that is guaranteed to have this symbol.
     45     pk11_encrypt_func_ = PK11_Encrypt;
     46 #else
     47     // Using system NSS libraries and PCKS #11 modules, which may not have the
     48     // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM).
     49 
     50     // If PK11_Encrypt() was successfully resolved, then NSS will support
     51     // AES-GCM directly. This was introduced in NSS 3.15.
     52     pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT,
     53                                                      "PK11_Encrypt");
     54 #endif
     55   }
     56 
     57   // |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt.
     58   static PK11_EncryptFunction pk11_encrypt_func_;
     59 };
     60 
     61 // static
     62 PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = NULL;
     63 
     64 base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
     65     LAZY_INSTANCE_INITIALIZER;
     66 
     67 // Calls PK11_Encrypt if it's available.  Otherwise, emulates CKM_AES_GCM using
     68 // CKM_AES_CTR and the GaloisHash class.
     69 SECStatus My_Encrypt(PK11SymKey* key,
     70                      CK_MECHANISM_TYPE mechanism,
     71                      SECItem* param,
     72                      unsigned char* out,
     73                      unsigned int* out_len,
     74                      unsigned int max_len,
     75                      const unsigned char* data,
     76                      unsigned int data_len) {
     77   // If PK11_Encrypt() was successfully resolved or if bundled version of NSS is
     78   // being used, then NSS will support AES-GCM directly.
     79   PK11_EncryptFunction pk11_encrypt_func =
     80       GcmSupportChecker::pk11_encrypt_func();
     81   if (pk11_encrypt_func != NULL) {
     82     return pk11_encrypt_func(key, mechanism, param, out, out_len, max_len, data,
     83                              data_len);
     84   }
     85 
     86   // Otherwise, the user has an older version of NSS. Regrettably, NSS 3.14.x
     87   // has a bug in the AES GCM code
     88   // (https://bugzilla.mozilla.org/show_bug.cgi?id=853285), as well as missing
     89   // the PK11_Encrypt function
     90   // (https://bugzilla.mozilla.org/show_bug.cgi?id=854063), both of which are
     91   // resolved in NSS 3.15.
     92 
     93   DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM));
     94   DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS));
     95 
     96   if (max_len < static_cast<unsigned int>(Aes128Gcm12Encrypter::kAuthTagSize)) {
     97     DVLOG(1) << "max_len is less than kAuthTagSize";
     98     PORT_SetError(SEC_ERROR_OUTPUT_LEN);
     99     return SECFailure;
    100   }
    101 
    102   const CK_GCM_PARAMS* gcm_params =
    103       reinterpret_cast<CK_GCM_PARAMS*>(param->data);
    104 
    105   DCHECK_EQ(gcm_params->ulTagBits,
    106             static_cast<CK_ULONG>(Aes128Gcm12Encrypter::kAuthTagSize * 8));
    107   if (gcm_params->ulIvLen != 12u) {
    108     DVLOG(1) << "ulIvLen is not equal to 12";
    109     PORT_SetError(SEC_ERROR_INPUT_LEN);
    110     return SECFailure;
    111   }
    112 
    113   SECItem my_param = { siBuffer, NULL, 0 };
    114 
    115   // Step 1. Let H = CIPH_K(128 '0' bits).
    116   unsigned char ghash_key[16] = {0};
    117   crypto::ScopedPK11Context ctx(PK11_CreateContextBySymKey(
    118       CKM_AES_ECB, CKA_ENCRYPT, key, &my_param));
    119   if (!ctx) {
    120     DVLOG(1) << "PK11_CreateContextBySymKey failed";
    121     return SECFailure;
    122   }
    123   int output_len;
    124   if (PK11_CipherOp(ctx.get(), ghash_key, &output_len, sizeof(ghash_key),
    125                     ghash_key, sizeof(ghash_key)) != SECSuccess) {
    126     DVLOG(1) << "PK11_CipherOp failed";
    127     return SECFailure;
    128   }
    129 
    130   PK11_Finalize(ctx.get());
    131 
    132   if (output_len != sizeof(ghash_key)) {
    133     DVLOG(1) << "Wrong output length";
    134     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    135     return SECFailure;
    136   }
    137 
    138   // Step 2. If len(IV)=96, then let J0 = IV || 31 '0' bits || 1.
    139   CK_AES_CTR_PARAMS ctr_params = {0};
    140   ctr_params.ulCounterBits = 32;
    141   memcpy(ctr_params.cb, gcm_params->pIv, gcm_params->ulIvLen);
    142   ctr_params.cb[12] = 0;
    143   ctr_params.cb[13] = 0;
    144   ctr_params.cb[14] = 0;
    145   ctr_params.cb[15] = 1;
    146 
    147   my_param.type = siBuffer;
    148   my_param.data = reinterpret_cast<unsigned char*>(&ctr_params);
    149   my_param.len = sizeof(ctr_params);
    150 
    151   ctx.reset(PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, key,
    152                                        &my_param));
    153   if (!ctx) {
    154     DVLOG(1) << "PK11_CreateContextBySymKey failed";
    155     return SECFailure;
    156   }
    157 
    158   // Step 6. Calculate the encryption mask of GCTR_K(J0, ...).
    159   unsigned char tag_mask[16] = {0};
    160   if (PK11_CipherOp(ctx.get(), tag_mask, &output_len, sizeof(tag_mask),
    161                     tag_mask, sizeof(tag_mask)) != SECSuccess) {
    162     DVLOG(1) << "PK11_CipherOp failed";
    163     return SECFailure;
    164   }
    165   if (output_len != sizeof(tag_mask)) {
    166     DVLOG(1) << "Wrong output length";
    167     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    168     return SECFailure;
    169   }
    170 
    171   // The const_cast for |data| can be removed if system NSS libraries are
    172   // NSS 3.14.1 or later (NSS bug
    173   // https://bugzilla.mozilla.org/show_bug.cgi?id=808218).
    174   if (PK11_CipherOp(ctx.get(), out, &output_len, max_len,
    175                     const_cast<unsigned char*>(data), data_len) != SECSuccess) {
    176     DVLOG(1) << "PK11_CipherOp failed";
    177     return SECFailure;
    178   }
    179 
    180   PK11_Finalize(ctx.get());
    181 
    182   if (static_cast<unsigned int>(output_len) != data_len) {
    183     DVLOG(1) << "Wrong output length";
    184     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    185     return SECFailure;
    186   }
    187 
    188   if ((max_len - Aes128Gcm12Encrypter::kAuthTagSize) <
    189       static_cast<unsigned int>(output_len)) {
    190     DVLOG(1) << "(max_len - kAuthTagSize) is less than output_len";
    191     PORT_SetError(SEC_ERROR_OUTPUT_LEN);
    192     return SECFailure;
    193   }
    194 
    195   crypto::GaloisHash ghash(ghash_key);
    196   ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen);
    197   ghash.UpdateCiphertext(out, output_len);
    198   ghash.Finish(out + output_len, Aes128Gcm12Encrypter::kAuthTagSize);
    199   for (unsigned int i = 0; i < Aes128Gcm12Encrypter::kAuthTagSize; i++) {
    200     out[output_len + i] ^= tag_mask[i];
    201   }
    202 
    203   *out_len = output_len + Aes128Gcm12Encrypter::kAuthTagSize;
    204   return SECSuccess;
    205 }
    206 
    207 }  // namespace
    208 
    209 Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
    210     : AeadBaseEncrypter(CKM_AES_GCM, My_Encrypt, kKeySize, kAuthTagSize,
    211                         kNoncePrefixSize) {
    212   COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
    213   COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
    214                  nonce_prefix_size_too_big);
    215   ignore_result(g_gcm_support_checker.Get());
    216 }
    217 
    218 Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
    219 
    220 void Aes128Gcm12Encrypter::FillAeadParams(StringPiece nonce,
    221                                           StringPiece associated_data,
    222                                           size_t auth_tag_size,
    223                                           AeadParams* aead_params) const {
    224   aead_params->len = sizeof(aead_params->data.gcm_params);
    225   CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
    226   gcm_params->pIv =
    227       reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
    228   gcm_params->ulIvLen = nonce.size();
    229   gcm_params->pAAD =
    230       reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
    231   gcm_params->ulAADLen = associated_data.size();
    232   gcm_params->ulTagBits = auth_tag_size * 8;
    233 }
    234 
    235 }  // namespace net
    236