diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-18 20:19:34 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-18 20:19:34 +0000 |
commit | d008566b720e4d41fb0fdc83d2b7f3b74ce39c02 (patch) | |
tree | 816ecde55aaf3c3ec81b3a4839f6dac5fcfef53c /content/child/webcrypto/nss/aes_gcm_nss.cc | |
parent | b31dbfcc5310f4056950e874efbfc513955c47bf (diff) | |
download | chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.zip chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.tar.gz chromium_src-d008566b720e4d41fb0fdc83d2b7f3b74ce39c02.tar.bz2 |
Refactor WebCrypto code.
Split up the monstrously large platform_crypto_nss.cc, platform_crypto_openssl.cc into multiple files.
-----------
Overview:
-----------
* algorithm_implementation.h:
This defines a base class AlgorithmImplementation, which has virtual methods for synchronous encrypt/decrypt/generatekey. All of the information about an algorithm is now encapsulated by an AlgorithmImplementation. So for instance the JWK specific knowledge, key usages for each key type are pulled into this interface.
* algorithm_registry.cc:
Contains a mapping from WebCryptoAlgorithmID --> AlgorithmImplementation, stored by a singleton.
* algorithm_dispatch.cc:
Given parameters from Blink, looks up the appropriate AlgorithmImplementation in the registry and dispatches the operation. Also implements wrap/unwrap in terms of encrypt/decrypt.
* structured_clone.cc:
Contains the code related to structured cloning (which still needs some cleanup, and is implemented in terms of import/export).
* nss/*, openssl/*:
Contains the AlgorithmImplementation concrete classes for each algorithm.
This reorganization also unintentionally fixes a few bugs.
* ExportKey() for spki/pkcs8/raw uses the already serialized key data rather than re-exporting
* Some exception codes were fixed.
BUG=389325,389342,389327,374912
Review URL: https://codereview.chromium.org/379383002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284192 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/child/webcrypto/nss/aes_gcm_nss.cc')
-rw-r--r-- | content/child/webcrypto/nss/aes_gcm_nss.cc | 188 |
1 files changed, 188 insertions, 0 deletions
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 0000000..a745556 --- /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 |