From 914e13067f0a5afc049d37955d97920aaa1ec7f5 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Wed, 16 Sep 2015 13:34:15 -0700 Subject: [PATCH] Add Support for Baked-In Certificates Port from the widevine repo of http://go/wvgerrit/15628 This change enables easy support for baked-in certificates. Platforms using this feature need only change the supports_keybox property to false, replace keys.cpp with the file provided by Google, and make sure the cert.bin provided by Google is preinstalled in the storage. To enable this, new files defining storage for the embedded private key were added to MockOEMCrypto. When supports_keybox is false, these are referenced to get the embedded private key. As this code is mostly shared with the existing test utility function that loads the test certificate, the shared code was moved to a helper. Also, the behavior of the MockOEMCrypto implementation OEMCrypto_LoadDeviceRSAKey when supports_keybox is false was changed from erroring to validating that the "wrapped private key" is actually the magic value 0xDEADBEEF. Bug: 23554998 Change-Id: I8b5d7b4d37b8ec80bb4342e441625cbc5046df89 --- libwvdrmengine/oemcrypto/mock/Android.mk | 1 + libwvdrmengine/oemcrypto/mock/src/keys.cpp | 10 ++ libwvdrmengine/oemcrypto/mock/src/keys.h | 11 ++ .../mock/src/oemcrypto_engine_mock.cpp | 115 ++++++++++-------- .../mock/src/oemcrypto_engine_mock.h | 2 + .../oemcrypto/mock/src/oemcrypto_mock.cpp | 16 ++- 6 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 libwvdrmengine/oemcrypto/mock/src/keys.cpp create mode 100644 libwvdrmengine/oemcrypto/mock/src/keys.h diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index de1378dd..e90fa063 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -3,6 +3,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + src/keys.cpp \ src/oemcrypto_engine_device_properties.cpp \ src/oemcrypto_engine_mock.cpp \ src/oemcrypto_key_mock.cpp \ diff --git a/libwvdrmengine/oemcrypto/mock/src/keys.cpp b/libwvdrmengine/oemcrypto/mock/src/keys.cpp new file mode 100644 index 00000000..380e0324 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/keys.cpp @@ -0,0 +1,10 @@ +// If you are using a baked-in certificate instead of supporting keyboxes, +// you should have received a keys.cpp from Widevine that replaces this file. +// +// If you are not using a baked-in certificate, then you may ignore this file, +// as it intentionally defines an empty key. + +#include "keys.h" + +const uint8_t kPrivateKey[] = { 0, }; +const size_t kPrivateKeySize = 0; diff --git a/libwvdrmengine/oemcrypto/mock/src/keys.h b/libwvdrmengine/oemcrypto/mock/src/keys.h new file mode 100644 index 00000000..e6c0145d --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/keys.h @@ -0,0 +1,11 @@ +// This header is used to access the baked-in certificate if one is in use. +#ifndef KEYS_H_ +#define KEYS_H_ + +#include +#include + +extern const uint8_t kPrivateKey[]; +extern const size_t kPrivateKeySize; + +#endif // KEYS_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 4bfe8ac9..f0b85fb8 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -5,6 +5,7 @@ #include "oemcrypto_engine_mock.h" #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include +#include "keys.h" #include "log.h" #include "oemcrypto_key_mock.h" #include "oemcrypto_logging.h" @@ -1165,6 +1167,11 @@ CryptoEngine::CryptoEngine() : current_session_(NULL), use_test_keybox_(false), usage_table_(new UsageTable(this)), rsa_key_(NULL) { ERR_load_crypto_strings(); + + if (!supports_keybox() && !LoadPkcs8RsaKey(kPrivateKey, kPrivateKeySize)) { + LOGE("FATAL ERROR: Platform uses a baked-in certificate instead of a " + "keybox, but the certificate could not be loaded."); + } } CryptoEngine::~CryptoEngine() { @@ -1183,59 +1190,8 @@ bool CryptoEngine::LoadTestRSAKey() { RSA_free(rsa_key_); rsa_key_ = NULL; } - uint8_t *pkcs8_rsa_key - = const_cast(kTestRSAPKCS8PrivateKeyInfo2_2048); - size_t rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); - BIO *bio = BIO_new_mem_buf(pkcs8_rsa_key, rsa_key_length); - if ( bio == NULL ) { - LOGE("[LoadTestRSAKey(): Could not allocate bio buffer]"); - return false; - } - bool success = true; - PKCS8_PRIV_KEY_INFO *pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); - if (pkcs8_pki == NULL) { - LOGE("d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL."); - success = false; - } - EVP_PKEY *evp = NULL; - if (success) { - evp = EVP_PKCS82PKEY(pkcs8_pki); - if (evp == NULL) { - LOGE("EVP_PKCS82PKEY returned NULL."); - success = false; - } - } - if (success) { - rsa_key_ = EVP_PKEY_get1_RSA(evp); - if (rsa_key_ == NULL) { - LOGE("PrivateKeyInfo did not contain an RSA key."); - success = false; - } - } - if (evp != NULL) { - EVP_PKEY_free(evp); - } - if (pkcs8_pki != NULL) { - PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); - } - BIO_free(bio); - if (!success) { - return false; - } - switch (RSA_check_key(rsa_key_)) { - case 1: // valid. - return true; - case 0: // not valid. - LOGE("[LoadTestRSAKey(): rsa key not valid]"); - dump_openssl_error(); - return false; - default: // -1 == check failed. - LOGE("[LoadTestRSAKey(): error checking rsa key]"); - dump_openssl_error(); - return false; - } - - + return LoadPkcs8RsaKey(kTestRSAPKCS8PrivateKeyInfo2_2048, + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); } SessionId CryptoEngine::CreateSession() { @@ -1268,6 +1224,59 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) { return NULL; } +bool CryptoEngine::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) { + assert(buffer != NULL); + uint8_t* pkcs8_rsa_key = const_cast(buffer); + BIO* bio = BIO_new_mem_buf(pkcs8_rsa_key, length); + if (bio == NULL) { + LOGE("[LoadPkcs8RsaKey(): Could not allocate bio buffer]"); + return false; + } + bool success = true; + PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); + if (pkcs8_pki == NULL) { + LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL]"); + success = false; + } + EVP_PKEY* evp = NULL; + if (success) { + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == NULL) { + LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned NULL]"); + success = false; + } + } + if (success) { + rsa_key_ = EVP_PKEY_get1_RSA(evp); + if (rsa_key_ == NULL) { + LOGE("[LoadPkcs8RsaKey(): PrivateKeyInfo did not contain an RSA key]"); + success = false; + } + } + if (evp != NULL) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != NULL) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + if (!success) { + return false; + } + switch (RSA_check_key(rsa_key_)) { + case 1: // valid. + return true; + case 0: // not valid. + LOGE("[LoadPkcs8RsaKey(): rsa key not valid]"); + dump_openssl_error(); + return false; + default: // -1 == check failed. + LOGE("[LoadPkcs8RsaKey(): error checking rsa key]"); + dump_openssl_error(); + return false; + } +} + // Internal utility function to decrypt the message bool SessionContext::DecryptMessage(const std::vector& key, const std::vector& iv, diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 27eeb8df..560d131c 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -261,6 +261,8 @@ class CryptoEngine { const char* security_level(); private: + bool LoadPkcs8RsaKey(const uint8_t* buffer, size_t length); + SessionContext* current_session_; ActiveSessions sessions_; WvKeybox keybox_; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 58fb0392..39105df2 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -4,7 +4,9 @@ // #include "OEMCryptoCENC.h" +#include #include +#include #include #include #include @@ -22,6 +24,10 @@ #include "string_conversions.h" #include "wv_cdm_constants.h" +namespace { +const uint8_t kBakedInCertificateMagicBytes[] = { 0xDE, 0xAD, 0xBE, 0xEF }; +} // namespace + namespace wvoec_mock { static CryptoEngine* crypto_engine = NULL; @@ -994,7 +1000,15 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (!crypto_engine->supports_keybox()) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + // If we are not using keyboxes, the "wrapped RSA key" should actually be + // the magic value for baked-in certificates. + if (wrapped_rsa_key_length != sizeof(kBakedInCertificateMagicBytes) || + memcmp(kBakedInCertificateMagicBytes, wrapped_rsa_key, + wrapped_rsa_key_length) != 0) { + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } else { + return OEMCrypto_SUCCESS; + } } const WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key);