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:
Fred Gylys-Colwell
2015-09-16 13:34:15 -07:00
parent ff6b79d945
commit 914e13067f
6 changed files with 101 additions and 54 deletions

View File

@@ -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 \

View 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;

View 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_

View File

@@ -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,

View File

@@ -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_;

View File

@@ -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);