Home | History | Annotate | Download | only in nss
      1 // Copyright 2014 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 "base/numerics/safe_math.h"
      6 #include "base/stl_util.h"
      7 #include "content/child/webcrypto/crypto_data.h"
      8 #include "content/child/webcrypto/nss/aes_key_nss.h"
      9 #include "content/child/webcrypto/nss/key_nss.h"
     10 #include "content/child/webcrypto/nss/util_nss.h"
     11 #include "content/child/webcrypto/status.h"
     12 #include "content/child/webcrypto/webcrypto_util.h"
     13 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
     14 
     15 // At the time of this writing:
     16 //   * Windows and Mac builds ship with their own copy of NSS (3.15+)
     17 //   * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+
     18 //     on other distros).
     19 //
     20 // Since NSS provides AES-GCM support starting in version 3.15, it may be
     21 // unavailable for Linux Chrome users.
     22 //
     23 //  * !defined(CKM_AES_GCM)
     24 //
     25 //      This means that at build time, the NSS header pkcs11t.h is older than
     26 //      3.15. However at runtime support may be present.
     27 //
     28 // TODO(eroman): Simplify this once 3.15+ is required by Linux builds.
     29 #if !defined(CKM_AES_GCM)
     30 #define CKM_AES_GCM 0x00001087
     31 
     32 struct CK_GCM_PARAMS {
     33   CK_BYTE_PTR pIv;
     34   CK_ULONG ulIvLen;
     35   CK_BYTE_PTR pAAD;
     36   CK_ULONG ulAADLen;
     37   CK_ULONG ulTagBits;
     38 };
     39 #endif  // !defined(CKM_AES_GCM)
     40 
     41 namespace content {
     42 
     43 namespace webcrypto {
     44 
     45 namespace {
     46 
     47 Status NssSupportsAesGcm() {
     48   if (NssRuntimeSupport::Get()->IsAesGcmSupported())
     49     return Status::Success();
     50   return Status::ErrorUnsupported(
     51       "NSS version doesn't support AES-GCM. Try using version 3.15 or later");
     52 }
     53 
     54 // Helper to either encrypt or decrypt for AES-GCM. The result of encryption is
     55 // the concatenation of the ciphertext and the authentication tag. Similarly,
     56 // this is the expectation for the input to decryption.
     57 Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode,
     58                             const blink::WebCryptoAlgorithm& algorithm,
     59                             const blink::WebCryptoKey& key,
     60                             const CryptoData& data,
     61                             std::vector<uint8_t>* buffer) {
     62   Status status = NssSupportsAesGcm();
     63   if (status.IsError())
     64     return status;
     65 
     66   PK11SymKey* sym_key = SymKeyNss::Cast(key)->key();
     67   const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams();
     68   if (!params)
     69     return Status::ErrorUnexpected();
     70 
     71   unsigned int tag_length_bits;
     72   status = GetAesGcmTagLengthInBits(params, &tag_length_bits);
     73   if (status.IsError())
     74     return status;
     75   unsigned int tag_length_bytes = tag_length_bits / 8;
     76 
     77   CryptoData iv(params->iv());
     78   CryptoData additional_data(params->optionalAdditionalData());
     79 
     80   CK_GCM_PARAMS gcm_params = {0};
     81   gcm_params.pIv = const_cast<unsigned char*>(iv.bytes());
     82   gcm_params.ulIvLen = iv.byte_length();
     83 
     84   gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes());
     85   gcm_params.ulAADLen = additional_data.byte_length();
     86 
     87   gcm_params.ulTagBits = tag_length_bits;
     88 
     89   SECItem param;
     90   param.type = siBuffer;
     91   param.data = reinterpret_cast<unsigned char*>(&gcm_params);
     92   param.len = sizeof(gcm_params);
     93 
     94   base::CheckedNumeric<unsigned int> buffer_size(data.byte_length());
     95 
     96   // Calculate the output buffer size.
     97   if (mode == ENCRYPT) {
     98     buffer_size += tag_length_bytes;
     99     if (!buffer_size.IsValid())
    100       return Status::ErrorDataTooLarge();
    101   }
    102 
    103   // TODO(eroman): In theory the buffer allocated for the plain text should be
    104   // sized as |data.byte_length() - tag_length_bytes|.
    105   //
    106   // However NSS has a bug whereby it will fail if the output buffer size is
    107   // not at least as large as the ciphertext:
    108   //
    109   // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674
    110   //
    111   // From the analysis of that bug it looks like it might be safe to pass a
    112   // correctly sized buffer but lie about its size. Since resizing the
    113   // WebCryptoArrayBuffer is expensive that hack may be worth looking into.
    114 
    115   buffer->resize(buffer_size.ValueOrDie());
    116   unsigned char* buffer_data = vector_as_array(buffer);
    117 
    118   PK11_EncryptDecryptFunction encrypt_or_decrypt_func =
    119       (mode == ENCRYPT) ? NssRuntimeSupport::Get()->pk11_encrypt_func()
    120                         : NssRuntimeSupport::Get()->pk11_decrypt_func();
    121 
    122   unsigned int output_len = 0;
    123   SECStatus result = encrypt_or_decrypt_func(sym_key,
    124                                              CKM_AES_GCM,
    125                                              &param,
    126                                              buffer_data,
    127                                              &output_len,
    128                                              buffer->size(),
    129                                              data.bytes(),
    130                                              data.byte_length());
    131 
    132   if (result != SECSuccess)
    133     return Status::OperationError();
    134 
    135   // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug
    136   // above).
    137   buffer->resize(output_len);
    138 
    139   return Status::Success();
    140 }
    141 
    142 class AesGcmImplementation : public AesAlgorithm {
    143  public:
    144   AesGcmImplementation() : AesAlgorithm(CKM_AES_GCM, "GCM") {}
    145 
    146   virtual Status VerifyKeyUsagesBeforeImportKey(
    147       blink::WebCryptoKeyFormat format,
    148       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
    149     // Prevent importing AES-GCM keys if it is unavailable.
    150     Status status = NssSupportsAesGcm();
    151     if (status.IsError())
    152       return status;
    153     return AesAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usage_mask);
    154   }
    155 
    156   virtual Status VerifyKeyUsagesBeforeGenerateKey(
    157       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
    158     // Prevent generating AES-GCM keys if it is unavailable.
    159     Status status = NssSupportsAesGcm();
    160     if (status.IsError())
    161       return status;
    162     return AesAlgorithm::VerifyKeyUsagesBeforeGenerateKey(usage_mask);
    163   }
    164 
    165   virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
    166                          const blink::WebCryptoKey& key,
    167                          const CryptoData& data,
    168                          std::vector<uint8_t>* buffer) const OVERRIDE {
    169     return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer);
    170   }
    171 
    172   virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
    173                          const blink::WebCryptoKey& key,
    174                          const CryptoData& data,
    175                          std::vector<uint8_t>* buffer) const OVERRIDE {
    176     return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer);
    177   }
    178 };
    179 
    180 }  // namespace
    181 
    182 AlgorithmImplementation* CreatePlatformAesGcmImplementation() {
    183   return new AesGcmImplementation;
    184 }
    185 
    186 }  // namespace webcrypto
    187 
    188 }  // namespace content
    189