Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Port AES and RSA crypto to mbedtls #1119

Merged
merged 10 commits into from
Nov 2, 2022
3 changes: 3 additions & 0 deletions packager/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@
/// You can use the insertion operator to add specific logs to this.
#define NOTIMPLEMENTED() LOG(ERROR) << "NOTIMPLEMENTED: "

/// AES block size in bytes, regardless of key size.
#define AES_BLOCK_SIZE 16

#endif // PACKAGER_MACROS_H_
1 change: 1 addition & 0 deletions packager/media/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
# https://developers.google.com/open-source/licenses/bsd

# Subdirectories with their own CMakeLists.txt, all of whose targets are built.
add_subdirectory(base)
add_subdirectory(test)
98 changes: 98 additions & 0 deletions packager/media/base/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2022 Google LLC. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd

# TODO: Add widevine_protos

add_library(media_base STATIC
# TODO: finish media_base
aes_cryptor.cc
aes_decryptor.cc
aes_encryptor.cc
aes_pattern_cryptor.cc
#audio_stream_info.cc
#audio_timestamp_helper.cc
#bit_reader.cc
#bit_writer.cc
#buffer_reader.cc
#buffer_writer.cc
#byte_queue.cc
#cc_stream_filter.cc
#closure_thread.cc
#common_pssh_generator.cc
#container_names.cc
#decrypt_config.cc
#decryptor_source.cc
#http_key_fetcher.cc
#id3_tag.cc
#key_fetcher.cc
#key_source.cc
#language_utils.cc
#media_handler.cc
#media_sample.cc
#muxer.cc
#muxer_options.cc
#muxer_util.cc
#network_util.cc
#offset_byte_queue.cc
#playready_key_source.cc
#playready_pssh_generator.cc
#protection_system_specific_info.cc
#proto_json_util.cc
#pssh_generator.cc
#pssh_generator_util.cc
#raw_key_source.cc
#request_signer.cc
rsa_key.cc
#stream_info.cc
#text_muxer.cc
#text_sample.cc
#text_stream_info.cc
#text_track_config.cc
#video_stream_info.cc
#video_util.cc
#widevine_key_source.cc
#widevine_pssh_generator.cc
)

target_link_libraries(media_base
absl::base
absl::strings
glog
mbedtls)

# TODO: lib media_handler_test_base

add_executable(media_base_unittest
# TODO: finish media_base_unittest
aes_cryptor_unittest.cc
aes_pattern_cryptor_unittest.cc
#audio_timestamp_helper_unittest.cc
#bit_reader_unittest.cc
#bit_writer_unittest.cc
#buffer_writer_unittest.cc
#closure_thread_unittest.cc
#container_names_unittest.cc
#decryptor_source_unittest.cc
#http_key_fetcher_unittest.cc
#id3_tag_unittest.cc
#muxer_util_unittest.cc
#offset_byte_queue_unittest.cc
#producer_consumer_queue_unittest.cc
#protection_system_specific_info_unittest.cc
#pssh_generator_unittest.cc
#raw_key_source_unittest.cc
rsa_key_unittest.cc
test/rsa_test_data.cc
#video_util_unittest.cc
#widevine_key_source_unittest.cc
)
target_link_libraries(media_base_unittest
media_base
gmock
gtest
gtest_main
test_data_util)
add_test(NAME media_base_unittest COMMAND media_base_unittest)
85 changes: 68 additions & 17 deletions packager/media/base/aes_cryptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,11 @@

#include "packager/media/base/aes_cryptor.h"

#include <openssl/aes.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/rand.h>

#include <string>
#include <vector>

#include "packager/base/logging.h"
#include "glog/logging.h"
#include "mbedtls/entropy.h"

namespace {

Expand All @@ -30,20 +26,21 @@ namespace shaka {
namespace media {

AesCryptor::AesCryptor(ConstantIvFlag constant_iv_flag)
: aes_key_(new AES_KEY),
constant_iv_flag_(constant_iv_flag),
num_crypt_bytes_(0) {
CRYPTO_library_init();
: constant_iv_flag_(constant_iv_flag), num_crypt_bytes_(0) {
mbedtls_cipher_init(&cipher_ctx_);
}

AesCryptor::~AesCryptor() {}
AesCryptor::~AesCryptor() {
mbedtls_cipher_free(&cipher_ctx_);
}

bool AesCryptor::Crypt(const std::vector<uint8_t>& text,
std::vector<uint8_t>* crypt_text) {
// Save text size to make it work for in-place conversion, since the
// next statement will update the text size.
const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size));
// mbedtls requires an extra block's worth of output buffer available.
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
crypt_text->resize(text_size + NumPaddingBytes(text_size) + AES_BLOCK_SIZE);
size_t crypt_text_size = crypt_text->size();
if (!Crypt(text.data(), text_size, crypt_text->data(), &crypt_text_size)) {
return false;
Expand All @@ -57,7 +54,8 @@ bool AesCryptor::Crypt(const std::string& text, std::string* crypt_text) {
// Save text size to make it work for in-place conversion, since the
// next statement will update the text size.
const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size));
// mbedtls requires an extra block's worth of output buffer available.
crypt_text->resize(text_size + NumPaddingBytes(text_size) + AES_BLOCK_SIZE);
size_t crypt_text_size = crypt_text->size();
if (!Crypt(reinterpret_cast<const uint8_t*>(text.data()), text_size,
reinterpret_cast<uint8_t*>(&(*crypt_text)[0]), &crypt_text_size))
Expand Down Expand Up @@ -98,7 +96,7 @@ void AesCryptor::UpdateIv() {
increment = (num_crypt_bytes_ + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE;
}

for (int i = iv_.size() - 1; increment > 0 && i >= 0; --i) {
for (int64_t i = iv_.size() - 1; increment > 0 && i >= 0; --i) {
increment += iv_[i];
iv_[i] = increment & 0xFF;
increment >>= 8;
Expand All @@ -118,18 +116,71 @@ bool AesCryptor::GenerateRandomIv(FourCC protection_scheme,
? 8
: 16;
iv->resize(iv_size);
if (RAND_bytes(iv->data(), iv_size) != 1) {
LOG(ERROR) << "RAND_bytes failed with error: "
<< ERR_error_string(ERR_get_error(), NULL);

mbedtls_entropy_context entropy_ctx;
mbedtls_entropy_init(&entropy_ctx);
int rv = mbedtls_entropy_func(&entropy_ctx, iv->data(), iv_size);
mbedtls_entropy_free(&entropy_ctx);

if (rv != 0) {
LOG(ERROR) << "mbedtls_entropy_func failed with: " << rv;
return false;
}
return true;
}

size_t AesCryptor::NumPaddingBytes(size_t size) const {
// No padding by default.
UNUSED(size);
return 0;
}

bool AesCryptor::SetupCipher(size_t key_size, CipherMode mode) {
mbedtls_cipher_type_t type;

// AES defines three key sizes: 128, 192 and 256 bits.
// NOTE: Because we use ECB mode in the CTR cryptors, this returns ECB
// instead of CTR. Counters and block offsets are managed internally.
switch (key_size) {
case 16:
type = mode == kCtrMode ? MBEDTLS_CIPHER_AES_128_ECB
: MBEDTLS_CIPHER_AES_128_CBC;
break;
case 24:
type = mode == kCtrMode ? MBEDTLS_CIPHER_AES_192_ECB
: MBEDTLS_CIPHER_AES_192_CBC;
break;
case 32:
type = mode == kCtrMode ? MBEDTLS_CIPHER_AES_256_ECB
: MBEDTLS_CIPHER_AES_256_CBC;
break;
default:
LOG(ERROR) << "Invalid AES key size: " << key_size;
return false;
}

const mbedtls_cipher_info_t* cipher_info =
mbedtls_cipher_info_from_type(type);
CHECK(cipher_info);

if (mbedtls_cipher_setup(&cipher_ctx_, cipher_info) != 0) {
LOG(ERROR) << "Cipher setup failed";
return false;
}

// Padding mode only applies to CBC.
if (mode == kCbcMode) {
// We handle padding ourselves. Don't let mbedtls mess with it.
mbedtls_cipher_padding_t cipher_padding = MBEDTLS_PADDING_NONE;

if (mbedtls_cipher_set_padding_mode(&cipher_ctx_, cipher_padding) != 0) {
LOG(ERROR) << "Failed to set CBC padding mode";
return false;
}
}

return true;
}

} // namespace media
} // namespace shaka
20 changes: 11 additions & 9 deletions packager/media/base/aes_cryptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
#include <string>
#include <vector>

#include "packager/base/macros.h"
#include "mbedtls/cipher.h"
#include "packager/macros.h"
#include "packager/media/base/fourccs.h"

struct aes_key_st;
typedef struct aes_key_st AES_KEY;

namespace shaka {
namespace media {

Expand Down Expand Up @@ -92,8 +90,15 @@ class AesCryptor {
std::vector<uint8_t>* iv);

protected:
const AES_KEY* aes_key() const { return aes_key_.get(); }
AES_KEY* mutable_aes_key() { return aes_key_.get(); }
enum CipherMode {
kCtrMode,
kCbcMode,
};

// mbedTLS cipher context.
mbedtls_cipher_context_t cipher_ctx_;

bool SetupCipher(size_t key_size, CipherMode mode);

private:
// Internal implementation of crypt function.
Expand All @@ -119,9 +124,6 @@ class AesCryptor {
// Note: No paddings should be needed except for pkcs5-cbc encryptor.
virtual size_t NumPaddingBytes(size_t size) const;

// Openssl AES_KEY.
std::unique_ptr<AES_KEY> aes_key_;

// Indicates whether a constant iv is used. Internal iv will be reset to
// |iv_| before calling Crypt if that is the case.
const ConstantIvFlag constant_iv_flag_;
Expand Down
Loading