| // 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 <openssl/aes.h> |
| #include <openssl/evp.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/logging.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 "crypto/scoped_openssl_types.h" |
| #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.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 NULL; |
| } |
| } |
| |
| 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 = output_max_len.ValueOrDie() % 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 |
| crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context( |
| EVP_CIPHER_CTX_new()); |
| |
| if (!context.get()) |
| return Status::OperationError(); |
| |
| const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(raw_key.size()); |
| DCHECK(cipher); |
| |
| if (!EVP_CipherInit_ex(context.get(), cipher, NULL, &raw_key[0], |
| params->iv().data(), cipher_operation)) { |
| return Status::OperationError(); |
| } |
| |
| buffer->resize(output_max_len.ValueOrDie()); |
| |
| 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 |
| |
| scoped_ptr<AlgorithmImplementation> CreateAesCbcImplementation() { |
| return make_scoped_ptr(new AesCbcImplementation); |
| } |
| |
| } // namespace webcrypto |