OEMCrypto v13.1

This commit is contained in:
Fred Gylys-Colwell
2017-05-18 14:20:44 -07:00
parent 422ec255cb
commit 1959bff979
77 changed files with 31461 additions and 0 deletions

49
oemcrypto/mock/Android.mk Normal file
View File

@@ -0,0 +1,49 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
src/keys.cpp \
src/oemcrypto_auth_mock.cpp \
src/oemcrypto_engine_device_properties.cpp \
src/oemcrypto_engine_mock.cpp \
src/oemcrypto_key_mock.cpp \
src/oemcrypto_keybox_mock.cpp \
src/oemcrypto_keybox_testkey.cpp \
src/oemcrypto_logging.cpp \
src/oemcrypto_mock.cpp \
src/oemcrypto_nonce_table.cpp \
src/oemcrypto_old_usage_table_mock.cpp \
src/oemcrypto_rsa_key_shared.cpp \
src/oemcrypto_session.cpp \
src/oemcrypto_session_key_table.cpp \
src/oemcrypto_usage_table_mock.cpp \
src/wvcrc.cpp \
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/src \
vendor/widevine/libwvdrmengine/cdm/core/include \
vendor/widevine/libwvdrmengine/third_party/stringencoders/src \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl \
liblog \
libmedia \
libstagefright_foundation \
libutils \
libz \
LOCAL_STATIC_LIBRARIES := \
libcdm_utils \
libcrypto_static \
# Proprietary modules are put in vendor/lib instead of /system/lib.
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE := liboemcrypto
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,6 @@
# This file is generated by gyp; do not edit.
export builddir_name ?= ./../oemcrypto/mock/out
.PHONY: all
all:
$(MAKE) -C ../../linux oec_mock

View File

@@ -0,0 +1,45 @@
# Copyright 2013 Google Inc. All Rights Reserved.
{
'target_defaults': {
# It seems that if one target uses -fPIC, then all targets will need that
# flag or else there will be linking errors. For generating a shared
# library, we need position independent code.
'cflags': [
'-g',
'-fPIC',
],
'ldflags': [
'-fPIC',
],
},
'targets': [
{
'target_name': 'oec_mock',
'type': 'static_library',
'sources': [
'src/oemcrypto_keybox_testkey.cpp',
'src/oemcrypto_engine_device_properties.cpp',
],
'variables': {
'oec_mock_dir': '.',
},
'includes': [
'oec_mock_kernel.gypi',
],
'link_settings': {
'libraries': [
'-lcrypto',
],
},
},
{
'target_name': 'oec_mock_shared',
'type': 'shared_library',
'dependencies': [
'oec_mock'
# TODO(joeyparrish or fredgc): circular dependencies. 'wvcdm_sysdep'
],
},
],
}

View File

@@ -0,0 +1,84 @@
# This file is generated by gyp; do not edit.
TOOLSET := target
TARGET := oec_mock
DEFS_Default :=
# Flags passed to all source files.
CFLAGS_Default := \
-g \
-fPIC
# Flags passed to only C files.
CFLAGS_C_Default :=
# Flags passed to only C++ files.
CFLAGS_CC_Default :=
INCS_Default := \
-I../core/include \
-I../oemcrypto/include \
-I../oemcrypto/mock/src
OBJS := \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_keybox_testkey.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_engine_device_properties.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/keys.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_auth_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_engine_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_key_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_keybox_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_nonce_table.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_old_usage_table_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_rsa_key_shared.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_session.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_session_key_table.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_usage_table_mock.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/wvcrc.o \
$(obj).target/$(TARGET)/../oemcrypto/mock/src/oemcrypto_logging.o
# Add to the list of files we specially track dependencies for.
all_deps += $(OBJS)
# CFLAGS et al overrides must be target-local.
# See "Target-specific Variable Values" in the GNU Make manual.
$(OBJS): TOOLSET := $(TOOLSET)
$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
# Suffix rules, putting all outputs into $(obj).
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
# Try building from generated source, too.
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
# End of this set of suffix rules
### Rules for final target.
LDFLAGS_Default := \
-fPIC
LIBS :=
$(obj).target/../oemcrypto/mock/liboec_mock.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
$(obj).target/../oemcrypto/mock/liboec_mock.a: LIBS := $(LIBS)
$(obj).target/../oemcrypto/mock/liboec_mock.a: TOOLSET := $(TOOLSET)
$(obj).target/../oemcrypto/mock/liboec_mock.a: $(OBJS) FORCE_DO_CMD
$(call do_cmd,alink_thin)
all_deps += $(obj).target/../oemcrypto/mock/liboec_mock.a
# Add target alias
.PHONY: oec_mock
oec_mock: $(obj).target/../oemcrypto/mock/liboec_mock.a
# Add target alias to "all" target.
.PHONY: all
all: oec_mock

View File

@@ -0,0 +1,34 @@
# Copyright 2013 Google Inc. All Rights Reserved.
# Define oec_mock_dir and include into your oec target.
{
'include_dirs': [
'<(oec_mock_dir)/../../core/include', # for lock.h and wvcdm_types.h
'<(oec_mock_dir)/../include',
'<(oec_mock_dir)/src',
],
'direct_dependent_settings': {
'include_dirs': [
'<(oec_mock_dir)/../../core/include',
'<(oec_mock_dir)/../include',
'<(oec_mock_dir)/src',
],
},
'sources': [
'<(oec_mock_dir)/src/keys.cpp',
'<(oec_mock_dir)/src/oemcrypto_auth_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_engine_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_key_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_keybox_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_nonce_table.cpp',
'<(oec_mock_dir)/src/oemcrypto_old_usage_table_mock.cpp',
'<(oec_mock_dir)/src/oemcrypto_rsa_key_shared.cpp',
'<(oec_mock_dir)/src/oemcrypto_session.cpp',
'<(oec_mock_dir)/src/oemcrypto_session_key_table.cpp',
'<(oec_mock_dir)/src/oemcrypto_usage_table_mock.cpp',
'<(oec_mock_dir)/src/wvcrc.cpp',
'<(oec_mock_dir)/src/oemcrypto_logging.cpp',
],
}

View File

@@ -0,0 +1,33 @@
# This file is generated by gyp; do not edit.
TOOLSET := target
TARGET := oec_mock_shared
### Rules for final target.
LDFLAGS_Default := \
-fPIC
LIBS := \
-lcrypto
$(obj).target/../oemcrypto/mock/liboec_mock_shared.so: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
$(obj).target/../oemcrypto/mock/liboec_mock_shared.so: LIBS := $(LIBS)
$(obj).target/../oemcrypto/mock/liboec_mock_shared.so: LD_INPUTS := $(obj).target/../oemcrypto/mock/liboec_mock.a
$(obj).target/../oemcrypto/mock/liboec_mock_shared.so: TOOLSET := $(TOOLSET)
$(obj).target/../oemcrypto/mock/liboec_mock_shared.so: $(obj).target/../oemcrypto/mock/liboec_mock.a FORCE_DO_CMD
$(call do_cmd,solink)
all_deps += $(obj).target/../oemcrypto/mock/liboec_mock_shared.so
# Add target alias
.PHONY: oec_mock_shared
oec_mock_shared: $(builddir)/lib.target/liboec_mock_shared.so
# Copy this to the shared library output path.
$(builddir)/lib.target/liboec_mock_shared.so: TOOLSET := $(TOOLSET)
$(builddir)/lib.target/liboec_mock_shared.so: $(obj).target/../oemcrypto/mock/liboec_mock_shared.so FORCE_DO_CMD
$(call do_cmd,copy)
all_deps += $(builddir)/lib.target/liboec_mock_shared.so
# Short alias for building this shared library.
.PHONY: liboec_mock_shared.so
liboec_mock_shared.so: $(obj).target/../oemcrypto/mock/liboec_mock_shared.so $(builddir)/lib.target/liboec_mock_shared.so

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;

11
oemcrypto/mock/src/keys.h Normal file
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

@@ -0,0 +1,141 @@
// This file contains the test OEM cert.
#include "oem_cert.h"
// TODO(fredgc, b/30141311): get a real certificate from server gang.
// TODO(fredgc): This is in PEM format. Is that what we want?
const uint8_t kOEMPublicCert[] = "-----BEGIN CERTIFICATE-----\n"
"MIIEOTCCAyGgAwIBAgIJAJNm87hSM9ZIMA0GCSqGSIb3DQEBCwUAMIGyMQswCQYD\n"
"VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjERMA8GA1UEBwwIS2lya2xhbmQx\n"
"GDAWBgNVBAoMD0dvb2dsZSBXaWRldmluZTEXMBUGA1UECwwOVGVzdCBhbmQgRGVi\n"
"dWcxJjAkBgNVBAMMHU9FTUNyeXB0byBNb2NrIFJlZmVyZW5jZSBDb2RlMSAwHgYJ\n"
"KoZIhvcNAQkBFhFmcmVkZ2NAZ29vZ2xlLmNvbTAeFw0xNjEwMDYxODUzNDZaFw0x\n"
"NjEwMTYxODUzNDZaMIGyMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv\n"
"bjERMA8GA1UEBwwIS2lya2xhbmQxGDAWBgNVBAoMD0dvb2dsZSBXaWRldmluZTEX\n"
"MBUGA1UECwwOVGVzdCBhbmQgRGVidWcxJjAkBgNVBAMMHU9FTUNyeXB0byBNb2Nr\n"
"IFJlZmVyZW5jZSBDb2RlMSAwHgYJKoZIhvcNAQkBFhFmcmVkZ2NAZ29vZ2xlLmNv\n"
"bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuQQOqS9Q6K/Di8G4xl\n"
"8pz6Ea90D1FLYzIZXd98Ybd94qwWHYaaNuTfTXvZH07fWxrc8HG44N51ArzZGEaT\n"
"h1O7Jt39qLGMirocCgQLxNI3SP7l/pXmsZBM20tYKOoSq7E68eFbXWp39LDOVElS\n"
"/Lib0QkcwmJd55Fnxk6PIscBqYQilLIglQheoDLcljmOd9Rcr59JTtDENfBJE+SC\n"
"ZD0Kckc2e6lBvy/VGKUqE2hmt00pFBugWsN+SkasLGst5/pTVZfTKOBd1E3OvfL3\n"
"qtli4bRyPVX23zSk/bCzxQO47pXe6nVna9m4Yk/LfwJfDIb+r1ErNwyZ4SmZLw6T\n"
"LrkCAwEAAaNQME4wHQYDVR0OBBYEFNx/l/kMs1AY9IhIphtjzR7cwIVBMB8GA1Ud\n"
"IwQYMBaAFNx/l/kMs1AY9IhIphtjzR7cwIVBMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n"
"hvcNAQELBQADggEBAFSaXoWtFU/895lD1Sh/u6pdJcGuCJS0/s2scs95T1sglpfn\n"
"7pdTNtC1aniGuN0T+Aa2O95rJnS0W6RBGdKt1umFOcC7NfRBq1h7a1rn5wYicE5z\n"
"6rIumCSU4oniKMnQ5J+6LpKUFDVJt2W2o8LCKrHWgvcomo8B4ffdKrg+IKpS9Lxc\n"
"U3wyi27rRCyH004YpyE48hPXB3et6OmP2/Aw01d9LxTXieDh0HkhptEllV5EyGM2\n"
"nnRkbd21nHMkVUDzXYWDibxQj6PXw57smnjx7vTiZ1GoE72g2ENSwT1MQhoI59hy\n"
"WCKO91GNxZnyblSTrjLvzAY2hlVCauo7NmLG/yM=\n"
"-----END CERTIFICATE-----\n";
const size_t kOEMPublicCertSize = sizeof(kOEMPublicCert);
// TODO(fredgc): get the private key that goes with the certificate above.
const uint8_t kOEMPrivateKey[] = {
0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
0x01, 0x00, 0xcb, 0x90, 0x40, 0xea, 0x92, 0xf5, 0x0e, 0x8a, 0xfc, 0x38,
0xbc, 0x1b, 0x8c, 0x65, 0xf2, 0x9c, 0xfa, 0x11, 0xaf, 0x74, 0x0f, 0x51,
0x4b, 0x63, 0x32, 0x19, 0x5d, 0xdf, 0x7c, 0x61, 0xb7, 0x7d, 0xe2, 0xac,
0x16, 0x1d, 0x86, 0x9a, 0x36, 0xe4, 0xdf, 0x4d, 0x7b, 0xd9, 0x1f, 0x4e,
0xdf, 0x5b, 0x1a, 0xdc, 0xf0, 0x71, 0xb8, 0xe0, 0xde, 0x75, 0x02, 0xbc,
0xd9, 0x18, 0x46, 0x93, 0x87, 0x53, 0xbb, 0x26, 0xdd, 0xfd, 0xa8, 0xb1,
0x8c, 0x8a, 0xba, 0x1c, 0x0a, 0x04, 0x0b, 0xc4, 0xd2, 0x37, 0x48, 0xfe,
0xe5, 0xfe, 0x95, 0xe6, 0xb1, 0x90, 0x4c, 0xdb, 0x4b, 0x58, 0x28, 0xea,
0x12, 0xab, 0xb1, 0x3a, 0xf1, 0xe1, 0x5b, 0x5d, 0x6a, 0x77, 0xf4, 0xb0,
0xce, 0x54, 0x49, 0x52, 0xfc, 0xb8, 0x9b, 0xd1, 0x09, 0x1c, 0xc2, 0x62,
0x5d, 0xe7, 0x91, 0x67, 0xc6, 0x4e, 0x8f, 0x22, 0xc7, 0x01, 0xa9, 0x84,
0x22, 0x94, 0xb2, 0x20, 0x95, 0x08, 0x5e, 0xa0, 0x32, 0xdc, 0x96, 0x39,
0x8e, 0x77, 0xd4, 0x5c, 0xaf, 0x9f, 0x49, 0x4e, 0xd0, 0xc4, 0x35, 0xf0,
0x49, 0x13, 0xe4, 0x82, 0x64, 0x3d, 0x0a, 0x72, 0x47, 0x36, 0x7b, 0xa9,
0x41, 0xbf, 0x2f, 0xd5, 0x18, 0xa5, 0x2a, 0x13, 0x68, 0x66, 0xb7, 0x4d,
0x29, 0x14, 0x1b, 0xa0, 0x5a, 0xc3, 0x7e, 0x4a, 0x46, 0xac, 0x2c, 0x6b,
0x2d, 0xe7, 0xfa, 0x53, 0x55, 0x97, 0xd3, 0x28, 0xe0, 0x5d, 0xd4, 0x4d,
0xce, 0xbd, 0xf2, 0xf7, 0xaa, 0xd9, 0x62, 0xe1, 0xb4, 0x72, 0x3d, 0x55,
0xf6, 0xdf, 0x34, 0xa4, 0xfd, 0xb0, 0xb3, 0xc5, 0x03, 0xb8, 0xee, 0x95,
0xde, 0xea, 0x75, 0x67, 0x6b, 0xd9, 0xb8, 0x62, 0x4f, 0xcb, 0x7f, 0x02,
0x5f, 0x0c, 0x86, 0xfe, 0xaf, 0x51, 0x2b, 0x37, 0x0c, 0x99, 0xe1, 0x29,
0x99, 0x2f, 0x0e, 0x93, 0x2e, 0xb9, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02,
0x82, 0x01, 0x00, 0x5d, 0xc7, 0x67, 0x20, 0xa9, 0xf3, 0x1b, 0x70, 0x0c,
0x22, 0x57, 0x06, 0x99, 0xf7, 0x9d, 0x7d, 0x93, 0xf6, 0xf1, 0xcd, 0x96,
0x00, 0xed, 0xaa, 0x15, 0x3a, 0x7a, 0x74, 0xaa, 0xe8, 0x99, 0x8f, 0xf5,
0x0d, 0x32, 0x63, 0x07, 0xcf, 0xa3, 0xda, 0x6c, 0xc5, 0x55, 0x79, 0x01,
0x63, 0x64, 0xa2, 0xa4, 0x0d, 0x84, 0xf7, 0xdf, 0x24, 0x39, 0x57, 0xce,
0x9b, 0x11, 0xa8, 0x8d, 0x5b, 0x09, 0xcd, 0x19, 0x3b, 0x1e, 0xa9, 0xed,
0x3d, 0x5e, 0x71, 0xca, 0xab, 0x80, 0x31, 0xbc, 0xfa, 0x3f, 0x9e, 0x18,
0x92, 0xd5, 0x82, 0x23, 0xac, 0xd3, 0xc0, 0x96, 0xa7, 0xb0, 0x5e, 0x3c,
0xfb, 0x18, 0xfe, 0xdf, 0xf7, 0x37, 0xd7, 0x8a, 0x2f, 0xcf, 0x0c, 0xd4,
0x3d, 0x5f, 0xd0, 0x94, 0xb7, 0x16, 0x96, 0x35, 0xb2, 0x67, 0x70, 0x48,
0x5c, 0xe8, 0xc5, 0xf5, 0xc6, 0xc9, 0x25, 0x07, 0xec, 0x0d, 0xa1, 0x74,
0x09, 0x9d, 0xcd, 0x25, 0xa5, 0x7a, 0x2f, 0xa6, 0x4c, 0xba, 0x7d, 0x55,
0x4f, 0x79, 0x83, 0xaa, 0xfa, 0x3a, 0xc0, 0xaf, 0xc2, 0x33, 0xb2, 0x9a,
0x68, 0x89, 0x72, 0x2f, 0x86, 0x69, 0x28, 0x1b, 0xdc, 0xa2, 0x05, 0xe3,
0xfc, 0x24, 0x4c, 0xe0, 0x02, 0xb7, 0x06, 0x4b, 0xe0, 0x88, 0xe9, 0x79,
0x72, 0x46, 0x1c, 0x49, 0x5e, 0xfa, 0x2d, 0x29, 0xe5, 0xbf, 0x29, 0xfe,
0xf2, 0xee, 0xeb, 0xe8, 0x4a, 0x8d, 0xdd, 0xfa, 0x8e, 0xb0, 0x65, 0x68,
0x38, 0xa5, 0xb4, 0xd7, 0x75, 0xd7, 0x8c, 0x32, 0x51, 0xe3, 0x97, 0x00,
0x91, 0x19, 0x87, 0xaa, 0xeb, 0x4b, 0xcf, 0xf9, 0x6b, 0xf2, 0x87, 0xb9,
0x93, 0x4e, 0xd7, 0x5c, 0x27, 0xbb, 0x92, 0x29, 0x5e, 0xb8, 0xe9, 0x75,
0xc0, 0xc6, 0xa3, 0x8a, 0xb7, 0x1e, 0x19, 0xce, 0xd9, 0x5a, 0xc1, 0x0f,
0x6d, 0xa6, 0x4a, 0x56, 0x24, 0x10, 0x01, 0x02, 0x81, 0x81, 0x00, 0xe8,
0x1b, 0x99, 0x55, 0x0e, 0x19, 0x8b, 0x48, 0x46, 0xb1, 0x36, 0xb0, 0xe8,
0x15, 0x5f, 0x45, 0x8b, 0x09, 0xac, 0x82, 0xd1, 0x93, 0x6f, 0x8d, 0xed,
0xe8, 0x80, 0x82, 0xe4, 0xbc, 0x33, 0x40, 0x50, 0x71, 0x3f, 0x2c, 0x20,
0x46, 0x34, 0x3f, 0x27, 0xc3, 0x98, 0x66, 0x34, 0x99, 0x05, 0x06, 0x53,
0x43, 0xdf, 0x52, 0x0b, 0x56, 0x1a, 0xde, 0x3f, 0xbf, 0xba, 0x61, 0xd3,
0xcf, 0xfd, 0xee, 0x34, 0x71, 0x75, 0x9f, 0xfd, 0xc4, 0xb0, 0x8b, 0x26,
0xb1, 0x77, 0x83, 0xd4, 0x90, 0x28, 0xc2, 0xb7, 0x52, 0x98, 0xa0, 0x6b,
0x4b, 0x19, 0x8b, 0x28, 0xbb, 0x18, 0x3e, 0x29, 0x14, 0x7e, 0xa3, 0x87,
0xf8, 0x4d, 0x8a, 0x67, 0x3a, 0xf6, 0xec, 0x04, 0x7b, 0xfb, 0x3e, 0xa5,
0xe3, 0xbb, 0xb2, 0x35, 0xf2, 0xd1, 0x63, 0x55, 0xb6, 0x87, 0x07, 0xea,
0xbb, 0x06, 0x62, 0xbf, 0xaa, 0xbb, 0x19, 0x02, 0x81, 0x81, 0x00, 0xe0,
0x84, 0x77, 0xb0, 0xc1, 0x94, 0x89, 0xc6, 0x54, 0x78, 0x8a, 0xf0, 0xcd,
0xa4, 0x88, 0x92, 0xb4, 0xc9, 0xf1, 0x1c, 0x60, 0xea, 0x6d, 0x1c, 0x1d,
0x33, 0x92, 0x12, 0xe3, 0xe3, 0xd4, 0x11, 0xb6, 0x4d, 0x05, 0x93, 0xbc,
0x19, 0x50, 0xfc, 0x20, 0xe6, 0x07, 0x4a, 0xbc, 0xb6, 0x4f, 0xc1, 0x81,
0xc8, 0x71, 0xa7, 0xe2, 0x79, 0xb3, 0xc9, 0x43, 0x43, 0xca, 0x27, 0xe7,
0x37, 0x5b, 0x20, 0x83, 0x14, 0x8f, 0xb7, 0xba, 0x6c, 0x19, 0x21, 0xd5,
0x60, 0x87, 0xae, 0x45, 0xa8, 0x17, 0xd8, 0x73, 0xae, 0x65, 0xae, 0x83,
0x26, 0xff, 0x4e, 0x4f, 0x95, 0xdc, 0xce, 0x12, 0x40, 0xe6, 0xdd, 0xf2,
0x64, 0x84, 0x65, 0x86, 0x3e, 0xb4, 0xbe, 0x2d, 0x95, 0x74, 0x5c, 0xb2,
0xc6, 0x7c, 0x84, 0x17, 0x06, 0xe8, 0x80, 0xc8, 0xfe, 0x79, 0x22, 0xe6,
0xfa, 0x1f, 0xd2, 0x71, 0x84, 0x24, 0xa1, 0x02, 0x81, 0x80, 0x1d, 0x33,
0x63, 0xad, 0xfc, 0xb1, 0x20, 0x01, 0xbe, 0xcb, 0x0a, 0xbb, 0x64, 0xe7,
0x53, 0x6e, 0x17, 0x58, 0xe7, 0x38, 0x2a, 0x0f, 0xa7, 0x68, 0x2e, 0xb7,
0x22, 0x7b, 0xd5, 0x35, 0x0c, 0x29, 0x9a, 0x35, 0x35, 0x22, 0x63, 0x09,
0x12, 0x07, 0xa4, 0x04, 0x0a, 0x87, 0x49, 0x34, 0xbb, 0x1a, 0x19, 0x9d,
0x9f, 0x59, 0xde, 0x0d, 0x3e, 0x22, 0x19, 0xd9, 0x10, 0x24, 0xc0, 0x96,
0x19, 0x37, 0x3f, 0xa7, 0xca, 0x89, 0x8f, 0x4e, 0x90, 0x7b, 0x61, 0x29,
0xd0, 0x84, 0x68, 0x58, 0x9e, 0x98, 0x28, 0xa2, 0x1e, 0x8b, 0x88, 0x14,
0x11, 0xa9, 0x9d, 0x3d, 0x34, 0x86, 0x95, 0x7a, 0x7b, 0x98, 0x2d, 0x42,
0x02, 0xd7, 0x57, 0xb7, 0x66, 0x5b, 0x39, 0x11, 0x34, 0x01, 0xa4, 0xb3,
0x2a, 0xe8, 0xf7, 0xba, 0x8d, 0xb7, 0x36, 0x90, 0x59, 0x1a, 0x98, 0xe0,
0x60, 0xa4, 0x49, 0xc2, 0xbb, 0xf9, 0x02, 0x81, 0x80, 0x06, 0x01, 0x65,
0x16, 0x34, 0x47, 0x5d, 0xdc, 0x11, 0x3c, 0x5c, 0x33, 0x0e, 0xbd, 0x1c,
0xee, 0x17, 0xa9, 0xe3, 0x2a, 0x28, 0x29, 0x7d, 0x1b, 0xa8, 0x68, 0x4d,
0xba, 0xf5, 0x9f, 0x8d, 0x77, 0x9f, 0xd1, 0xb5, 0x99, 0x7b, 0x09, 0x8e,
0x52, 0x00, 0x2b, 0x46, 0xfc, 0xa7, 0xc9, 0x94, 0x9e, 0x8f, 0x73, 0x26,
0x1f, 0x20, 0x7e, 0xb2, 0xe1, 0x6a, 0x4c, 0x30, 0xe7, 0x1a, 0x57, 0x2f,
0xb7, 0xd1, 0xe9, 0xc5, 0xe2, 0x5b, 0x39, 0x32, 0xfe, 0xe5, 0xaf, 0x3c,
0x51, 0xdc, 0x09, 0x20, 0x02, 0x29, 0x2d, 0xfc, 0x08, 0x4b, 0xf7, 0xca,
0x12, 0x75, 0x2c, 0x84, 0x08, 0x7b, 0x12, 0x83, 0x5a, 0x62, 0x76, 0x6f,
0xd8, 0x2b, 0x5c, 0x18, 0x07, 0x92, 0x3e, 0x92, 0x2b, 0x3c, 0x98, 0xf4,
0x91, 0xaf, 0xef, 0xfe, 0x5e, 0x1b, 0x82, 0x3b, 0x09, 0x44, 0xf6, 0x61,
0xcd, 0x86, 0x3d, 0xcb, 0xa1, 0x02, 0x81, 0x81, 0x00, 0xe5, 0x81, 0xf6,
0x6f, 0x37, 0xf4, 0xef, 0xc7, 0x0e, 0xdf, 0x39, 0xd0, 0x97, 0x68, 0x1c,
0xd5, 0x12, 0x42, 0x00, 0x0e, 0xd1, 0x89, 0xdc, 0xf5, 0x24, 0x30, 0xb3,
0xeb, 0xea, 0x64, 0x3c, 0x9e, 0xa2, 0xc3, 0x49, 0x3c, 0xed, 0x2d, 0x4e,
0x9a, 0x00, 0x23, 0x71, 0xcc, 0x15, 0xda, 0x49, 0xdc, 0xab, 0xd1, 0x36,
0xe1, 0x8c, 0x91, 0x6c, 0x5b, 0x47, 0x43, 0x34, 0xec, 0xcd, 0x0c, 0xd0,
0x88, 0x7c, 0x5a, 0xd4, 0x91, 0x79, 0xe5, 0xe6, 0xd2, 0x5d, 0x2e, 0x14,
0x26, 0x81, 0x94, 0x9f, 0x29, 0x2b, 0x3e, 0xd6, 0x2f, 0x2d, 0xd9, 0xec,
0x88, 0x6a, 0xd3, 0x35, 0x71, 0x8a, 0xb2, 0xef, 0x22, 0xdc, 0xab, 0x26,
0xf9, 0x4d, 0x4c, 0x08, 0x8e, 0x16, 0x5b, 0x56, 0xb6, 0x76, 0x61, 0x89,
0x8a, 0x3a, 0xdb, 0xbb, 0x42, 0xe3, 0x50, 0x3f, 0xa1, 0x08, 0x82, 0xc2,
0x92, 0xef, 0x8c, 0xc2, 0xca
};
const size_t kOEMPrivateKeySize = sizeof(kOEMPrivateKey);

View File

@@ -0,0 +1,14 @@
// This header is used to access the OEM certificate if one is in use.
#ifndef OEM_CERT_H_
#define OEM_CERT_H_
#include <stddef.h>
#include <stdint.h>
extern const uint8_t kOEMPrivateKey[];
extern const size_t kOEMPrivateKeySize;
extern const uint8_t kOEMPublicCert[];
extern const size_t kOEMPublicCertSize;
#endif // OEM_CERT_H_

View File

@@ -0,0 +1,203 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_auth_mock.h"
#include <vector>
#include "keys.h"
#include "log.h"
#include "oemcrypto_key_mock.h"
#include "oemcrypto_logging.h"
#include "oemcrypto_rsa_key_shared.h"
#include "wv_cdm_constants.h"
namespace {
// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
// This is the RSA Test Key.
static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = {
0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01,
0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00,
0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a,
0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f,
0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c,
0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e,
0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a,
0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a,
0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3,
0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f,
0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06,
0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb,
0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3,
0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f,
0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4,
0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04,
0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25,
0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda,
0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67,
0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f,
0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03,
0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53,
0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13,
0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b,
0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e,
0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb,
0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01,
0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87,
0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72,
0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed,
0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44,
0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b,
0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb,
0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03,
0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e,
0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05,
0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29,
0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97,
0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7,
0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d,
0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc,
0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c,
0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1,
0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9,
0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3,
0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba,
0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7,
0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75,
0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22,
0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88,
0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78,
0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21,
0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14,
0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47,
0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa,
0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27,
0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d,
0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51,
0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64,
0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53,
0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77,
0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f,
0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29,
0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0,
0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21,
0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81,
0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02,
0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d,
0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75,
0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9,
0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04,
0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda,
0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4,
0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0,
0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f,
0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13,
0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64,
0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8,
0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96,
0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48,
0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44,
0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27,
0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef,
0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce,
0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87,
0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19,
0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28,
0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb,
0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78,
0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3,
0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54,
0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f,
0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41,
0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42,
0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87,
0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd,
0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53,
0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55,
0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17,
0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02,
0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57,
0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0,
0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97,
0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43,
0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c,
0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80,
0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4,
0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f,
0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81,
0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc,
0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41,
0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54,
0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8,
0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5,
0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf,
0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35,
0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b,
0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae,
0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10,
0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8,
0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b,
0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61,
0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a,
0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69,
0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34,
0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a,
0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26,
0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c,
0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a,
0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3,
0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8,
0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8,
0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80,
0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d,
0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65,
0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57,
0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7,
0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33,
0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50,
0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad,
0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35,
0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a,
0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30,
0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4,
0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18,
0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b,
0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde,
0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18,
0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03
};
} // namespace
namespace wvoec_mock {
AuthenticationRoot::AuthenticationRoot(OEMCrypto_ProvisioningMethod method) :
provisioning_method_(method),
use_test_keybox_(false) {
if ((provisioning_method_ == OEMCrypto_DrmCertificate) &&
!rsa_key_.LoadPkcs8RsaKey(kPrivateKey, kPrivateKeySize)) {
// This error message is OK in unit tests which use test certificate.
LOGE("FATAL ERROR: Platform uses a baked-in certificate instead of a "
"keybox, but the certificate could not be loaded.");
}
}
KeyboxError AuthenticationRoot::ValidateKeybox() {
return keybox().Validate();
}
bool AuthenticationRoot::LoadTestRsaKey() {
return rsa_key_.LoadPkcs8RsaKey(kTestRSAPKCS8PrivateKeyInfo2_2048,
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048));
}
bool AuthenticationRoot::Validate() {
return NO_ERROR == ValidateKeybox();
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,73 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_AUTH_MOCK_H_
#define OEMCRYPTO_AUTH_MOCK_H_
#include <stdint.h>
#include <vector>
#include "openssl/rsa.h"
#include "OEMCryptoCENC.h" // Needed for enums only.
#include "oemcrypto_key_mock.h"
#include "oemcrypto_keybox_mock.h"
#include "oemcrypto_rsa_key_shared.h"
#include "wv_cdm_types.h"
namespace wvoec_mock {
class AuthenticationRoot {
public:
explicit AuthenticationRoot(OEMCrypto_ProvisioningMethod method);
~AuthenticationRoot() {}
bool Validate();
KeyboxError ValidateKeybox();
bool InstallKeybox(const uint8_t* keybox_data, size_t keybox_length) {
return keybox().InstallKeybox(keybox_data, keybox_length);
}
const std::vector<uint8_t>& DeviceKey(bool use_real_keybox = false) {
return use_real_keybox ? real_keybox().device_key() :
keybox().device_key();
}
const std::vector<uint8_t>& DeviceId() {
return keybox().device_id();
}
size_t DeviceTokenLength() {
return keybox().key_data_length();
}
const uint8_t* const DeviceToken() {
return keybox().key_data();
}
WvKeybox& keybox() { return use_test_keybox_ ? test_keybox_ : keybox_; }
void UseTestKeybox() { use_test_keybox_ = true; }
RSA_shared_ptr& SharedRsaKey() { return rsa_key_; }
RSA* rsa_key() { return rsa_key_.get(); }
bool LoadTestRsaKey();
private:
OEMCrypto_ProvisioningMethod provisioning_method_;
WvKeybox& real_keybox() { return keybox_; }
WvKeybox keybox_;
WvTestKeybox test_keybox_;
bool use_test_keybox_;
RSA_shared_ptr rsa_key_; // If no keybox, this is baked in certificate.
CORE_DISALLOW_COPY_AND_ASSIGN(AuthenticationRoot);
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_AUTH_MOCK_H_

View File

@@ -0,0 +1,13 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_engine_mock.h"
namespace wvoec_mock {
CryptoEngine* CryptoEngine::MakeCryptoEngine(wvcdm::FileSystem* file_system) {
return new CryptoEngine(file_system);
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,35 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This file contains oemcrypto engine properties that would be for a
// level 1 device.
#include "oemcrypto_engine_mock.h"
namespace wvoec_mock {
class L1CryptoEngine : public CryptoEngine {
public:
explicit L1CryptoEngine(wvcdm::FileSystem* file_system)
: CryptoEngine(file_system) {}
bool config_local_display_only() { return true; }
OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() {
return HDCP_V2;
}
bool config_is_anti_rollback_hw_present() { return true; }
const char* config_security_level() { return "L1"; }
// This should start at 0, and be incremented only when a security patch has
// been applied to the device that fixes a security bug.
uint8_t config_security_patch_level() { return 3; }
};
CryptoEngine* CryptoEngine::MakeCryptoEngine(wvcdm::FileSystem* file_system) {
return new L1CryptoEngine(file_system);
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,34 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This file contains oemcrypto engine properties that would be for a device
// that does not have persistant storage or a keybox.
//
// Note: We also define it to be L2 for illustration only. Production devices
// are rarely level 2.
#include "oemcrypto_engine_mock.h"
namespace wvoec_mock {
class CertOnlyCryptoEngine : public CryptoEngine {
public:
explicit CertOnlyCryptoEngine(wvcdm::FileSystem* file_system)
: CryptoEngine(file_system) {}
bool config_local_display_only() { return true; }
bool config_supports_usage_table() { return false; }
OEMCrypto_ProvisioningMethod config_provisioning_method() {
return OEMCrypto_DrmCertificate;
}
const char* config_security_level() { return "L2"; }
};
CryptoEngine* CryptoEngine::MakeCryptoEngine(wvcdm::FileSystem* file_system) {
return new CertOnlyCryptoEngine(file_system);
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,594 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This file contains oemcrypto engine properties that reads data from a file
// to decide what it's current status is. It is used for testing cdm code.
// The following properties are read from the file:
// log_level: logging level to use.
// 0 = LOG_ERROR,
// 1 = LOG_WARN,
// 2 = LOG_INFO,
// 3 = LOG_DEBUG,
// 4 = LOG_VERBOSE
// kLogging*: All logging flags found in oemcrypto/include/oemcrypto_logging.h
// can be turned on (1) or off (0).
// security_level: returned by OEMCrypto_SecurityLevel.
// secure_lib: If set, then this will be used as a path to
// the L1 liboemcrypto.so that we can use secure buffers.
// current_hdcp: returned by OEMCrypto_GetHDCPCapability and
// used to verify the key control block in methods like DecryptCENC.
// HDCP_NONE = 0, // No HDCP supported, no secure data path.
// HDCP_V1 = 1, // HDCP version 1.0
// HDCP_V2 = 2, // HDCP version 2.0 Type 1.
// HDCP_V2_1 = 3, // HDCP version 2.1 Type 1.
// HDCP_V2_2 = 4, // HDCP version 2.2 Type 1.
// HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output.
// max_hdcp: returned by OEMCrypto_GetHDCPCapability. Same values as above.
// srm_update_supported: If "1", then srm update is supported.
// srm_initial_version: Initial value for srm version.
// This will be ignored after a reset. If this is not set, CurrentSRM will
// return NOT_IMPLEMENTED.
// srm_load_fail: If set to a nonzero number, then load_srm will
// fail and the version will not be updated. The number is converted to
// an OEMCryptoResult and returned.
// srm_load_version: If this is set, then it will be used as the
// new srm version after loading an SRM -- ignoring the contents of the SRM.
// If srm_load_version is -1, then the buffer passed into LoadSRM will be
// parsed.
// srm_blacklisted_device_attached: If set to "1", then a
// oemcrypto will act as if a blacklisted device is attached -- i.e.
// playback will be restricted to the local display only.
// srm_attached_device_id: If nonzero, the id of a blacklisted device.
// If this id is in the revocation list of an SRM file when it is loaded,
// playback will be restricted to the local display only.
// security_patch_level: This is the value returned by
// OEMCrypto_Security_Patch_Level. If the key control block requires a
// higher level, then OEMCrypto_LoadKeys will fail.
// max_buffer_size: maximum size of a buffer accepted by DecryptCENC and
// friends. If this is 0, there is no restriction. If it is 1, the
// minimum allowed value is used.
// use_keybox: If this is 1, then the test keybox is used. If this is zero,
// then the test OEM certificate is used.
// use_fallback: If this is nonzero, then the installed Level 1 library will
// be used to play content to a secure buffer. Decryption and key
// verification are done by the mock, but then the data is copied to the
// secure buffer using OEMCrypto_CopyBuffer. The filename of the fallback
// library is hardcoded to "level1_backup_liboemcrypto.so". It is
// recommended you use the install script to ensure you have the right
// filename.
//
#include <arpa/inet.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <string>
#include <map>
#include "log.h"
#include "oem_cert.h"
#include "oemcrypto_engine_mock.h"
#include "oemcrypto_logging.h"
#include "properties.h"
#include "string_conversions.h"
namespace wvoec_mock {
namespace {
typedef OEMCryptoResult (*L1_Initialize_t)(void);
typedef OEMCryptoResult (*L1_Terminate_t)(void);
typedef OEMCryptoResult (*L1_CopyBuffer_t)(const uint8_t* data_addr,
size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer,
uint8_t subsample_flags);
const std::string kDefaultOptionsFile = "/data/mediadrm/oemcrypto/options.txt";
} // namespace
class AndroidModifiableCryptoEngine : public CryptoEngine {
public:
AndroidModifiableCryptoEngine(wvcdm::FileSystem *file_system)
: CryptoEngine(file_system),
options_file_(kDefaultOptionsFile),
srm_loaded_(false),
srm_version_(0),
level1_valid_(false),
level1_library_(NULL) {
std::string path;
if (wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
options_file_ = path + "options.txt";
}
}
void MaybeReadOptionsFile() {
static time_t last_check = 0;
static time_t last_changed = 0;
time_t now = time(NULL);
if (now > last_check + 5) { // Check every five seconds.
last_check = now;
struct stat file_stat;
if (stat(options_file_.c_str(), &file_stat)) {
LOGE("Could not stat %s: %s", options_file_.c_str(), strerror(errno));
return;
}
if (file_stat.st_mtime > last_changed) {
last_changed = file_stat.st_mtime;
ReadOptionsFile();
}
}
}
void ReadOptionsFile() {
FILE *file = fopen(options_file_.c_str(), "r");
if (!file) {
LOGE("Could not read %s %s", options_file_.c_str(), strerror(errno));
return;
}
while (!feof(file)) {
char name[80 + 1];
int64_t value;
if (fscanf(file, "%80s %lld", name, &value)) {
LOGD("Option %s = %lld", name, value);
options_[std::string(name)] = value;
}
}
fclose(file);
InitializeLogging();
}
int64_t GetOption(const std::string &key, int64_t default_value) {
MaybeReadOptionsFile();
if (options_.find(key) == options_.end() ) {
LOGW("Option %s not set. Using default %lld", key.c_str(), default_value);
return default_value;
}
return options_[key];
}
void InitializeLogging() {
int log_level = GetOption("log_level", wvcdm::LOG_DEBUG);
int categories = 0;
if (GetOption("kLoggingTraceOEMCryptoCalls", 0) > 0)
categories |= kLoggingTraceOEMCryptoCalls;
if (GetOption("kLoggingDumpContentKeys", 0) > 0)
categories |= kLoggingDumpContentKeys;
if (GetOption("kLoggingDumpKeyControlBlocks", 0) > 0)
categories |= kLoggingDumpKeyControlBlocks;
if (GetOption("kLoggingDumpDerivedKeys", 0) > 0)
categories |= kLoggingDumpDerivedKeys;
if (GetOption("kLoggingTraceNonce", 0) > 0)
categories |= kLoggingTraceNonce;
if (GetOption("kLoggingTraceDecryption", 0) > 0)
categories |= kLoggingTraceDecryption;
if (GetOption("kLoggingTraceUsageTable", 0) > 0)
categories |= kLoggingTraceUsageTable;
if (GetOption("kLoggingTraceDecryptCalls", 0) > 0)
categories |= kLoggingTraceDecryptCalls;
if (GetOption("kLoggingDumpTraceAll", 0) > 0)
categories |= kLoggingDumpTraceAll;
SetLoggingSettings(log_level, categories);
}
#define QUOTE_DEFINE(A) #A
#define QUOTE(A) QUOTE_DEFINE(A)
#define LOOKUP(Name, Function) \
Name = (L1_##Name##t)dlsym(level1_library_, QUOTE(Function)); \
if (!Name) { \
LOGW("Could not load L1 %s.", \
QUOTE(Function)); \
Terminate(); \
return false; \
}
virtual bool Initialize() {
LOGD("OEMCrypto Mock With Options " " " __DATE__ " " __TIME__);
MaybeReadOptionsFile();
if (!GetOption("use_fallback", 1)) {
LOGD("Level 1 fallback ignored.");
return true;
}
level1_library_ = dlopen("level1_backup_liboemcrypto.so", RTLD_NOW);
if (level1_library_ == NULL) {
LOGE("Could not load backup: %s", dlerror());
return false;
}
LOOKUP(Initialize_, OEMCrypto_Initialize);
LOOKUP(Terminate_, OEMCrypto_Terminate);
LOOKUP(CopyBuffer_, OEMCrypto_CopyBuffer);
level1_valid_ = true;
OEMCryptoResult sts = Initialize_();
LOGD("L1 fall back initialized. status = %d.", sts);
if (sts != OEMCrypto_SUCCESS) {
LOGW("Terminating L1 because init failed.");
Terminate();
LOGW("Continuing Mock without L1 fallback.");
}
return true;
}
virtual void Terminate() {
if (level1_valid_) Terminate_();
if (level1_library_ != NULL) {
LOGD("Closing L1 fall back.\n");
dlclose(level1_library_);
level1_valid_ = false;
level1_library_ = NULL;
CopyBuffer_ = NULL;
Initialize_ = NULL;
Terminate_ = NULL;
} else {
LOGD("Terminate mock.\n");
}
}
const char *HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
switch (value) {
case HDCP_NONE:
return "No HDCP supported, no secure data path";
case HDCP_V1:
return "HDCP version 1.0";
case HDCP_V2:
return "HDCP version 2.0";
case HDCP_V2_1:
return "HDCP version 2.1";
case HDCP_V2_2:
return "HDCP version 2.2";
case HDCP_NO_DIGITAL_OUTPUT:
return "No HDCP device attached/using local display with secure path";
default:
return "<INVALID VALUE>";
}
}
OEMCrypto_ProvisioningMethod config_provisioning_method() {
if (GetOption("use_keybox", 1)) {
return OEMCrypto_Keybox;
} else {
return OEMCrypto_OEMCertificate;
}
}
OEMCryptoResult get_oem_certificate(SessionContext* session,
uint8_t* public_cert,
size_t* public_cert_length) {
if (GetOption("use_keybox", 1)) {
LOGD("OEM Cert asked for when use_keybox = 1.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (kOEMPublicCertSize == 0) {
LOGD("OEM Cert Size is 0.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (public_cert_length == NULL) {
LOGD("OEM Cert length is 0.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*public_cert_length < kOEMPublicCertSize) {
*public_cert_length = kOEMPublicCertSize;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*public_cert_length = kOEMPublicCertSize;
if (public_cert == NULL) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize);
if (!session->LoadRSAKey(kOEMPrivateKey, kOEMPrivateKeySize)) {
LOGE("Private RSA Key did not load correctly.");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return OEMCrypto_SUCCESS;
}
// Returns "L3" for a software only library. L1 is for hardware protected
// data paths.
const char *config_security_level() {
switch (GetOption("security_level", 0)) {
default:
LOGW("Option security_level not set. Default is L3.");
case 3:
return "L3";
case 2:
return "L2";
case 1:
return "L1";
}
}
// Returns the HDCP version currently in use.
OEMCrypto_HDCP_Capability config_current_hdcp_capability() {
static OEMCrypto_HDCP_Capability current_hdcp = HDCP_NONE;
OEMCrypto_HDCP_Capability new_current_hdcp =
static_cast<OEMCrypto_HDCP_Capability>(GetOption("current_hdcp", 0));
if (current_hdcp != new_current_hdcp) {
LOGI("OEMCrypto current HDCP changed from %d (%s) to %d (%s)", current_hdcp,
HDCPCapabilityAsString(current_hdcp), new_current_hdcp,
HDCPCapabilityAsString(new_current_hdcp));
current_hdcp = new_current_hdcp;
}
return current_hdcp;
}
// Returns the max HDCP version supported.
OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() {
static OEMCrypto_HDCP_Capability max_hdcp = HDCP_NONE;
MaybeReadOptionsFile();
OEMCrypto_HDCP_Capability new_max_hdcp =
static_cast<OEMCrypto_HDCP_Capability>(GetOption("max_hdcp", 0));
if (max_hdcp != new_max_hdcp) {
LOGI("OEMCrypto max HDCP changed from %d (%s) to %d (%s)", max_hdcp,
HDCPCapabilityAsString(max_hdcp), new_max_hdcp,
HDCPCapabilityAsString(new_max_hdcp));
max_hdcp = new_max_hdcp;
}
return max_hdcp;
}
// This should start at 0, and be incremented only when a security patch has
// been applied to the device that fixes a security bug.
uint8_t config_security_patch_level() {
return GetOption("security_patch_level", 0);
}
size_t max_buffer_size() {
int max = GetOption("max_buffer_size", 0);
// If max is 1, just use default max buffer.
if (max == 1) return CryptoEngine::max_buffer_size();
return max; // If 0, no restriction. If something else, use that restriction.
}
bool srm_update_supported() {
int supported = GetOption("srm_update_supported", 0);
LOGI("OEMCrypto mock %s supporting SRM update.",
supported ? "is" : "is not");
return supported != 0;
}
OEMCryptoResult current_srm_version(uint16_t *version) {
if (srm_loaded_) {
LOGV("SRM loaded. version used is %d.", srm_version_);
*version = srm_version_;
return OEMCrypto_SUCCESS;
}
int value = GetOption("srm_initial_version", -1);
if (value > 0) {
LOGV("SRM version from get option: %d.", value);
srm_version_ = value;
*version = value;
return OEMCrypto_SUCCESS;
} else {
LOGI("SRM initial version is %d -- reporting not implemented.", value);
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
// Convert uint24 or uint40 into a uint64.
int64_t unpack_odd_bytes(const uint8_t *buffer, size_t length) {
uint8_t small_buffer[8];
memset(small_buffer, 0, 8);
if (length > 8) {
LOGE("OEMCrypto Mock: programmer error. unpack %d bytes.", length);
length = 8;
}
size_t offset = 8 - length;
memcpy(small_buffer + offset, buffer, length);
return wvcdm::htonll64(*reinterpret_cast<const int64_t*>(small_buffer));
}
OEMCryptoResult load_srm(const uint8_t *buffer, size_t buffer_length) {
if (!srm_update_supported()) {
LOGE("OEMCrypto mock update not supported, but load_srm called.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
int result = GetOption("srm_load_fail", 0);
if (result > 0) {
LOGE("OEMCrypto mock load_srm returning error %d.", result);
return static_cast<OEMCryptoResult>(result);
}
int new_version = GetOption("srm_load_version", -1);
if (new_version >= 0) {
if (new_version < srm_version_) {
LOGE("New SRM version is lower than existing SRM version: %d < %d",
new_version, srm_version_);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
srm_version_ = new_version;
LOGI("OEMCrypto mock told to change SRM version to %d.", srm_version_);
srm_loaded_ = true;
return OEMCrypto_SUCCESS;
}
if (buffer_length < 395) {
LOGE("OEMCrypto mock bad buffer size: %ld < 395.", buffer_length);
return OEMCrypto_ERROR_SHORT_BUFFER;
}
uint8_t srm_id = buffer[0] >> 4;
uint8_t hdcp2_indicator = buffer[0] & 0x0F;
uint8_t reserved = buffer[1];
uint16_t version = htons(*reinterpret_cast<const uint16_t *>(&buffer[2]));
if (reserved) {
LOGE("OEMCrypto mock. SRM's second byte nonzero: %02X.", reserved);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint8_t generation = buffer[4];
if (generation > 1) {
LOGW("OEMCrypto mock. SRM Generation number is %d, but only first gen is parsed.",
generation);
LOGW("If the revoked device is in a a later generation, it will not be recognized.");
}
int64_t length = unpack_odd_bytes(buffer + 5, 3); // 24 bits.
if (length + 5 != buffer_length) {
LOGW("OEMCrypto mock. SRM length is %lld = 0x%llx, but I expected %zd = 0x%zx.",
length, length, buffer_length - 5, buffer_length - 5);
}
int64_t count = 0;
const uint8_t *ids;
if (srm_id == 8 && hdcp2_indicator == 0) {
// https://www.digital-cp.com/sites/default/files/specifications/HDCP%20Specification%20Rev1_4_Secure.pdf
count = buffer[8];
LOGI("OEMCrypto mock loading HDCP1 SRM. version = %d. count=%lld.",
version, count);
ids = buffer + 9;
if (buffer_length < 9 + count*5) {
LOGE("OEMCrypto mock bad buffer size for count = %lld: %d < %lld.",
count, buffer_length, 12 + count*5);
return OEMCrypto_ERROR_SHORT_BUFFER;
}
} else if (srm_id == 9 && hdcp2_indicator == 1) {
// https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
count = unpack_odd_bytes(buffer + 8, 2) >> 6; // 10 bits = 2 bytes - 6.
LOGI("OEMCrypto mock loading HDCP2 SRM. version = %d. count=%lld.",
version, count);
ids = buffer + 12;
if (buffer_length < 12 + count*5) {
LOGE("OEMCrypto mock bad buffer size for count: %d < %ld.",
buffer_length, 12 + count*5);
return OEMCrypto_ERROR_SHORT_BUFFER;
}
} else {
LOGE("OEMCrypto mock bad buffer start: %02X%02X%02X%02X...", buffer[0],
buffer[1], buffer[2], buffer[3]);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for(size_t i = 0; i < count; i++) {
int64_t id = unpack_odd_bytes(ids + 5*i, 5);
srm_revocation_list_.push_back(id);
LOGI("OEMCrypto mock SRM revokes device %lld = 0x%llx", id, id);
}
srm_loaded_ = true;
srm_version_ = version;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult remove_srm() {
if (!srm_update_supported()) {
LOGE("OEMCrypto mock update not supported, bug load_srm called.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
srm_version_ = 0;
srm_loaded_ = false;
return OEMCrypto_SUCCESS;
}
bool srm_blacklisted_device_attached() {
if (GetOption("srm_load_version", -1) < 0) {
return scan_revoked_list();
}
static int blacklisted = 0;
int new_value = GetOption("srm_blacklisted_device_attached", 0);
if (new_value != blacklisted) {
LOGI("SRM blacklisted device changed from %d to %d", blacklisted,
new_value);
blacklisted = new_value;
}
return blacklisted > 0;
}
bool scan_revoked_list() {
static int64_t old_attached_id = 0;
int64_t attached_id = GetOption("srm_attached_device_id", 0);
bool print_all_ids = false;
if (attached_id != old_attached_id) {
LOGD("OEMCrypto mock -- ID of attached device is %lld = 0x%lld",
attached_id, attached_id);
old_attached_id = attached_id;
print_all_ids = true;
}
for (size_t i = 0; i < srm_revocation_list_.size(); i++) {
if (print_all_ids) {
LOGD("OEMCrypto mock: %d) revoked id %lld = 0x%lld.", i,
srm_revocation_list_[i], srm_revocation_list_[i]);
}
if (srm_revocation_list_[i] == attached_id) {
LOGD("OEMCrypto mock: attached device %lld = 0x%lld is revoked.",
attached_id, attached_id);
return true;
}
}
LOGD("OEMCrypto mock: attached device %lld is not revoked.", attached_id);
return false;
}
virtual void adjust_destination(OEMCrypto_DestBufferDesc *out_description,
size_t data_length, uint8_t subsample_flags) {
if (out_description->type != OEMCrypto_BufferType_Secure) return;
if (!level1_valid_) {
static bool warned_once = false;
if (!warned_once) {
warned_once = true;
LOGW("OEMCrypto Mock: given secure buffer with no level1 fallback.");
}
return;
}
if (subsample_flags & OEMCrypto_FirstSubsample) {
final_destination_.type = OEMCrypto_BufferType_Secure;
final_destination_.buffer.secure.handle =
out_description->buffer.secure.handle;
final_destination_.buffer.secure.max_length =
out_description->buffer.secure.max_length;
final_destination_.buffer.secure.offset =
out_description->buffer.secure.offset;
temp_buffer_.resize(final_destination_.buffer.secure.max_length);
temp_buffer_length_ = 0;
}
if (temp_buffer_length_ != out_description->buffer.secure.offset) {
LOGW("OEMCrypto: offset into secure buffer is not correct %zd != %zd.",
temp_buffer_length_, out_description->buffer.secure.offset);
}
size_t new_length = temp_buffer_length_ + data_length;
if (new_length > temp_buffer_.size()) {
LOGW("Temp buffer was not big enough. %zd > %zd.", new_length,
temp_buffer_.size());
temp_buffer_.resize(new_length);
}
destination_ = &temp_buffer_[temp_buffer_length_];
temp_buffer_length_ = new_length;
}
// Push destination buffer to L1 output.
virtual OEMCryptoResult PushDestination(
OEMCrypto_DestBufferDesc *out_description, uint8_t subsample_flags) {
if (level1_valid_ &&
(out_description->type == OEMCrypto_BufferType_Secure)) {
if (subsample_flags & OEMCrypto_LastSubsample) {
return CopyBuffer_(&temp_buffer_[0], temp_buffer_length_,
&final_destination_,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
}
}
return OEMCrypto_SUCCESS;
}
private:
// If the SRM version has been loaded or not. If not, we use the system
// property to find the current SRM version.
bool srm_loaded_;
// Current srm version. Before an SRM has been loaded, this will be set from
// the system property.
int srm_version_;
// List of forbidden/revoked devices.
std::vector<int64_t> srm_revocation_list_;
std::map<std::string, int64_t> options_;
std::string options_file_;
bool level1_valid_;
void* level1_library_;
L1_CopyBuffer_t CopyBuffer_;
L1_Initialize_t Initialize_;
L1_Terminate_t Terminate_;
OEMCrypto_DestBufferDesc final_destination_;
std::vector<uint8_t> temp_buffer_;
size_t temp_buffer_length_; // Length of temp buffer currently in use.
};
CryptoEngine* CryptoEngine::MakeCryptoEngine(wvcdm::FileSystem *file_system) {
return new AndroidModifiableCryptoEngine(file_system);
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,65 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This file contains oemcrypto engine properties that would be for a
// level 2 device that does not have persistant storage or a keybox.
// Note: this is for illustration only. Production devices are rarely level 2.
#include "oemcrypto_engine_mock.h"
#include <string.h>
#include "log.h"
#include "oem_cert.h"
namespace wvoec_mock {
class Prov30CryptoEngine : public CryptoEngine {
public:
explicit Prov30CryptoEngine(wvcdm::FileSystem* file_system)
: CryptoEngine(file_system) {}
bool config_local_display_only() { return true; }
bool config_supports_usage_table() { return false; }
OEMCrypto_ProvisioningMethod config_provisioning_method() {
return OEMCrypto_OEMCertificate;
}
OEMCryptoResult get_oem_certificate(SessionContext* session,
uint8_t* public_cert,
size_t* public_cert_length) {
if (kOEMPublicCertSize == 0) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (public_cert_length == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*public_cert_length < kOEMPublicCertSize) {
*public_cert_length = kOEMPublicCertSize;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*public_cert_length = kOEMPublicCertSize;
if (public_cert == NULL) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize);
if (!session->LoadRSAKey(kOEMPrivateKey, kOEMPrivateKeySize)) {
LOGE("Private RSA Key did not load correctly.");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return OEMCrypto_SUCCESS;
}
// Returns "L3" for a software only library. L1 is for hardware protected
// keys and data paths. L2 is for hardware protected keys but no data path
// protection.
const char* config_security_level() { return "L2"; }
};
CryptoEngine* CryptoEngine::MakeCryptoEngine(wvcdm::FileSystem* file_system) {
return new Prov30CryptoEngine(file_system);
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,123 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_engine_mock.h"
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <openssl/err.h>
#include "keys.h"
#include "log.h"
#include "oemcrypto_key_mock.h"
#include "oemcrypto_rsa_key_shared.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
// Note: The class CryptoEngine is configured at compile time by compiling in
// different device property files. The methods in this file are generic to
// all configurations. See the files oemcrypto_engine_device_properties*.cpp
// for methods that are configured for specific configurations.
CryptoEngine::CryptoEngine(wvcdm::FileSystem* file_system)
: root_of_trust_(config_provisioning_method()),
file_system_(file_system),
usage_table_(this) {
ERR_load_crypto_strings();
}
CryptoEngine::~CryptoEngine() {
wvcdm::AutoLock lock(session_table_lock_);
ActiveSessions::iterator it;
for (it = sessions_.begin(); it != sessions_.end(); ++it) {
delete it->second;
}
sessions_.clear();
}
SessionId CryptoEngine::CreateSession() {
wvcdm::AutoLock lock(session_table_lock_);
static int unique_id = 1;
SessionId sid = (SessionId)++unique_id;
SessionContext* sctx =
new SessionContext(this, sid, root_of_trust_.SharedRsaKey());
sessions_[sid] = sctx;
return sid;
}
bool CryptoEngine::DestroySession(SessionId sid) {
SessionContext* sctx = FindSession(sid);
wvcdm::AutoLock lock(session_table_lock_);
if (sctx) {
sessions_.erase(sid);
delete sctx;
return true;
} else {
return false;
}
}
SessionContext* CryptoEngine::FindSession(SessionId sid) {
wvcdm::AutoLock lock(session_table_lock_);
ActiveSessions::iterator it = sessions_.find(sid);
if (it != sessions_.end()) {
return it->second;
}
return NULL;
}
OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() {
return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1;
}
OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() {
return HDCP_NO_DIGITAL_OUTPUT;
}
OEMCryptoResult CryptoEngine::SetDestination(
OEMCrypto_DestBufferDesc* out_description, size_t data_length,
uint8_t subsample_flags) {
size_t max_length = 0;
switch (out_description->type) {
case OEMCrypto_BufferType_Clear:
destination_ = out_description->buffer.clear.address;
max_length = out_description->buffer.clear.max_length;
break;
case OEMCrypto_BufferType_Secure:
destination_ =
reinterpret_cast<uint8_t*>(out_description->buffer.secure.handle) +
out_description->buffer.secure.offset;
max_length = out_description->buffer.secure.max_length -
out_description->buffer.secure.offset;
break;
case OEMCrypto_BufferType_Direct:
// Direct buffer type is only used on some specialized devices where
// oemcrypto has a direct connection to the screen buffer. It is not,
// for example, supported on Android.
destination_ = NULL;
break;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (out_description->type != OEMCrypto_BufferType_Direct &&
max_length < data_length) {
LOGE("[SetDestination(): OEMCrypto_ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
adjust_destination(out_description, data_length, subsample_flags);
if ((out_description->type != OEMCrypto_BufferType_Direct) &&
(destination_ == NULL)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,177 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef MOCK_OEMCRYPTO_ENGINE_MOCK_H_
#define MOCK_OEMCRYPTO_ENGINE_MOCK_H_
#include <stdint.h>
#include <time.h>
#include <map>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCENC.h" // Needed for enums only.
#include "file_store.h"
#include "lock.h"
#include "oemcrypto_auth_mock.h"
#include "oemcrypto_key_mock.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session.h"
#include "oemcrypto_usage_table_mock.h"
#include "wv_cdm_types.h"
namespace wvoec_mock {
typedef std::map<SessionId, SessionContext*> ActiveSessions;
class CryptoEngine {
public:
// This is like a factory method, except we choose which version to use at
// compile time. It is defined in several source files. The build system
// should choose which one to use by only linking in the correct one.
static CryptoEngine* MakeCryptoEngine(wvcdm::FileSystem* file_system);
virtual ~CryptoEngine();
virtual bool Initialize() { return true; }
bool ValidRootOfTrust() { return root_of_trust_.Validate(); }
bool InstallKeybox(const uint8_t* keybox, size_t keybox_length) {
return root_of_trust_.InstallKeybox(keybox, keybox_length);
}
void UseTestKeybox() { root_of_trust_.UseTestKeybox(); }
bool LoadTestRsaKey() { return root_of_trust_.LoadTestRsaKey(); }
KeyboxError ValidateKeybox() { return root_of_trust_.ValidateKeybox(); }
const std::vector<uint8_t>& DeviceRootKey(bool override_to_real = false) {
return root_of_trust_.DeviceKey(override_to_real);
}
const std::vector<uint8_t>& DeviceRootId() {
return root_of_trust_.DeviceId();
}
size_t DeviceRootTokenLength() { return root_of_trust_.DeviceTokenLength(); }
const uint8_t* const DeviceRootToken() {
return root_of_trust_.DeviceToken();
}
virtual void Terminate() {}
SessionId CreateSession();
bool DestroySession(SessionId sid);
SessionContext* FindSession(SessionId sid);
size_t GetNumberOfOpenSessions() { return sessions_.size(); }
size_t GetMaxNumberOfSessions() {
// An arbitrary limit for mock implementation.
static const size_t kMaxSupportedOEMCryptoSessions = 64;
return kMaxSupportedOEMCryptoSessions;
}
// Returns the HDCP version currently in use.
virtual OEMCrypto_HDCP_Capability config_current_hdcp_capability();
// Returns the max HDCP version supported.
virtual OEMCrypto_HDCP_Capability config_maximum_hdcp_capability();
UsageTable& usage_table() { return usage_table_; }
wvcdm::FileSystem* file_system() { return file_system_; }
// If config_local_display_only() returns true, we pretend we are using a
// built-in display, instead of HDMI or WiFi output.
virtual bool config_local_display_only() { return false; }
// A closed platform is permitted to use clear buffers.
virtual bool config_closed_platform() { return false; }
// Returns true if the client supports persistent storage of
// offline usage table information.
virtual bool config_supports_usage_table() { return true; }
virtual OEMCrypto_ProvisioningMethod config_provisioning_method() {
return OEMCrypto_Keybox;
}
virtual OEMCryptoResult get_oem_certificate(SessionContext* session,
uint8_t* public_cert,
size_t* public_cert_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Used for OEMCrypto_IsAntiRollbackHwPresent.
virtual bool config_is_anti_rollback_hw_present() { return false; }
// Returns "L3" for a software only library. L1 is for hardware protected
// data paths.
virtual const char* config_security_level() { return "L3"; }
// This should start at 0, and be incremented only when a security patch has
// been applied to the device that fixes a security bug.
virtual uint8_t config_security_patch_level() { return 0; }
// If 0 no restriction, otherwise it's the max buffer for DecryptCENC.
virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB.
virtual bool srm_update_supported() { return false; }
virtual OEMCryptoResult current_srm_version(uint16_t* version) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult load_srm(const uint8_t* buffer,
size_t buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult remove_srm() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual bool srm_blacklisted_device_attached() { return false; }
// Set destination pointer based on the output destination description.
OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description,
size_t data_length, uint8_t subsample_flags);
// The current destination.
uint8_t* destination() { return destination_; }
// Subclasses can adjust the destination -- for use in testing.
virtual void adjust_destination(OEMCrypto_DestBufferDesc* out_description,
size_t data_length, uint8_t subsample_flags) {
}
// Push destination buffer to output -- used by subclasses for testing.
virtual OEMCryptoResult PushDestination(
OEMCrypto_DestBufferDesc* out_description, uint8_t subsample_flags) {
return OEMCrypto_SUCCESS;
}
protected:
explicit CryptoEngine(wvcdm::FileSystem* file_system);
uint8_t* destination_;
private:
ActiveSessions sessions_;
AuthenticationRoot root_of_trust_;
wvcdm::Lock session_table_lock_;
wvcdm::FileSystem* file_system_;
UsageTable usage_table_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
};
} // namespace wvoec_mock
#endif // MOCK_OEMCRYPTO_ENGINE_MOCK_H_

View File

@@ -0,0 +1,121 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_key_mock.h"
#include <string.h>
#include <vector>
#include "log.h"
#include "oemcrypto_logging.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
bool KeyControlBlock::Validate() {
if (memcmp(verification_, "kctl", 4) && // original verification
memcmp(verification_, "kc09", 4) && // add in version 9 api
memcmp(verification_, "kc10", 4) && // add in version 10 api
memcmp(verification_, "kc11", 4) && // add in version 11 api
memcmp(verification_, "kc12", 4) && // add in version 11 api
memcmp(verification_, "kc13", 4)) { // add in version 13 api
LOGE("KCB: BAD verification string: %4.4s", verification_);
valid_ = false;
} else {
valid_ = true;
}
return valid_;
}
// This extracts 4 bytes in network byte order to a 32 bit integer in
// host byte order.
uint32_t KeyControlBlock::ExtractField(const std::vector<uint8_t>& str,
int idx) {
int bidx = idx * 4;
uint32_t t = static_cast<unsigned char>(str[bidx]) << 24;
t |= static_cast<unsigned char>(str[bidx + 1]) << 16;
t |= static_cast<unsigned char>(str[bidx + 2]) << 8;
t |= static_cast<unsigned char>(str[bidx + 3]);
return t;
}
KeyControlBlock::KeyControlBlock(
const std::vector<uint8_t>& key_control_string) {
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(),
wvcdm::KEY_CONTROL_SIZE);
return;
}
memcpy(verification_, &key_control_string[0], 4);
duration_ = ExtractField(key_control_string, 1);
nonce_ = ExtractField(key_control_string, 2);
control_bits_ = ExtractField(key_control_string, 3);
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
LOGD("KCB:");
LOGD(" valid: %d", valid());
LOGD(" duration: %d", duration());
LOGD(" nonce: %08X", nonce());
LOGD(" magic: %08X", verification());
LOGD(" bits: %08X", control_bits());
LOGD(" bit kSharedLicense %s.",
(control_bits() & kSharedLicense) ? "set" : "unset");
LOGD(" bit kControlSRMVersionRequired %s.",
(control_bits() & kControlSRMVersionRequired) ? "set" : "unset");
LOGD(" bit kControlDisableAnalogOutput %s.",
(control_bits() & kControlDisableAnalogOutput) ? "set" : "unset");
LOGD(" bits kControlSecurityPatchLevel 0x%02x.",
(control_bits() & kControlSecurityPatchLevelMask)
>> kControlSecurityPatchLevelShift);
switch (control_bits() & kControlReplayMask) {
case kControlNonceRequired:
LOGD(" bits kControlReplay kControlNonceRequired.");
break;
case kControlNonceOrEntry:
LOGD(" bits kControlReplay kControlNonceOrEntry.");
break;
default:
LOGD(" bits kControlReplay unset.");
break;
}
LOGD(" bits kControlHDCPVersion 0x%02x.",
(control_bits() & kControlHDCPVersionMask)
>> kControlHDCPVersionShift);
LOGD(" bit kControlAllowEncrypt %s.",
(control_bits() & kControlAllowEncrypt) ? "set" : "unset");
LOGD(" bit kControlAllowDecrypt %s.",
(control_bits() & kControlAllowDecrypt) ? "set" : "unset");
LOGD(" bit kControlAllowSign %s.",
(control_bits() & kControlAllowSign) ? "set" : "unset");
LOGD(" bit kControlAllowVerify %s.",
(control_bits() & kControlAllowVerify) ? "set" : "unset");
LOGD(" bit kControlObserveDataPath %s.",
(control_bits() & kControlObserveDataPath) ? "set" : "unset");
LOGD(" bit kControlObserveHDCP %s.",
(control_bits() & kControlObserveHDCP) ? "set" : "unset");
LOGD(" bit kControlObserveCGMS %s.",
(control_bits() & kControlObserveCGMS) ? "set" : "unset");
LOGD(" bit kControlDataPathSecure %s.",
(control_bits() & kControlDataPathSecure) ? "set" : "unset");
LOGD(" bit kControlNonceEnabled %s.",
(control_bits() & kControlNonceEnabled) ? "set" : "unset");
LOGD(" bit kControlHDCPRequired %s.",
(control_bits() & kControlHDCPRequired) ? "set" : "unset");
uint32_t cgms_bits = control_bits() & 0x3;
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
}
Validate();
}
void Key::UpdateDuration(const KeyControlBlock& control) {
control_.set_duration(control.duration());
}
void KeyControlBlock::RequireLocalDisplay() {
// Set all bits to require HDCP Local Display Only.
control_bits_ |= kControlHDCPVersionMask;
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,90 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_KEY_MOCK_H_
#define OEMCRYPTO_KEY_MOCK_H_
#include <stdint.h>
#include <string>
#include <vector>
namespace wvoec_mock {
const uint32_t kControlObserveDataPath = (1<<31);
const uint32_t kControlObserveHDCP = (1<<30);
const uint32_t kControlObserveCGMS = (1<<29);
const uint32_t kControlRequireAntiRollbackHardware = (1<<28);
const uint32_t kSharedLicense = (1<<23);
const uint32_t kControlSRMVersionRequired = (1<<22);
const uint32_t kControlDisableAnalogOutput = (1<<21);
const uint32_t kControlSecurityPatchLevelShift = 15;
const uint32_t kControlSecurityPatchLevelMask =
(0x3F<<kControlSecurityPatchLevelShift);
const uint32_t kControlReplayMask = (0x03<<13);
const uint32_t kControlNonceRequired = (0x01<<13);
const uint32_t kControlNonceOrEntry = (0x02<<13);
const uint32_t kControlHDCPVersionShift = 9;
const uint32_t kControlHDCPVersionMask = (0x0F<<kControlHDCPVersionShift);
const uint32_t kControlAllowEncrypt = (1<<8);
const uint32_t kControlAllowDecrypt = (1<<7);
const uint32_t kControlAllowSign = (1<<6);
const uint32_t kControlAllowVerify = (1<<5);
const uint32_t kControlDataPathSecure = (1<<4);
const uint32_t kControlNonceEnabled = (1<<3);
const uint32_t kControlHDCPRequired = (1<<2);
const uint32_t kControlCGMSMask = (0x03);
const uint32_t kControlCGMSCopyFreely = (0x00);
const uint32_t kControlCGMSCopyOnce = (0x02);
const uint32_t kControlCGMSCopyNever = (0x03);
class KeyControlBlock {
public:
KeyControlBlock(const std::vector<uint8_t>& key_control_string);
~KeyControlBlock() {}
bool Validate();
void Invalidate() { valid_ = false; }
bool valid() const { return valid_; }
uint32_t duration() const { return duration_; }
void set_duration(uint32_t duration) { duration_ = duration; }
uint32_t nonce() const { return nonce_; }
const char* verification() const { return verification_; }
uint32_t control_bits() const { return control_bits_; }
void RequireLocalDisplay();
private:
uint32_t ExtractField(const std::vector<uint8_t>& str, int idx);
bool valid_;
char verification_[4];
uint32_t duration_;
uint32_t nonce_;
uint32_t control_bits_;
};
// AES-128 crypto key, or HMAC signing key.
class Key {
public:
Key(const Key& key)
: value_(key.value_), control_(key.control_), ctr_mode_(key.ctr_mode_) {}
Key(const std::vector<uint8_t>& key_string, const KeyControlBlock& control,
bool ctr_mode)
: value_(key_string), control_(control), ctr_mode_(ctr_mode){};
virtual ~Key(){};
void UpdateDuration(const KeyControlBlock& control);
const std::vector<uint8_t>& value() const { return value_; }
const KeyControlBlock& control() const { return control_; }
bool ctr_mode() const { return ctr_mode_; }
private:
std::vector<uint8_t> value_;
KeyControlBlock control_;
bool ctr_mode_;
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_KEY_MOCK_H_

View File

@@ -0,0 +1,110 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_keybox_mock.h"
#include <arpa/inet.h> // needed for ntoh()
#include <string.h>
#include <sys/types.h>
#include <string>
#include "log.h"
#include "wv_keybox.h"
#include "wvcrc32.h"
namespace wvoec_mock {
namespace {
const WidevineKeybox kTestKeybox = {
// Sample keybox used for test vectors
{
// deviceID
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
}, {
// key
0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e,
0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04,
}, {
// data
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1,
0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd,
0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8,
0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64,
0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8,
0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8,
0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64,
0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39,
}, {
// magic
0x6b, 0x62, 0x6f, 0x78,
}, {
// Crc
0x0a, 0x7a, 0x2c, 0x35,
}
};
} // namespace
WvKeybox::WvKeybox() {
valid_ = Prepare();
}
KeyboxError WvKeybox::Validate() {
if (!valid_) {
LOGE("[KEYBOX NOT LOADED]");
return OTHER_ERROR;
}
if (strncmp(reinterpret_cast<char*>(magic_), "kbox", 4) != 0) {
LOGE("[KEYBOX HAS BAD MAGIC]");
return BAD_MAGIC;
}
uint32_t crc_computed;
uint32_t* crc_stored = (uint32_t*)crc_;
WidevineKeybox keybox;
memset(&keybox, 0, sizeof(keybox));
memcpy(keybox.device_id_, &device_id_[0], device_id_.size());
memcpy(keybox.device_key_, &device_key_[0], sizeof(keybox.device_key_));
memcpy(keybox.data_, key_data_, sizeof(keybox.data_));
memcpy(keybox.magic_, magic_, sizeof(keybox.magic_));
crc_computed = ntohl(wvcrc32(reinterpret_cast<uint8_t*>(&keybox),
sizeof(keybox) - 4)); // Drop last 4 bytes.
if (crc_computed != *crc_stored) {
LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n",
crc_computed, *crc_stored);
return BAD_CRC;
}
return NO_ERROR;
}
bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) {
if (keyBoxLength != 128) {
return false;
}
const WidevineKeybox* keybox
= reinterpret_cast<const WidevineKeybox*>(buffer);
size_t device_id_length
= strnlen(reinterpret_cast<const char*>(keybox->device_id_), 32);
device_id_.assign(keybox->device_id_,
keybox->device_id_ + device_id_length);
device_key_.assign(keybox->device_key_,
keybox->device_key_ + sizeof(keybox->device_key_));
memcpy(key_data_, keybox->data_, sizeof(keybox->data_));
memcpy(magic_, keybox->magic_, sizeof(keybox->magic_));
memcpy(crc_, keybox->crc_, sizeof(keybox->crc_));
return true;
}
WvTestKeybox::WvTestKeybox() {
InstallKeybox(reinterpret_cast<const uint8_t*>(&kTestKeybox),
sizeof(kTestKeybox));
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,51 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_KEYBOX_MOCK_H_
#define OEMCRYPTO_KEYBOX_MOCK_H_
#include "oemcrypto_key_mock.h"
namespace wvoec_mock {
const int DEVICE_KEY_LENGTH = 16;
typedef uint8_t WvKeyboxKey[DEVICE_KEY_LENGTH];
const int KEY_DATA_LENGTH = 72;
typedef uint8_t WvKeyboxKeyData[KEY_DATA_LENGTH];
enum KeyboxError { NO_ERROR, BAD_CRC, BAD_MAGIC, OTHER_ERROR };
// Widevine keybox
class WvKeybox {
public:
WvKeybox();
~WvKeybox() {}
KeyboxError Validate();
const std::vector<uint8_t>& device_id() { return device_id_; }
std::vector<uint8_t>& device_key() { return device_key_; }
const WvKeyboxKeyData& key_data() { return key_data_; }
size_t key_data_length() { return KEY_DATA_LENGTH; }
bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength);
private:
bool Prepare();
bool valid_;
std::vector<uint8_t> device_id_;
std::vector<uint8_t> device_key_;
WvKeyboxKeyData key_data_;
uint8_t magic_[4];
uint8_t crc_[4];
};
class WvTestKeybox : public WvKeybox {
public:
WvTestKeybox();
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_KEYBOX_MOCK_H_

View File

@@ -0,0 +1,54 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Test keybox.
#include "oemcrypto_keybox_mock.h"
#include "wv_keybox.h"
namespace wvoec_mock {
namespace {
// Note: this is a valid keybox, but it is not accepted by production servers.
// However, it is different from the one used for most of the unit tests.
const WidevineKeybox kKeybox = {
// Sample keybox used for test vectors
{
// deviceID
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02
0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
}, {
// key
0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0,
0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6,
}, {
// data
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93,
0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4,
0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae,
0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13,
0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33,
0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa,
0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a,
0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80,
}, {
// magic
0x6b, 0x62, 0x6f, 0x78,
}, {
// Crc
0x2a, 0x3b, 0x3e, 0xe4,
}
};
} // namespace
bool WvKeybox::Prepare() {
InstallKeybox(reinterpret_cast<const uint8_t*>(&kKeybox),
sizeof(kKeybox));
return true;
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,104 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "oemcrypto_logging.h"
#include <stdio.h>
namespace wvoec_mock {
int logging_category_setting = 0x00;
void SetLoggingSettings(int level, int categories) {
SetLoggingLevel(level);
TurnOffLoggingForAllCategories();
AddLoggingForCategories(categories);
}
void TurnOffLoggingForAllCategories() { logging_category_setting = 0; }
void SetLoggingLevel(int level) {
wvcdm::g_cutoff = static_cast<wvcdm::LogPriority>(level);
}
void SetLoggingLevel(wvcdm::LogPriority level) { wvcdm::g_cutoff = level; }
void AddLoggingForCategories(int categories) {
logging_category_setting |= categories;
}
void RemoveLoggingForCategories(int categories) {
logging_category_setting &= ~categories;
}
bool LogCategoryEnabled(int categories) {
return ((logging_category_setting & categories) != 0);
}
void dump_hex_helper(std::string& buffer, std::string name,
const uint8_t* vector, size_t length) {
buffer += name + " = ";
if (vector == NULL) {
buffer += "NULL;\n";
LOGE(buffer.c_str());
return;
}
int a, b;
char int_to_hexcar[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
for (size_t i = 0; i < length; i++) {
if (i == 0) {
buffer += "\n wvcdm::a2b_hex(\"";
} else if (i % 32 == 0) {
buffer += "\"\n \"";
}
a = vector[i] % 16;
b = (vector[i] - a) / 16;
buffer += int_to_hexcar[b];
buffer += int_to_hexcar[a];
}
buffer += "\");\n";
}
void dump_hex(std::string name, const uint8_t* vector, size_t length) {
std::string buffer = "";
dump_hex_helper(buffer, name, vector, length);
LOGV(buffer.c_str());
}
void dump_array_part_helper(std::string& buffer, std::string array,
size_t index, std::string name,
const uint8_t* vector, size_t length) {
char index_str[256];
snprintf(index_str, sizeof index_str, "%zu", index);
if (vector == NULL) {
buffer += array.c_str();
buffer += "[";
buffer += index_str;
buffer += "].";
buffer += name.c_str();
buffer += " = NULL;\n";
LOGW(buffer.c_str());
return;
}
buffer += "std::string s";
buffer += index_str;
buffer += "_";
dump_hex_helper(buffer, name, vector, length);
buffer += array.c_str();
buffer += "[";
buffer += index_str;
buffer += "]." + name + " = message_ptr + message.find(s";
buffer += index_str;
buffer += "_" + name + ".data());\n";
}
void dump_array_part(std::string array, size_t index, std::string name,
const uint8_t* vector, size_t length) {
std::string buffer = "";
dump_array_part_helper(buffer, array, index, name, vector, length);
LOGV(buffer.c_str());
}
} // namespace wvoec_mock

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_nonce_table.h"
namespace wvoec_mock {
void NonceTable::AddNonce(uint32_t nonce) {
int new_slot = -1;
int oldest_slot = -1;
// Flush any nonces that have been checked but not flushed.
// After flush, nonces will be either valid or invalid.
Flush();
for (int i = 0; i < kTableSize; ++i) {
// Increase age of all valid nonces.
if (kNTStateValid == state_[i]) {
++age_[i];
if (-1 == oldest_slot) {
oldest_slot = i;
} else {
if (age_[i] > age_[oldest_slot]) {
oldest_slot = i;
}
}
} else {
if (-1 == new_slot) {
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
new_slot = i;
}
}
}
if (-1 == new_slot) {
// reuse oldest
// assert (oldest_slot != -1)
int i = oldest_slot;
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
}
}
bool NonceTable::CheckNonce(uint32_t nonce) {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateInvalid != state_[i]) {
if (nonce == nonces_[i]) {
state_[i] = kNTStateFlushPending;
return true;
}
}
}
return false;
}
void NonceTable::Flush() {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateFlushPending == state_[i]) {
state_[i] = kNTStateInvalid;
}
}
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,38 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef MOCK_OEMCRYPTO_NONCE_TABLE_H_
#define MOCK_OEMCRYPTO_NONCE_TABLE_H_
#include <stdint.h>
namespace wvoec_mock {
class NonceTable {
public:
static const int kTableSize = 16;
NonceTable() {
for (int i = 0; i < kTableSize; ++i) {
state_[i] = kNTStateInvalid;
}
}
~NonceTable() {}
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
void Flush();
private:
enum NonceTableState {
kNTStateInvalid,
kNTStateValid,
kNTStateFlushPending
};
NonceTableState state_[kTableSize];
uint32_t age_[kTableSize];
uint32_t nonces_[kTableSize];
};
} // namespace wvoec_mock
#endif // MOCK_OEMCRYPTO_NONCE_TABLE_H_

View File

@@ -0,0 +1,240 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This is from the v12 version of oemcrypto usage tables. It is used for
// devices that upgrade from v12 to v13 in the field, and need to convert from
// the old type of usage table to the new.
#include "oemcrypto_old_usage_table_mock.h"
#include <string.h>
#include <time.h>
#include <string>
#include <vector>
#include <openssl/aes.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "file_store.h"
#include "log.h"
#include "oemcrypto_engine_mock.h"
#include "oemcrypto_logging.h"
#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
OldUsageTableEntry::OldUsageTableEntry(const std::vector<uint8_t> &pst_hash)
: pst_hash_(pst_hash),
time_of_license_received_(time(NULL)),
time_of_first_decrypt_(0),
time_of_last_decrypt_(0),
status_(kUnused) {}
OldUsageTableEntry::~OldUsageTableEntry() {}
OldUsageTableEntry::OldUsageTableEntry(const OldStoredUsageEntry *buffer) {
pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH);
time_of_license_received_ = buffer->time_of_license_received;
time_of_first_decrypt_ = buffer->time_of_first_decrypt;
time_of_last_decrypt_ = buffer->time_of_last_decrypt;
status_ = buffer->status;
mac_key_server_.assign(buffer->mac_key_server,
buffer->mac_key_server + wvcdm::MAC_KEY_SIZE);
mac_key_client_.assign(buffer->mac_key_client,
buffer->mac_key_client + wvcdm::MAC_KEY_SIZE);
}
OldUsageTable::OldUsageTable(CryptoEngine *ce) {
ce_ = ce;
generation_ = 0;
table_.clear();
// Load saved table.
wvcdm::FileSystem *file_system = ce->file_system();
wvcdm::File *file;
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGE("OldUsageTable: Unable to get base path");
return;
}
std::string filename = path + "UsageTable.dat";
if (!file_system->Exists(filename)) {
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
LOGI("OldUsageTable: No saved usage table. Creating new table.");
}
return;
}
size_t file_size = file_system->FileSize(filename);
std::vector<uint8_t> encrypted_buffer(file_size);
std::vector<uint8_t> buffer(file_size);
OldStoredUsageTable *stored_table =
reinterpret_cast<OldStoredUsageTable *>(&buffer[0]);
OldStoredUsageTable *encrypted_table =
reinterpret_cast<OldStoredUsageTable *>(&encrypted_buffer[0]);
file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("OldUsageTable: File open failed: %s", path.c_str());
return;
}
file->Read(reinterpret_cast<char *>(&encrypted_buffer[0]), file_size);
file->Close();
// Verify the signature of the usage table file.
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const bool override_to_real = true;
const std::vector<uint8_t> &key = ce_->DeviceRootKey(override_to_real);
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
unsigned int sig_length = sizeof(computed_signature);
if (!HMAC(EVP_sha256(), &key[0], key.size(),
&encrypted_buffer[SHA256_DIGEST_LENGTH],
file_size - SHA256_DIGEST_LENGTH, computed_signature,
&sig_length)) {
LOGE("OldUsageTable: Could not recreate signature.");
table_.clear();
return;
}
if (memcmp(encrypted_table->signature, computed_signature, sig_length)) {
LOGE("OldUsageTable: Invalid signature given: %s",
wvcdm::HexEncode(&encrypted_buffer[0], sig_length).c_str());
LOGE("OldUsageTable: Invalid signature computed: %s",
wvcdm::HexEncode(computed_signature, sig_length).c_str());
table_.clear();
return;
}
// Next, decrypt the table.
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE],
&buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE],
file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE,
&aes_key, iv_buffer, AES_DECRYPT);
// Next, read the generation number from a different location.
// On a real implementation, you should NOT put the generation number in
// a file in user space. It should be stored in secure memory. For the
// reference implementation, we'll just pretend this is secure.
std::string filename2 = path + "GenerationNumber.dat";
file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("OldUsageTable: File open failed: %s (clearing table)", path.c_str());
generation_ = 0;
table_.clear();
return;
}
file->Read(reinterpret_cast<char *>(&generation_), sizeof(int64_t));
file->Close();
if (stored_table->generation == generation_ + 1) {
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
LOGW("OldUsageTable: File is one generation old. Acceptable rollback.");
}
} else if (stored_table->generation == generation_ - 1) {
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
LOGW("OldUsageTable: File is one generation new. Acceptable rollback.");
}
// This might happen if the generation number was rolled back?
} else if (stored_table->generation != generation_) {
LOGE("OldUsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx",
generation_, stored_table->generation);
table_.clear();
generation_ = 0;
return;
}
// At this point, the stored table looks valid. We can load in all the
// entries.
for (uint64_t i = 0; i < stored_table->count; i++) {
OldUsageTableEntry *entry =
new OldUsageTableEntry(&stored_table->entries[i].entry);
table_[entry->pst_hash()] = entry;
}
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
LOGI("OldUsageTable: loaded %d entries.", stored_table->count);
}
}
OldUsageTableEntry *OldUsageTable::FindEntry(const std::vector<uint8_t> &pst) {
wvcdm::AutoLock lock(lock_);
return FindEntryLocked(pst);
}
OldUsageTableEntry *OldUsageTable::FindEntryLocked(const std::vector<uint8_t> &pst) {
std::vector<uint8_t> pst_hash;
if (!ComputeHash(pst, pst_hash)) {
LOGE("OldUsageTable: Could not compute hash of pst.");
return NULL;
}
EntryMap::iterator it = table_.find(pst_hash);
if (it == table_.end()) {
return NULL;
}
return it->second;
}
OldUsageTableEntry *OldUsageTable::CreateEntry(const std::vector<uint8_t> &pst) {
std::vector<uint8_t> pst_hash;
if (!ComputeHash(pst, pst_hash)) {
LOGE("OldUsageTable: Could not compute hash of pst.");
return NULL;
}
OldUsageTableEntry *entry = new OldUsageTableEntry(pst_hash);
wvcdm::AutoLock lock(lock_);
table_[pst_hash] = entry;
return entry;
}
void OldUsageTable::Clear() {
wvcdm::AutoLock lock(lock_);
for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) {
if (i->second) delete i->second;
}
table_.clear();
}
void OldUsageTable::DeleteFile(CryptoEngine *ce) {
wvcdm::FileSystem *file_system = ce->file_system();
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGE("OldUsageTable: Unable to get base path");
return;
}
std::string filename = path + "UsageTable.dat";
if (file_system->Exists(filename)) {
if (!file_system->Remove(filename)) {
LOGE("DeleteOldUsageTable: error removing file.");
}
}
}
bool OldUsageTable::ComputeHash(const std::vector<uint8_t> &pst,
std::vector<uint8_t> &pst_hash) {
// The PST is not fixed size, and we have no promises that it is reasonbly
// sized, so we compute a hash of it, and store that instead.
pst_hash.resize(SHA256_DIGEST_LENGTH);
SHA256_CTX context;
if (!SHA256_Init(&context)) return false;
if (!SHA256_Update(&context, &pst[0], pst.size())) return false;
if (!SHA256_Final(&pst_hash[0], &context)) return false;
return true;
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,93 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
// This is from the v12 version of oemcrypto usage tables. It is used for
// devices that upgrade from v12 to v13 in the field, and need to convert from
// the old type of usage table to the new.
#ifndef OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_
#define OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include "lock.h"
#include "OEMCryptoCENC.h"
#include "openssl/sha.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
class CryptoEngine;
class UsagetTableEntry;
struct OldStoredUsageEntry {
// To save disk space, we only store a hash of the pst.
uint8_t pst_hash[SHA256_DIGEST_LENGTH];
int64_t time_of_license_received;
int64_t time_of_first_decrypt;
int64_t time_of_last_decrypt;
enum OEMCrypto_Usage_Entry_Status status;
uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE];
uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE];
};
typedef union {
struct OldStoredUsageEntry entry;
uint8_t padding[128]; // multiple of block size and bigger than entry size.
} AlignedOldStoredUsageEntry;
struct OldStoredUsageTable {
uint8_t signature[SHA256_DIGEST_LENGTH];
uint8_t iv[wvcdm::KEY_IV_SIZE];
int64_t generation;
uint64_t count;
AlignedOldStoredUsageEntry entries[];
};
class OldUsageTableEntry {
public:
OldUsageTableEntry(const std::vector<uint8_t> &pst_hash);
OldUsageTableEntry(const OldStoredUsageEntry *buffer);
~OldUsageTableEntry();
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
private:
std::vector<uint8_t> pst_hash_;
int64_t time_of_license_received_;
int64_t time_of_first_decrypt_;
int64_t time_of_last_decrypt_;
enum OEMCrypto_Usage_Entry_Status status_;
std::vector<uint8_t> mac_key_server_;
std::vector<uint8_t> mac_key_client_;
friend class UsageTableEntry;
friend class UsageTable;
};
class OldUsageTable {
public:
OldUsageTable(CryptoEngine *ce);
~OldUsageTable() { Clear(); }
OldUsageTableEntry *FindEntry(const std::vector<uint8_t> &pst);
OldUsageTableEntry *CreateEntry(const std::vector<uint8_t> &pst);
void Clear();
static void DeleteFile(CryptoEngine *ce);
private:
OldUsageTableEntry *FindEntryLocked(const std::vector<uint8_t> &pst);
bool ComputeHash(const std::vector<uint8_t> &pst,
std::vector<uint8_t> &pst_hash);
typedef std::map<std::vector<uint8_t>, OldUsageTableEntry *> EntryMap;
EntryMap table_;
wvcdm::Lock lock_;
int64_t generation_;
CryptoEngine *ce_;
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_

View File

@@ -0,0 +1,94 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_rsa_key_shared.h"
#include <assert.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "oemcrypto_logging.h"
namespace {
void dump_openssl_error() {
while (unsigned long err = ERR_get_error()) {
char buffer[120];
LOGE("openssl error -- %lu -- %s",
err, ERR_error_string(err, buffer));
}
}
} // namespace
namespace wvoec_mock {
void RSA_shared_ptr::reset() {
if (rsa_key_ && key_owned_) {
RSA_free(rsa_key_);
}
key_owned_ = false;
rsa_key_ = NULL;
}
bool RSA_shared_ptr::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) {
assert(buffer != NULL);
reset();
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;
}
key_owned_ = true;
}
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;
}
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,37 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_RSA_KEY_SHARED_H_
#define OEMCRYPTO_RSA_KEY_SHARED_H_
#include <stdint.h>
#include <openssl/rsa.h>
namespace wvoec_mock {
// Shared pointer with specialized destructor. This pointer is only shared
// from a CryptoEngine to a Session -- so we don't have to use full reference
// counting.
class RSA_shared_ptr {
public:
RSA_shared_ptr() : rsa_key_(NULL), key_owned_(false) {}
~RSA_shared_ptr() { reset(); };
// Explicitly allow copy as share.
explicit RSA_shared_ptr(const RSA_shared_ptr& other) :
rsa_key_(other.rsa_key_), key_owned_(false) {}
RSA* get() { return rsa_key_; }
void reset();
bool LoadPkcs8RsaKey(const uint8_t* buffer, size_t length);
private:
void operator=(const RSA_shared_ptr); // disallow assign.
RSA* rsa_key_;
bool key_owned_;
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_RSA_KEY_SHARED_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef MOCK_OEMCRYPTO_SESSION_H_
#define MOCK_OEMCRYPTO_SESSION_H_
#include <stdint.h>
#include <time.h>
#include <map>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCENC.h" // Needed for enums only.
#include "file_store.h"
#include "lock.h"
#include "oemcrypto_auth_mock.h"
#include "oemcrypto_key_mock.h"
#include "oemcrypto_nonce_table.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_usage_table_mock.h"
#include "wv_cdm_types.h"
namespace wvoec_mock {
class CryptoEngine;
typedef uint32_t SessionId;
enum SRMVersionStatus { NoSRMVersion, ValidSRMVersion, InvalidSRMVersion };
class SessionContext {
private:
SessionContext() {}
public:
SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key)
: valid_(true),
ce_(ce),
id_(sid),
current_content_key_(NULL),
rsa_key_(rsa_key),
allowed_schemes_(kSign_RSASSA_PSS),
usage_entry_(NULL),
srm_requirements_status_(NoSRMVersion),
usage_entry_status_(kNoUsageEntry) {}
~SessionContext();
bool isValid() { return valid_; }
bool DeriveKeys(const std::vector<uint8_t>& master_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
bool GenerateSignature(const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
size_t RSASignatureSize();
OEMCryptoResult GenerateRSASignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length,
RSA_Padding_Scheme padding_scheme);
bool ValidateMessage(const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length);
OEMCryptoResult DecryptCENC(const uint8_t* iv, size_t block_offset,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
size_t cipher_data_length, bool is_encrypted,
uint8_t* clear_data,
OEMCryptoBufferType buffer_type);
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Decrypt(const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Sign(const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature, size_t* signature_length);
OEMCryptoResult Generic_Verify(const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
void StartTimer();
uint32_t CurrentTimer(); // (seconds).
OEMCryptoResult LoadKeys(const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_key_iv,
const uint8_t* enc_mac_keys, size_t num_keys,
const OEMCrypto_KeyObject* key_array,
const uint8_t* pst, size_t pst_length,
const uint8_t* srm_requirement);
OEMCryptoResult InstallKey(const KeyId& key_id,
const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv,
bool ctr_mode, bool second_license);
bool InstallRSAEncryptedKey(const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length);
bool DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* wrapped_rsa_key_iv, uint8_t* pkcs8_rsa_key);
bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key);
bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length);
OEMCryptoResult RefreshKey(const KeyId& key_id,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv);
bool UpdateMacKeys(const std::vector<uint8_t>& mac_keys,
const std::vector<uint8_t>& iv);
bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data);
OEMCryptoResult SelectContentKey(const KeyId& key_id);
const Key* current_content_key(void) { return current_content_key_; }
void set_mac_key_server(const std::vector<uint8_t>& mac_key_server) {
mac_key_server_ = mac_key_server;
}
const std::vector<uint8_t>& mac_key_server() { return mac_key_server_; }
void set_mac_key_client(const std::vector<uint8_t>& mac_key_client) {
mac_key_client_ = mac_key_client;
}
const std::vector<uint8_t>& mac_key_client() { return mac_key_client_; }
void set_encryption_key(const std::vector<uint8_t>& enc_key) {
encryption_key_ = enc_key;
}
const std::vector<uint8_t>& encryption_key() { return encryption_key_; }
uint32_t allowed_schemes() const { return allowed_schemes_; }
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
void FlushNonces();
OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number);
OEMCryptoResult LoadUsageEntry(uint32_t index,
const std::vector<uint8_t>& buffer);
OEMCryptoResult UpdateUsageEntry(uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
OEMCryptoResult DeactivateUsageEntry(const std::vector<uint8_t>& pst);
OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst, uint8_t* buffer,
size_t* buffer_length);
OEMCryptoResult MoveEntry(uint32_t new_index);
OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
private:
bool DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context, int counter,
std::vector<uint8_t>* out);
bool DecryptMessage(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& message,
std::vector<uint8_t>* decrypted);
// Either verify the nonce or usage entry, as required by the key control
// block.
OEMCryptoResult CheckNonceOrEntry(const KeyControlBlock& key_control_block);
// If there is a usage entry, check that it is not inactive.
// It also updates the status of the entry if needed.
bool CheckUsageEntry();
// Check that the usage entry status is valid for online use.
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
// Check that the usage entry status is valid for offline use.
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
size_t cipher_data_length, uint8_t* clear_data);
OEMCryptoResult PatternDecryptCTR(
const uint8_t* key, const uint8_t* iv, size_t block_offset,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data, size_t cipher_data_length,
uint8_t* clear_data);
OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv,
size_t block_offset, const uint8_t* cipher_data,
size_t cipher_data_length, uint8_t* clear_data);
// Checks if the key is allowed for the specified type. If there is a usage
// entry, it also checks the usage entry.
OEMCryptoResult CheckKeyUse(const std::string& log_string, uint32_t use_type,
OEMCryptoBufferType buffer_type);
RSA* rsa_key() { return rsa_key_.get(); }
bool valid_;
CryptoEngine* ce_;
SessionId id_;
std::vector<uint8_t> mac_key_server_;
std::vector<uint8_t> mac_key_client_;
std::vector<uint8_t> encryption_key_;
std::vector<uint8_t> session_key_;
const Key* current_content_key_;
SessionKeyTable session_keys_;
NonceTable nonce_table_;
RSA_shared_ptr rsa_key_;
uint32_t allowed_schemes_; // for RSA signatures.
time_t timer_start_;
UsageTableEntry* usage_entry_;
SRMVersionStatus srm_requirements_status_;
enum UsageEntryStatus {
kNoUsageEntry, // No entry loaded for this session.
kUsageEntryNew, // After entry was created.
kUsageEntryLoaded, // After loading entry or loading keys.
};
UsageEntryStatus usage_entry_status_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
};
} // namespace wvoec_mock
#endif // MOCK_OEMCRYPTO_SESSION_H_

View File

@@ -0,0 +1,46 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_session_key_table.h"
#include "keys.h"
#include "log.h"
namespace wvoec_mock {
SessionKeyTable::~SessionKeyTable() {
for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) {
if (NULL != i->second) {
delete i->second;
}
}
}
bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) {
if (keys_.find(key_id) != keys_.end()) return false;
keys_[key_id] = new Key(key_data);
return true;
}
Key* SessionKeyTable::Find(const KeyId key_id) {
if (keys_.find(key_id) == keys_.end()) {
return NULL;
}
return keys_[key_id];
}
void SessionKeyTable::Remove(const KeyId key_id) {
if (keys_.find(key_id) != keys_.end()) {
delete keys_[key_id];
keys_.erase(key_id);
}
}
void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) {
it->second->UpdateDuration(control);
}
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,45 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_
#define MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_
#include <stdint.h>
#include <map>
#include <vector>
#include "oemcrypto_key_mock.h"
#include "wv_cdm_types.h"
namespace wvoec_mock {
class SessionContext;
class CryptoEngine;
class UsageTable;
class UsageTableEntry;
typedef std::vector<uint8_t> KeyId;
typedef std::map<KeyId, Key*> KeyMap;
// SessionKeyTable holds the keys for the current session
class SessionKeyTable {
public:
SessionKeyTable() {}
~SessionKeyTable();
bool Insert(const KeyId key_id, const Key& key_data);
Key* Find(const KeyId key_id);
void Remove(const KeyId key_id);
void UpdateDuration(const KeyControlBlock& control);
size_t size() const { return keys_.size(); }
private:
KeyMap keys_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable);
};
} // namespace wvoec_mock
#endif // MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_

View File

@@ -0,0 +1,735 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#include "oemcrypto_usage_table_mock.h"
#include <string.h>
#include <time.h>
#include <string>
#include <vector>
#include <openssl/aes.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "file_store.h"
#include "log.h"
#include "oemcrypto_engine_mock.h"
#include "oemcrypto_logging.h"
#include "oemcrypto_old_usage_table_mock.h"
#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
namespace {
const size_t kMagicLength = 8;
const char* kEntryVerification = "USEENTRY";
const char* kHeaderVerification = "USEHEADR";
// Offset into a signed block where we start encrypting. We need to
// skip the signature and the iv.
const size_t kEncryptionOffset = SHA256_DIGEST_LENGTH + SHA256_DIGEST_LENGTH;
// A structure that holds an usage entry and its signature.
struct SignedEntryBlock {
uint8_t signature[SHA256_DIGEST_LENGTH];
uint8_t iv[SHA256_DIGEST_LENGTH];
uint8_t verification[kMagicLength];
StoredUsageEntry data;
};
// This has the data in the header of constant size. There is also an array
// of generation numbers.
struct SignedHeaderBlock {
uint8_t signature[SHA256_DIGEST_LENGTH];
uint8_t iv[SHA256_DIGEST_LENGTH];
uint8_t verification[kMagicLength];
int64_t master_generation;
uint64_t count;
};
} // namespace
UsageTableEntry::UsageTableEntry(UsageTable* table, uint32_t index,
int64_t generation)
: usage_table_(table), recent_decrypt_(false), forbid_report_(true) {
memset(&data_, 0, sizeof(data_));
data_.generation_number = generation;
data_.index = index;
}
UsageTableEntry::~UsageTableEntry() { usage_table_->ReleaseEntry(data_.index); }
OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) {
if (pst_length > kMaxPSTLength) return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
data_.pst_length = pst_length;
if (!pst) return OEMCrypto_ERROR_INVALID_CONTEXT;
memcpy(data_.pst, pst, pst_length);
data_.time_of_license_received = time(NULL);
return OEMCrypto_SUCCESS;
}
bool UsageTableEntry::VerifyPST(const uint8_t* pst, size_t pst_length) {
if (pst_length > kMaxPSTLength) return false;
if (data_.pst_length != pst_length) return false;
if (!pst) return false;
return 0 == memcmp(pst, data_.pst, pst_length);
}
bool UsageTableEntry::VerifyMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client) {
return (server.size() == wvcdm::MAC_KEY_SIZE) &&
(client.size() == wvcdm::MAC_KEY_SIZE) &&
(0 == memcmp(&server[0], data_.mac_key_server, wvcdm::MAC_KEY_SIZE)) &&
(0 == memcmp(&client[0], data_.mac_key_client, wvcdm::MAC_KEY_SIZE));
}
bool UsageTableEntry::SetMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client) {
if ((server.size() != wvcdm::MAC_KEY_SIZE) ||
(client.size() != wvcdm::MAC_KEY_SIZE))
return false;
memcpy(data_.mac_key_server, &server[0], wvcdm::MAC_KEY_SIZE);
memcpy(data_.mac_key_client, &client[0], wvcdm::MAC_KEY_SIZE);
return true;
}
bool UsageTableEntry::CheckForUse() {
if (Inactive()) return false;
recent_decrypt_ = true;
if (data_.status == kUnused) {
data_.status = kActive;
data_.time_of_first_decrypt = time(NULL);
data_.generation_number++;
usage_table_->IncrementGeneration();
}
return true;
}
void UsageTableEntry::Deactivate(const std::vector<uint8_t>& pst) {
if (data_.status == kUnused) {
data_.status = kInactiveUnused;
} else if (data_.status == kActive) {
data_.status = kInactiveUsed;
}
forbid_report_ = true;
data_.generation_number++;
usage_table_->IncrementGeneration();
}
OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer,
size_t* buffer_length) {
if (forbid_report_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE;
if (recent_decrypt_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE;
if (pst.size() == 0 || pst.size() > kMaxPSTLength ||
pst.size() != data_.pst_length) {
LOGE("ReportUsage: bad pst length = %d, should be %d.",
pst.size(), data_.pst_length);
return OEMCrypto_ERROR_WRONG_PST;
}
if (memcmp(&pst[0], data_.pst, data_.pst_length)) {
LOGE("ReportUsage: wrong pst %s, should be %s.",
wvcdm::b2a_hex(pst).c_str(),
wvcdm::HexEncode(data_.pst, data_.pst_length).c_str());
return OEMCrypto_ERROR_WRONG_PST;
}
size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size());
if (*buffer_length < length_needed) {
*buffer_length = length_needed;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (!buffer) {
LOGE("ReportUsage: buffer was null pointer.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
wvcdm::Unpacked_PST_Report pst_report(buffer);
int64_t now = time(NULL);
pst_report.set_seconds_since_license_received(now -
data_.time_of_license_received);
pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt);
pst_report.set_seconds_since_last_decrypt(now - data_.time_of_last_decrypt);
pst_report.set_status(data_.status);
pst_report.set_clock_security_level(kSecureTimer);
pst_report.set_pst_length(data_.pst_length);
memcpy(pst_report.pst(), data_.pst, data_.pst_length);
unsigned int md_len = SHA_DIGEST_LENGTH;
if (!HMAC(EVP_sha1(), data_.mac_key_client, wvcdm::MAC_KEY_SIZE,
buffer + SHA_DIGEST_LENGTH, length_needed - SHA_DIGEST_LENGTH,
pst_report.signature(), &md_len)) {
LOGE("ReportUsage: could not compute signature.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
void UsageTableEntry::UpdateAndIncrement() {
if (recent_decrypt_) {
data_.time_of_last_decrypt = time(NULL);
recent_decrypt_ = false;
}
data_.generation_number++;
usage_table_->IncrementGeneration();
forbid_report_ = false;
}
OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce,
SessionContext* session,
uint8_t* signed_buffer,
size_t buffer_size) {
// buffer_size was determined by calling function.
if (buffer_size != SignedEntrySize()) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
std::vector<uint8_t> clear_buffer(buffer_size);
memset(&clear_buffer[0], 0, buffer_size);
memset(signed_buffer, 0, buffer_size);
SignedEntryBlock* clear =
reinterpret_cast<SignedEntryBlock*>(&clear_buffer[0]);
SignedEntryBlock* encrypted =
reinterpret_cast<SignedEntryBlock*>(signed_buffer);
clear->data = this->data_; // Copy the current data.
memcpy(clear->verification, kEntryVerification, kMagicLength);
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const bool override_to_real = true;
const std::vector<uint8_t>& key = ce->DeviceRootKey(override_to_real);
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE);
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(
&clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset],
buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT);
// Sign the entry.
unsigned int sig_length = SHA256_DIGEST_LENGTH;
if (!HMAC(EVP_sha256(), &key[0], key.size(),
&signed_buffer[SHA256_DIGEST_LENGTH],
buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature,
&sig_length)) {
LOGE("SaveUsageEntry: Could not sign entry.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer) {
if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER;
if (buffer.size() > SignedEntrySize())
LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(),
SignedEntrySize());
std::vector<uint8_t> clear_buffer(buffer.size());
SignedEntryBlock* clear =
reinterpret_cast<SignedEntryBlock*>(&clear_buffer[0]);
const SignedEntryBlock* encrypted =
reinterpret_cast<const SignedEntryBlock*>(&buffer[0]);
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const bool override_to_real = true;
const std::vector<uint8_t>& key = ce->DeviceRootKey(override_to_real);
// Verify the signature of the usage entry. Sign encrypted into clear buffer.
unsigned int sig_length = SHA256_DIGEST_LENGTH;
if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH],
buffer.size() - SHA256_DIGEST_LENGTH, clear->signature,
&sig_length)) {
LOGE("LoadUsageEntry: Could not sign entry.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) {
LOGE("LoadUsageEntry: Signature did not match.");
LOGE("LoadUsageEntry: Invalid signature given: %s",
wvcdm::HexEncode(encrypted->signature, sig_length).c_str());
LOGE("LoadUsageEntry: Invalid signature computed: %s",
wvcdm::HexEncode(clear->signature, sig_length).c_str());
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Next, decrypt the entry.
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset],
buffer.size() - kEncryptionOffset, &aes_key, iv_buffer,
AES_DECRYPT);
// Check the verification string is correct.
if (memcmp(kEntryVerification, clear->verification, kMagicLength)) {
LOGE("LoadUsageEntry: Invalid magic: %s=%8.8s expected: %s=%8.8s",
wvcdm::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvcdm::HexEncode(reinterpret_cast<const uint8_t*>(kEntryVerification),
kMagicLength).c_str(),
reinterpret_cast<const uint8_t*>(kEntryVerification));
return OEMCrypto_ERROR_BAD_MAGIC;
}
// Check that the index is correct.
if (index != clear->data.index) {
LOGE("LoadUsageEntry: entry says index is %d, not %d", clear->data.index,
index);
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (clear->data.status > kInactiveUnused) {
LOGE("LoadUsageEntry: entry has bad status %d", clear->data.status);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
this->data_ = clear->data;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTableEntry::CopyOldUsageEntry(
const std::vector<uint8_t>& pst) {
OldUsageTableEntry* old_entry = usage_table_->FindOldUsageEntry(pst);
if (!old_entry) return OEMCrypto_ERROR_WRONG_PST;
data_.time_of_license_received = old_entry->time_of_license_received_;
data_.time_of_first_decrypt = old_entry->time_of_first_decrypt_;
data_.time_of_last_decrypt = old_entry->time_of_last_decrypt_;
data_.status = old_entry->status_;
if (old_entry->mac_key_server_.size() != wvcdm::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad server mac key.");
} else {
memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]),
wvcdm::MAC_KEY_SIZE);
}
if (old_entry->mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad client mac key.");
} else {
memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]),
wvcdm::MAC_KEY_SIZE);
}
if (pst.size() > kMaxPSTLength) {
LOGE("CopyOldEntry: PST Length was too large. Truncating.");
data_.pst_length = kMaxPSTLength;
} else {
data_.pst_length = pst.size();
}
memcpy(data_.pst, &pst[0], wvcdm::MAC_KEY_SIZE);
return OEMCrypto_SUCCESS;
}
size_t UsageTableEntry::SignedEntrySize() {
size_t base = sizeof(SignedEntryBlock);
// round up to make even number of blocks:
size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1;
return blocks * wvcdm::KEY_IV_SIZE;
}
UsageTable::~UsageTable() {
if (old_table_) {
delete old_table_;
old_table_ = NULL;
}
}
size_t UsageTable::SignedHeaderSize(size_t count) {
size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t);
// round up to make even number of blocks:
size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1;
return blocks * wvcdm::KEY_IV_SIZE;
}
OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session,
UsageTableEntry* entry,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length) {
size_t signed_header_size = SignedHeaderSize(generation_numbers_.size());
if (*entry_buffer_length < UsageTableEntry::SignedEntrySize() ||
*header_buffer_length < signed_header_size) {
*entry_buffer_length = UsageTableEntry::SignedEntrySize();
*header_buffer_length = signed_header_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*entry_buffer_length = UsageTableEntry::SignedEntrySize();
*header_buffer_length = signed_header_size;
if ((!header_buffer) || (!entry_buffer))
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
entry->UpdateAndIncrement();
generation_numbers_[entry->index()] = entry->generation_number();
OEMCryptoResult result =
entry->SaveData(ce_, session, entry_buffer, *entry_buffer_length);
if (result != OEMCrypto_SUCCESS) return result;
result = SaveUsageTableHeader(header_buffer, *header_buffer_length);
return result;
}
OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t* usage_entry_number) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (!usage_entry_number) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
uint32_t index = generation_numbers_.size();
UsageTableEntry* new_entry =
new UsageTableEntry(this, index, master_generation_number_);
generation_numbers_.push_back(master_generation_number_);
sessions_.push_back(session);
master_generation_number_++;
*entry = new_entry;
*usage_entry_number = index;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t index,
const std::vector<uint8_t>& buffer) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (index >= generation_numbers_.size())
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (sessions_[index]) {
LOGE("LoadUsageEntry: index %d used by other session.", index);
return OEMCrypto_ERROR_INVALID_SESSION;
}
UsageTableEntry* new_entry =
new UsageTableEntry(this, index, master_generation_number_);
OEMCryptoResult status = new_entry->LoadData(ce_, index, buffer);
if (status != OEMCrypto_SUCCESS) {
delete new_entry;
return status;
}
if (new_entry->generation_number() != generation_numbers_[index]) {
LOGE("Generation SKEW: %ld -> %ld", new_entry->generation_number(),
generation_numbers_[index]);
if ((new_entry->generation_number() + 1 < generation_numbers_[index]) ||
(new_entry->generation_number() - 1 > generation_numbers_[index])) {
delete new_entry;
return OEMCrypto_ERROR_GENERATION_SKEW;
}
status = OEMCrypto_WARNING_GENERATION_SKEW;
}
sessions_[index] = session;
*entry = new_entry;
return status;
}
OEMCryptoResult UsageTable::ShrinkUsageTableHeader(
uint32_t new_table_size, uint8_t* header_buffer,
size_t* header_buffer_length) {
if (new_table_size > generation_numbers_.size()) {
LOGE("OEMCrypto_ShrinkUsageTableHeader: %d > %zd.", new_table_size,
generation_numbers_.size());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t signed_header_size = SignedHeaderSize(new_table_size);
if (*header_buffer_length < signed_header_size) {
*header_buffer_length = signed_header_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*header_buffer_length = signed_header_size;
if (!header_buffer) {
LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
for (size_t i = new_table_size; i < sessions_.size(); ++i) {
if (sessions_[i]) {
LOGE("ShrinkUsageTableHeader: session open for %d", i);
return OEMCrypto_ERROR_ENTRY_IN_USE;
}
}
generation_numbers_.resize(new_table_size);
sessions_.resize(new_table_size);
master_generation_number_++;
return SaveUsageTableHeader(header_buffer, *header_buffer_length);
}
OEMCryptoResult UsageTable::SaveUsageTableHeader(uint8_t* signed_buffer,
size_t buffer_size) {
if (!SaveGenerationNumber()) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
size_t count = generation_numbers_.size();
// buffer_size was determined by calling function.
if (buffer_size != SignedHeaderSize(count))
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
std::vector<uint8_t> clear_buffer(buffer_size);
memset(&clear_buffer[0], 0, buffer_size);
memset(signed_buffer, 0, buffer_size);
SignedHeaderBlock* clear =
reinterpret_cast<SignedHeaderBlock*>(&clear_buffer[0]);
SignedHeaderBlock* encrypted =
reinterpret_cast<SignedHeaderBlock*>(signed_buffer);
// Pack the clear data into the clear buffer.
memcpy(clear->verification, kHeaderVerification, kMagicLength);
clear->master_generation = master_generation_number_;
clear->count = count;
// This points to the variable size part of the buffer.
int64_t* stored_generations =
reinterpret_cast<int64_t*>(&clear_buffer[sizeof(SignedHeaderBlock)]);
std::copy(generation_numbers_.begin(), generation_numbers_.begin() + count,
stored_generations);
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const bool override_to_real = true;
const std::vector<uint8_t>& key = ce_->DeviceRootKey(override_to_real);
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE);
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(
&clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset],
buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT);
// Sign the entry.
unsigned int sig_length = SHA256_DIGEST_LENGTH;
if (!HMAC(EVP_sha256(), &key[0], key.size(),
&signed_buffer[SHA256_DIGEST_LENGTH],
buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature,
&sig_length)) {
LOGE("SaveUsageHeader: Could not sign entry.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::LoadUsageTableHeader(
const std::vector<uint8_t>& buffer) {
if (!LoadGenerationNumber(false)) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (buffer.size() < SignedHeaderSize(0)) return OEMCrypto_ERROR_SHORT_BUFFER;
std::vector<uint8_t> clear_buffer(buffer.size());
SignedHeaderBlock* clear =
reinterpret_cast<SignedHeaderBlock*>(&clear_buffer[0]);
const SignedHeaderBlock* encrypted =
reinterpret_cast<const SignedHeaderBlock*>(&buffer[0]);
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const bool override_to_real = true;
const std::vector<uint8_t>& key = ce_->DeviceRootKey(override_to_real);
// Verify the signature of the usage entry. Sign encrypted into clear buffer.
unsigned int sig_length = SHA256_DIGEST_LENGTH;
if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH],
buffer.size() - SHA256_DIGEST_LENGTH, clear->signature,
&sig_length)) {
LOGE("LoadUsageTableHeader: Could not sign entry.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) {
LOGE("LoadUsageTableHeader: Signature did not match.");
LOGE("LoadUsageTableHeader: Invalid signature given: %s",
wvcdm::HexEncode(encrypted->signature, sig_length).c_str());
LOGE("LoadUsageTableHeader: Invalid signature computed: %s",
wvcdm::HexEncode(clear->signature, sig_length).c_str());
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Next, decrypt the entry.
uint8_t iv_buffer[wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset],
buffer.size() - kEncryptionOffset, &aes_key, iv_buffer,
AES_DECRYPT);
// Check the verification string is correct.
if (memcmp(kHeaderVerification, clear->verification, kMagicLength)) {
LOGE("LoadUsageTableHeader: Invalid magic: %s=%8.8s expected: %s=%8.8s",
wvcdm::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvcdm::HexEncode(reinterpret_cast<const uint8_t*>(kHeaderVerification),
kMagicLength).c_str(),
reinterpret_cast<const uint8_t*>(kHeaderVerification));
return OEMCrypto_ERROR_BAD_MAGIC;
}
// Check that size is correct, now that we know what it should be.
if (buffer.size() < SignedHeaderSize(clear->count)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (buffer.size() > SignedHeaderSize(clear->count)) {
LOGW("LoadUsageTableHeader: buffer is large. %d > %d", buffer.size(),
SignedHeaderSize(clear->count));
}
OEMCryptoResult status = OEMCrypto_SUCCESS;
if (clear->master_generation != master_generation_number_) {
LOGE("Generation SKEW: %ld -> %ld", clear->master_generation,
master_generation_number_);
if ((clear->master_generation + 1 < master_generation_number_) ||
(clear->master_generation - 1 > master_generation_number_)) {
return OEMCrypto_ERROR_GENERATION_SKEW;
}
status = OEMCrypto_WARNING_GENERATION_SKEW;
}
int64_t* stored_generations =
reinterpret_cast<int64_t*>(&clear_buffer[0] + sizeof(SignedHeaderBlock));
generation_numbers_.assign(stored_generations,
stored_generations + clear->count);
sessions_.clear();
sessions_.resize(clear->count);
header_loaded_ = true;
return status;
}
OEMCryptoResult UsageTable::MoveEntry(UsageTableEntry* entry,
uint32_t new_index) {
if (new_index >= generation_numbers_.size()) {
LOGE("MoveEntry: index beyond end of usage table %d >= %d", new_index,
generation_numbers_.size());
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (sessions_[new_index]) {
LOGE("MoveEntry: session open for %d", new_index);
return OEMCrypto_ERROR_ENTRY_IN_USE;
}
if (!entry) {
LOGE("MoveEntry: null entry");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
sessions_[new_index] = sessions_[entry->index()];
sessions_[entry->index()] = 0;
entry->set_index(new_index);
generation_numbers_[new_index] = master_generation_number_;
entry->set_generation_number(master_generation_number_);
master_generation_number_++;
return OEMCrypto_SUCCESS;
}
void UsageTable::IncrementGeneration() {
master_generation_number_++;
SaveGenerationNumber();
}
bool UsageTable::SaveGenerationNumber() {
wvcdm::FileSystem* file_system = ce_->file_system();
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGE("UsageTable: Unable to get base path");
return false;
}
// On a real implementation, you should NOT put the generation number in
// a file in user space. It should be stored in secure memory.
std::string filename = path + "GenerationNumber.dat";
wvcdm::File* file = file_system->Open(
filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
if (!file) {
LOGE("UsageTable: File open failed: %s", path.c_str());
return false;
}
file->Write(reinterpret_cast<char*>(&master_generation_number_),
sizeof(int64_t));
file->Close();
return true;
}
bool UsageTable::LoadGenerationNumber(bool or_make_new_one) {
wvcdm::FileSystem* file_system = ce_->file_system();
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGE("UsageTable: Unable to get base path");
return false;
}
// On a real implementation, you should NOT put the generation number in
// a file in user space. It should be stored in secure memory.
std::string filename = path + "GenerationNumber.dat";
wvcdm::File* file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
if (!file) {
if (or_make_new_one) {
RAND_bytes(reinterpret_cast<uint8_t*>(&master_generation_number_),
sizeof(int64_t));
return true;
}
LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str());
master_generation_number_ = 0;
return false;
}
file->Read(reinterpret_cast<char*>(&master_generation_number_),
sizeof(int64_t));
file->Close();
return true;
}
OEMCryptoResult UsageTable::CreateUsageTableHeader(
uint8_t* header_buffer, size_t* header_buffer_length) {
size_t signed_header_size = SignedHeaderSize(0);
if (*header_buffer_length < signed_header_size) {
*header_buffer_length = signed_header_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*header_buffer_length = signed_header_size;
if (!LoadGenerationNumber(true)) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
// Make sure there are no entries that are currently tied to an open session.
for (size_t i=0; i < sessions_.size(); ++i) {
if (sessions_[i] != NULL) {
LOGE("CreateUsageTableHeader: index %d used by session.", i);
return OEMCrypto_ERROR_INVALID_SESSION;
}
}
sessions_.clear();
generation_numbers_.clear();
header_loaded_ = true;
return SaveUsageTableHeader(header_buffer, *header_buffer_length);
}
OldUsageTableEntry* UsageTable::FindOldUsageEntry(
const std::vector<uint8_t>& pst) {
if (!old_table_) old_table_ = new OldUsageTable(ce_);
return old_table_->FindEntry(pst);
}
OEMCryptoResult UsageTable::DeleteOldUsageTable() {
if (old_table_) {
old_table_->Clear();
delete old_table_;
old_table_ = NULL;
}
OldUsageTable::DeleteFile(ce_);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length) {
if (!old_table_) old_table_ = new OldUsageTable(ce_);
std::vector<uint8_t> pstv(pst, pst+pst_length);
OldUsageTableEntry *old_entry = old_table_->CreateEntry(pstv);
int64_t now = time(NULL);
old_entry->time_of_license_received_ = now - time_since_license_received;
old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt;
old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt;
old_entry->status_ = status;
old_entry->mac_key_server_.assign(server_mac_key,
server_mac_key + wvcdm::MAC_KEY_SIZE);
old_entry->mac_key_client_.assign(client_mac_key,
client_mac_key + wvcdm::MAC_KEY_SIZE);
return OEMCrypto_SUCCESS;
}
} // namespace wvoec_mock

View File

@@ -0,0 +1,135 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Mock implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_USAGE_TABLE_MOCK_H_
#define OEMCRYPTO_USAGE_TABLE_MOCK_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "file_store.h"
#include "lock.h"
#include "openssl/sha.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
class SessionContext;
class CryptoEngine;
class UsageTable;
class OldUsageTable;
class OldUsageTableEntry;
const size_t kMaxPSTLength = 255;
// This is the data we store offline.
struct StoredUsageEntry {
int64_t generation_number;
int64_t time_of_license_received;
int64_t time_of_first_decrypt;
int64_t time_of_last_decrypt;
enum OEMCrypto_Usage_Entry_Status status;
uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE];
uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE];
uint32_t index;
uint8_t pst[kMaxPSTLength+1]; // add 1 for padding.
uint8_t pst_length;
};
class UsageTableEntry {
public:
UsageTableEntry(UsageTable* table, uint32_t index, int64_t generation);
// owner_(owner), session_(session), loaded_(false) {}
~UsageTableEntry(); // Free memory, remove reference in header.
bool Inactive() { return data_.status >= kInactive; }
OEMCryptoResult SetPST(const uint8_t* pst, size_t pst_length);
bool VerifyPST(const uint8_t* pst, size_t pst_length);
bool VerifyMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client);
bool SetMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client);
// Returns false if the entry is inactive. Otherwise, returns true.
// If the status was unused, it is updated, and decrypt times are flaged
// for update.
bool CheckForUse();
void Deactivate(const std::vector<uint8_t>& pst);
OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst, uint8_t* buffer,
size_t* buffer_length);
void UpdateAndIncrement();
OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session,
uint8_t* signed_buffer, size_t buffer_size);
OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer);
OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
int64_t generation_number() { return data_.generation_number; }
void set_generation_number(int64_t value) { data_.generation_number = value; }
void set_index(int32_t index) { data_.index = index; }
uint32_t index() { return data_.index; }
static size_t SignedEntrySize();
private:
UsageTable* usage_table_; // Owner of this object.
bool recent_decrypt_;
bool forbid_report_;
StoredUsageEntry data_;
};
class UsageTable {
public:
explicit UsageTable(CryptoEngine* ce)
: ce_(ce), header_loaded_(false), old_table_(NULL){};
~UsageTable();
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t* usage_entry_number);
OEMCryptoResult LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry, uint32_t index,
const std::vector<uint8_t>& buffer);
OEMCryptoResult UpdateUsageEntry(SessionContext* session,
UsageTableEntry* entry,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index);
OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length);
OEMCryptoResult LoadUsageTableHeader(const std::vector<uint8_t>& buffer);
OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_table_size,
uint8_t* header_buffer,
size_t* header_buffer_length);
void ReleaseEntry(uint32_t index) { sessions_[index] = 0; }
void IncrementGeneration();
static size_t SignedHeaderSize(size_t count);
OldUsageTableEntry* FindOldUsageEntry(const std::vector<uint8_t>& pst);
OEMCryptoResult DeleteOldUsageTable();
OEMCryptoResult CreateOldUsageEntry(uint64_t time_since_license_received,
uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt,
OEMCrypto_Usage_Entry_Status status,
uint8_t *server_mac_key,
uint8_t *client_mac_key,
const uint8_t* pst,
size_t pst_length);
private:
OEMCryptoResult SaveUsageTableHeader(uint8_t* signed_buffer,
size_t buffer_size);
bool SaveGenerationNumber();
bool LoadGenerationNumber(bool or_make_new_one);
CryptoEngine* ce_;
bool header_loaded_;
int64_t master_generation_number_;
std::vector<int64_t> generation_numbers_;
std::vector<SessionContext*> sessions_;
OldUsageTable *old_table_;
};
} // namespace wvoec_mock
#endif // OEMCRYPTO_USAGE_TABLE_MOCK_H_

View File

@@ -0,0 +1,26 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WV_KEYBOX_H_
#define WV_KEYBOX_H_
#include <stdint.h>
namespace wvoec_mock {
// This is the format of a Widevine keybox.
typedef struct { // 128 bytes total.
// C character string identifying the device. Null terminated.
uint8_t device_id_[32];
// 128 bit AES key assigned to device. Generated by Widevine.
uint8_t device_key_[16];
// Key Data. Encrypted data.
uint8_t data_[72];
// Constant code used to recognize a valid keybox "kbox" = 0x6b626f78.
uint8_t magic_[4];
// The CRC checksum of the first 124 bytes of the keybox.
uint8_t crc_[4];
} WidevineKeybox;
} // namespace wvoec_mock
#endif // WV_KEYBOX_H_

View File

@@ -0,0 +1,94 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
//
#include <arpa/inet.h>
#include "wvcrc32.h"
#define INIT_CRC32 0xffffffff
uint32_t wvrunningcrc32(const uint8_t* p_begin, int i_count, uint32_t i_crc) {
static uint32_t CRC32[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/* Calculate the CRC */
while (i_count > 0) {
i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)];
p_begin++;
i_count--;
}
return(i_crc);
}
uint32_t wvcrc32(const uint8_t* p_begin, int i_count) {
return(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count) {
return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}

View File

@@ -0,0 +1,15 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
//
#ifndef WVCRC32_H_
#define WVCRC32_H_
#include <stdint.h>
uint32_t wvcrc32(const uint8_t* p_begin, int i_count);
// Convert to network byte order
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count);
#endif // WVCRC32_H_

View File

@@ -0,0 +1,119 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "OEMCryptoCENC.h"
#include <string>
#include <gtest/gtest.h>
#include "log.h"
#include "oemcrypto_logging.h"
#include "oemcrypto_mock.cpp"
class OEMCryptoLoggingTest : public ::testing::Test {
protected:
OEMCryptoLoggingTest() {}
void SetUp() {
::testing::Test::SetUp();
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
}
void TearDown() {
OEMCrypto_Terminate();
::testing::Test::TearDown();
}
};
TEST_F(OEMCryptoLoggingTest, TestDumpHexFunctions) {
uint8_t vector[] = {0xFA, 0x11, 0x28, 0x33};
std::string buffer;
wvoec_mock::dump_hex_helper(buffer, "name", vector, 4u);
ASSERT_EQ("name = \n wvcdm::a2b_hex(\"FA112833\");\n", buffer);
uint8_t vector2[] = {
0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11,
0x28, 0x33, 0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11, 0x28, 0x33,
0xFA, 0x11, 0x28, 0x33, 0x01, 0x14, 0x28, 0xAB, 0xFA, 0xCD,
0xEF, 0x67, 0x01, 0x14, 0x28, 0xAB, 0xFA, 0xCD, 0xEF, 0x67,
};
buffer.clear(); // dump_hex_helper appends to buffer
wvoec_mock::dump_hex_helper(buffer, "name", vector2, 40u);
ASSERT_EQ(
"name = \n "
"wvcdm::a2b_hex("
"\"FA112833FA112833FA112833FA112833FA112833FA112833011428ABFACDEF67\"\n "
" \"011428ABFACDEF67\");\n",
buffer);
buffer.clear(); // dump_hex_helper appends to buffer
wvoec_mock::dump_array_part_helper(buffer, "array", 5u, "name", vector2, 40u);
ASSERT_EQ(
"std::string s5_name = \n "
"wvcdm::a2b_hex("
"\"FA112833FA112833FA112833FA112833FA112833FA112833011428ABFACDEF67\"\n "
" \"011428ABFACDEF67\");\narray[5].name = message_ptr + "
"message.find(s5_name.data());\n",
buffer);
buffer.clear(); // dump_hex_helper appends to buffer
wvoec_mock::dump_array_part_helper(buffer, "array", 5u, "name", NULL, 40u);
ASSERT_EQ("array[5].name = NULL;\n", buffer);
}
TEST_F(OEMCryptoLoggingTest, TestChangeLoggingLevel) {
wvoec_mock::SetLoggingLevel(wvcdm::LOG_WARN);
ASSERT_EQ(wvcdm::LOG_WARN, wvcdm::g_cutoff);
wvoec_mock::SetLoggingLevel(wvcdm::LOG_INFO);
ASSERT_EQ(wvcdm::LOG_INFO, wvcdm::g_cutoff);
wvoec_mock::SetLoggingSettings(wvcdm::LOG_WARN,
wvoec_mock::kLoggingDumpTraceAll);
ASSERT_EQ(wvcdm::LOG_WARN, wvcdm::g_cutoff);
ASSERT_TRUE(wvoec_mock::LogCategoryEnabled(wvoec_mock::kLoggingDumpTraceAll));
wvoec_mock::TurnOffLoggingForAllCategories();
wvoec_mock::SetLoggingLevel(wvcdm::LOG_VERBOSE);
ASSERT_EQ(wvcdm::LOG_VERBOSE, wvcdm::g_cutoff);
wvoec_mock::SetLoggingLevel(wvcdm::LOG_WARN);
}
TEST_F(OEMCryptoLoggingTest, TestChangeLoggingCategories) {
using namespace wvoec_mock;
TurnOffLoggingForAllCategories();
ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceDecryption |
kLoggingTraceOEMCryptoCalls));
AddLoggingForCategories(kLoggingDumpKeyControlBlocks |
kLoggingDumpDerivedKeys);
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks));
ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpTraceAll));
RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks |
kLoggingTraceUsageTable);
ASSERT_FALSE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpDerivedKeys));
ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable));
TurnOffLoggingForAllCategories();
ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable));
AddLoggingForCategories(kLoggingDumpTraceAll);
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks));
ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceOEMCryptoCalls));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpContentKeys));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpDerivedKeys));
ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceNonce));
ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceDecryption));
ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceUsageTable));
ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceDecryptCalls));
ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpTraceAll));
RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks);
ASSERT_FALSE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks));
}