| Index: content/child/webcrypto/nss/aes_gcm_nss.cc
|
| diff --git a/content/child/webcrypto/nss/aes_gcm_nss.cc b/content/child/webcrypto/nss/aes_gcm_nss.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a7455561957cff89818392b27bd965a01bc7a9a5
|
| --- /dev/null
|
| +++ b/content/child/webcrypto/nss/aes_gcm_nss.cc
|
| @@ -0,0 +1,188 @@
|
| +// Copyright 2014 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 "content/child/webcrypto/crypto_data.h"
|
| +#include "content/child/webcrypto/nss/aes_key_nss.h"
|
| +#include "content/child/webcrypto/nss/key_nss.h"
|
| +#include "content/child/webcrypto/nss/util_nss.h"
|
| +#include "content/child/webcrypto/status.h"
|
| +#include "content/child/webcrypto/webcrypto_util.h"
|
| +#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
|
| +
|
| +// At the time of this writing:
|
| +// * Windows and Mac builds ship with their own copy of NSS (3.15+)
|
| +// * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+
|
| +// on other distros).
|
| +//
|
| +// Since NSS provides AES-GCM support starting in version 3.15, it may be
|
| +// unavailable for Linux Chrome users.
|
| +//
|
| +// * !defined(CKM_AES_GCM)
|
| +//
|
| +// This means that at build time, the NSS header pkcs11t.h is older than
|
| +// 3.15. However at runtime support may be present.
|
| +//
|
| +// TODO(eroman): Simplify this once 3.15+ is required by Linux builds.
|
| +#if !defined(CKM_AES_GCM)
|
| +#define CKM_AES_GCM 0x00001087
|
| +
|
| +struct CK_GCM_PARAMS {
|
| + CK_BYTE_PTR pIv;
|
| + CK_ULONG ulIvLen;
|
| + CK_BYTE_PTR pAAD;
|
| + CK_ULONG ulAADLen;
|
| + CK_ULONG ulTagBits;
|
| +};
|
| +#endif // !defined(CKM_AES_GCM)
|
| +
|
| +namespace content {
|
| +
|
| +namespace webcrypto {
|
| +
|
| +namespace {
|
| +
|
| +Status NssSupportsAesGcm() {
|
| + if (NssRuntimeSupport::Get()->IsAesGcmSupported())
|
| + return Status::Success();
|
| + return Status::ErrorUnsupported(
|
| + "NSS version doesn't support AES-GCM. Try using version 3.15 or later");
|
| +}
|
| +
|
| +// Helper to either encrypt or decrypt for AES-GCM. The result of encryption is
|
| +// the concatenation of the ciphertext and the authentication tag. Similarly,
|
| +// this is the expectation for the input to decryption.
|
| +Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode,
|
| + const blink::WebCryptoAlgorithm& algorithm,
|
| + const blink::WebCryptoKey& key,
|
| + const CryptoData& data,
|
| + std::vector<uint8>* buffer) {
|
| + Status status = NssSupportsAesGcm();
|
| + if (status.IsError())
|
| + return status;
|
| +
|
| + PK11SymKey* sym_key = SymKeyNss::Cast(key)->key();
|
| + const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams();
|
| + if (!params)
|
| + return Status::ErrorUnexpected();
|
| +
|
| + unsigned int tag_length_bits;
|
| + status = GetAesGcmTagLengthInBits(params, &tag_length_bits);
|
| + if (status.IsError())
|
| + return status;
|
| + unsigned int tag_length_bytes = tag_length_bits / 8;
|
| +
|
| + CryptoData iv(params->iv());
|
| + CryptoData additional_data(params->optionalAdditionalData());
|
| +
|
| + CK_GCM_PARAMS gcm_params = {0};
|
| + gcm_params.pIv = const_cast<unsigned char*>(iv.bytes());
|
| + gcm_params.ulIvLen = iv.byte_length();
|
| +
|
| + gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes());
|
| + gcm_params.ulAADLen = additional_data.byte_length();
|
| +
|
| + gcm_params.ulTagBits = tag_length_bits;
|
| +
|
| + SECItem param;
|
| + param.type = siBuffer;
|
| + param.data = reinterpret_cast<unsigned char*>(&gcm_params);
|
| + param.len = sizeof(gcm_params);
|
| +
|
| + unsigned int buffer_size = 0;
|
| +
|
| + // Calculate the output buffer size.
|
| + if (mode == ENCRYPT) {
|
| + // TODO(eroman): This is ugly, abstract away the safe integer arithmetic.
|
| + if (data.byte_length() > (UINT_MAX - tag_length_bytes))
|
| + return Status::ErrorDataTooLarge();
|
| + buffer_size = data.byte_length() + tag_length_bytes;
|
| + } else {
|
| + // TODO(eroman): In theory the buffer allocated for the plain text should be
|
| + // sized as |data.byte_length() - tag_length_bytes|.
|
| + //
|
| + // However NSS has a bug whereby it will fail if the output buffer size is
|
| + // not at least as large as the ciphertext:
|
| + //
|
| + // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674
|
| + //
|
| + // From the analysis of that bug it looks like it might be safe to pass a
|
| + // correctly sized buffer but lie about its size. Since resizing the
|
| + // WebCryptoArrayBuffer is expensive that hack may be worth looking into.
|
| + buffer_size = data.byte_length();
|
| + }
|
| +
|
| + buffer->resize(buffer_size);
|
| + unsigned char* buffer_data = Uint8VectorStart(buffer);
|
| +
|
| + PK11_EncryptDecryptFunction encrypt_or_decrypt_func =
|
| + (mode == ENCRYPT) ? NssRuntimeSupport::Get()->pk11_encrypt_func()
|
| + : NssRuntimeSupport::Get()->pk11_decrypt_func();
|
| +
|
| + unsigned int output_len = 0;
|
| + SECStatus result = encrypt_or_decrypt_func(sym_key,
|
| + CKM_AES_GCM,
|
| + ¶m,
|
| + buffer_data,
|
| + &output_len,
|
| + buffer->size(),
|
| + data.bytes(),
|
| + data.byte_length());
|
| +
|
| + if (result != SECSuccess)
|
| + return Status::OperationError();
|
| +
|
| + // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug
|
| + // above).
|
| + buffer->resize(output_len);
|
| +
|
| + return Status::Success();
|
| +}
|
| +
|
| +class AesGcmImplementation : public AesAlgorithm {
|
| + public:
|
| + AesGcmImplementation() : AesAlgorithm(CKM_AES_GCM, "GCM") {}
|
| +
|
| + virtual Status VerifyKeyUsagesBeforeImportKey(
|
| + blink::WebCryptoKeyFormat format,
|
| + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
|
| + // Prevent importing AES-GCM keys if it is unavailable.
|
| + Status status = NssSupportsAesGcm();
|
| + if (status.IsError())
|
| + return status;
|
| + return AesAlgorithm::VerifyKeyUsagesBeforeImportKey(format, usage_mask);
|
| + }
|
| +
|
| + virtual Status VerifyKeyUsagesBeforeGenerateKey(
|
| + blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
|
| + // Prevent generating AES-GCM keys if it is unavailable.
|
| + Status status = NssSupportsAesGcm();
|
| + if (status.IsError())
|
| + return status;
|
| + return AesAlgorithm::VerifyKeyUsagesBeforeGenerateKey(usage_mask);
|
| + }
|
| +
|
| + virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
|
| + const blink::WebCryptoKey& key,
|
| + const CryptoData& data,
|
| + std::vector<uint8>* buffer) const OVERRIDE {
|
| + return AesGcmEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer);
|
| + }
|
| +
|
| + virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
|
| + const blink::WebCryptoKey& key,
|
| + const CryptoData& data,
|
| + std::vector<uint8>* buffer) const OVERRIDE {
|
| + return AesGcmEncryptDecrypt(DECRYPT, algorithm, key, data, buffer);
|
| + }
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +AlgorithmImplementation* CreatePlatformAesGcmImplementation() {
|
| + return new AesGcmImplementation;
|
| +}
|
| +
|
| +} // namespace webcrypto
|
| +
|
| +} // namespace content
|
|
|