// Copyright 2018 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 "media/cdm/aes_cbc_crypto.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "crypto/openssl_util.h" #include "crypto/symmetric_key.h" #include "third_party/boringssl/src/include/openssl/aes.h" #include "third_party/boringssl/src/include/openssl/crypto.h" #include "third_party/boringssl/src/include/openssl/err.h" #include "third_party/boringssl/src/include/openssl/evp.h" // Notes on using OpenSSL: // https://www.openssl.org/docs/man1.1.0/crypto/EVP_DecryptUpdate.html // The documentation for EVP_DecryptUpdate() only states // "EVP_DecryptInit_ex(), EVP_DecryptUpdate() and EVP_DecryptFinal_ex() // are the corresponding decryption operations. EVP_DecryptFinal() will // return an error code if padding is enabled and the final block is not // correctly formatted. The parameters and restrictions are identical // to the encryption operations except that if padding is enabled ..." // As this implementation does not use padding, the last part should not be // an issue. However, there is no mention whether data can be decrypted // block-by-block or if all the data must be unencrypted at once. // // The documentation for EVP_EncryptUpdate() (same page as above) states // "EVP_EncryptUpdate() encrypts inl bytes from the buffer in and writes // the encrypted version to out. This function can be called multiple times // to encrypt successive blocks of data." // Given that the EVP_Decrypt* methods have the same restrictions, the code // below assumes that EVP_DecryptUpdate() can be called on a block-by-block // basis. A test in aes_cbc_crypto_unittest.cc verifies this. namespace media { AesCbcCrypto::AesCbcCrypto() { // Ensure the crypto library is initialized. CRYPTO_library_init may be // safely called concurrently. CRYPTO_library_init(); EVP_CIPHER_CTX_init(&ctx_); } AesCbcCrypto::~AesCbcCrypto() { EVP_CIPHER_CTX_cleanup(&ctx_); } bool AesCbcCrypto::Initialize(const crypto::SymmetricKey& key, base::span iv) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); // This uses AES-CBC-128, so the key must be 128 bits. const EVP_CIPHER* cipher = EVP_aes_128_cbc(); const uint8_t* key_data = reinterpret_cast(key.key().data()); if (key.key().length() != EVP_CIPHER_key_length(cipher)) { DVLOG(1) << "Key length is incorrect."; return false; } // |iv| must also be 128 bits. if (iv.size_bytes() != EVP_CIPHER_iv_length(cipher)) { DVLOG(1) << "IV length is incorrect."; return false; } if (!EVP_DecryptInit_ex(&ctx_, cipher, nullptr, key_data, iv.data())) { DVLOG(1) << "EVP_DecryptInit_ex() failed."; return false; } if (!EVP_CIPHER_CTX_set_padding(&ctx_, 0)) { DVLOG(1) << "EVP_CIPHER_CTX_set_padding() failed."; return false; } return true; } bool AesCbcCrypto::Decrypt(base::span encrypted_data, uint8_t* decrypted_data) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); if (encrypted_data.size_bytes() % EVP_CIPHER_CTX_block_size(&ctx_) != 0) { DVLOG(1) << "Encrypted bytes not a multiple of block size."; return false; } int out_length; if (!EVP_DecryptUpdate(&ctx_, decrypted_data, &out_length, encrypted_data.data(), encrypted_data.size_bytes())) { DVLOG(1) << "EVP_DecryptUpdate() failed."; return false; } return encrypted_data.size_bytes() == base::checked_cast(out_length); } } // namespace media