|  |  | 
|  | // 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 <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/numerics/safe_math.h" | 
|  | #include "components/webcrypto/algorithms/aes.h" | 
|  | #include "components/webcrypto/algorithms/util.h" | 
|  | #include "components/webcrypto/blink_key_handle.h" | 
|  | #include "components/webcrypto/crypto_data.h" | 
|  | #include "components/webcrypto/status.h" | 
|  | #include "crypto/openssl_util.h" | 
|  | #include "third_party/blink/public/platform/web_crypto_algorithm_params.h" | 
|  | #include "third_party/boringssl/src/include/openssl/aes.h" | 
|  | #include "third_party/boringssl/src/include/openssl/cipher.h" | 
|  |  | 
|  | namespace webcrypto { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const EVP_CIPHER* GetAESCipherByKeyLength(size_t key_length_bytes) { | 
|  | // 192-bit AES is intentionally unsupported (http://crbug.com/533699). | 
|  | switch (key_length_bytes) { | 
|  | case 16: | 
|  | return EVP_aes_128_cbc(); | 
|  | case 32: | 
|  | return EVP_aes_256_cbc(); | 
|  | default: | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation, | 
|  | const blink::WebCryptoAlgorithm& algorithm, | 
|  | const blink::WebCryptoKey& key, | 
|  | const CryptoData& data, | 
|  | std::vector<uint8_t>* buffer) { | 
|  | crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | 
|  |  | 
|  | const blink::WebCryptoAesCbcParams* params = algorithm.AesCbcParams(); | 
|  | const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key); | 
|  |  | 
|  | if (params->Iv().size() != 16) | 
|  | return Status::ErrorIncorrectSizeAesCbcIv(); | 
|  |  | 
|  | // According to the openssl docs, the amount of data written may be as large | 
|  | // as (data_size + cipher_block_size - 1), constrained to a multiple of | 
|  | // cipher_block_size. | 
|  | base::CheckedNumeric<int> output_max_len = data.byte_length(); | 
|  | output_max_len += AES_BLOCK_SIZE - 1; | 
|  | if (!output_max_len.IsValid()) | 
|  | return Status::ErrorDataTooLarge(); | 
|  |  | 
|  | const unsigned remainder = | 
|  | base::ValueOrDieForType<unsigned>(output_max_len % AES_BLOCK_SIZE); | 
|  | if (remainder != 0) | 
|  | output_max_len += AES_BLOCK_SIZE - remainder; | 
|  | if (!output_max_len.IsValid()) | 
|  | return Status::ErrorDataTooLarge(); | 
|  |  | 
|  | // Note: PKCS padding is enabled by default | 
|  | const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(raw_key.size()); | 
|  | DCHECK(cipher); | 
|  |  | 
|  | bssl::ScopedEVP_CIPHER_CTX context; | 
|  | if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, &raw_key[0], | 
|  | params->Iv().Data(), cipher_operation)) { | 
|  | return Status::OperationError(); | 
|  | } | 
|  |  | 
|  | buffer->resize(base::ValueOrDieForType<size_t>(output_max_len)); | 
|  |  | 
|  | int output_len = 0; | 
|  | if (!EVP_CipherUpdate(context.get(), buffer->data(), &output_len, | 
|  | data.bytes(), data.byte_length())) { | 
|  | return Status::OperationError(); | 
|  | } | 
|  | int final_output_chunk_len = 0; | 
|  | if (!EVP_CipherFinal_ex(context.get(), buffer->data() + output_len, | 
|  | &final_output_chunk_len)) { | 
|  | return Status::OperationError(); | 
|  | } | 
|  |  | 
|  | const unsigned int final_output_len = | 
|  | static_cast<unsigned int>(output_len) + | 
|  | static_cast<unsigned int>(final_output_chunk_len); | 
|  |  | 
|  | buffer->resize(final_output_len); | 
|  |  | 
|  | return Status::Success(); | 
|  | } | 
|  |  | 
|  | class AesCbcImplementation : public AesAlgorithm { | 
|  | public: | 
|  | AesCbcImplementation() : AesAlgorithm("CBC") {} | 
|  |  | 
|  | Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, | 
|  | const blink::WebCryptoKey& key, | 
|  | const CryptoData& data, | 
|  | std::vector<uint8_t>* buffer) const override { | 
|  | return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer); | 
|  | } | 
|  |  | 
|  | Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, | 
|  | const blink::WebCryptoKey& key, | 
|  | const CryptoData& data, | 
|  | std::vector<uint8_t>* buffer) const override { | 
|  | return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer); | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | std::unique_ptr<AlgorithmImplementation> CreateAesCbcImplementation() { | 
|  | return base::WrapUnique(new AesCbcImplementation); | 
|  | } | 
|  |  | 
|  | }  // namespace webcrypto |