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
This commit is contained in:
@@ -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 \
|
||||
|
||||
10
libwvdrmengine/oemcrypto/mock/src/keys.cpp
Normal file
10
libwvdrmengine/oemcrypto/mock/src/keys.cpp
Normal file
@@ -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;
|
||||
11
libwvdrmengine/oemcrypto/mock/src/keys.h
Normal file
11
libwvdrmengine/oemcrypto/mock/src/keys.h
Normal file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern const uint8_t kPrivateKey[];
|
||||
extern const size_t kPrivateKeySize;
|
||||
|
||||
#endif // KEYS_H_
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#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<uint8_t *>(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<uint8_t*>(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<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
//
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
@@ -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<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||
|
||||
Reference in New Issue
Block a user