Widevine MediaCas client code that works with Android R

This commit is contained in:
Lu Chen
2020-08-13 15:18:12 -07:00
parent ff9728aaa2
commit 0f6db6f751
243 changed files with 47012 additions and 0 deletions

37
oemcrypto/ref/Android.bp Normal file
View File

@@ -0,0 +1,37 @@
cc_library_shared {
name: "libcasoemcrypto",
proprietary: true,
srcs: [
"src/keys.cpp",
"src/oem_cert.cpp",
"src/oemcrypto_auth_ref.cpp",
"src/oemcrypto_engine_device_properties.cpp",
"src/oemcrypto_engine_ref.cpp",
"src/oemcrypto_entitled_key_session.cpp",
"src/oemcrypto_keybox_ref.cpp",
"src/oemcrypto_key_ref.cpp",
"src/oemcrypto_nonce_table.cpp",
"src/oemcrypto_ref.cpp",
"src/oemcrypto_rsa_key_shared.cpp",
"src/oemcrypto_session.cpp",
"src/oemcrypto_session_key_table.cpp",
"src/oemcrypto_usage_table_ref.cpp",
"src/wvcrc.cpp",
"src/ts_parser.cpp",
],
include_dirs: [
"vendor/widevine/libwvmediacas/oemcrypto/odk/include",
],
static_libs: [
"//vendor/widevine/libwvmediacas/wvutil:libcasutil",
"libwvcas_odk",
],
shared_libs: [
"libcrypto",
"libutils",
"liblog",
],
header_libs: [
"//vendor/widevine/libwvmediacas/oemcrypto:oemcastroheaders",
],
}

5
oemcrypto/ref/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Reference OEMCrypto
This directory contains a testing-only implementation of OEMCrypto. **This
implementation is *NOT* suitable for production use and must *NOT* be released
on devices.**

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/ref/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,334 @@
// This file contains the test OEM cert.
#include "oem_cert.h"
namespace wvoec_ref {
const uint32_t kOEMSystemId_Prod = 7913;
// From file test_rsa_key_2_carmichael.pk8 in team shared drive.
// Size is 1216.
const uint8_t kOEMPrivateKey_Prod[] = {
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, 0x0a, 0xf9, 0x4a, 0x19, 0x72, 0x88, 0x1b, 0x4e, 0xd8,
0x2f, 0xef, 0x99, 0x93, 0x32, 0xda, 0x51, 0x21, 0x2e, 0x14, 0x06, 0xf4,
0xe9, 0x65, 0x1c, 0xf9, 0xd4, 0xcf, 0x1a, 0x51, 0x53, 0xcd, 0x48, 0x33,
0x8c, 0x30, 0xed, 0xdd, 0x53, 0x6f, 0x29, 0x82, 0xf9, 0xe0, 0x74, 0xde,
0xb1, 0x13, 0x01, 0x88, 0x8f, 0xce, 0x14, 0xc1, 0x3b, 0x90, 0xb7, 0xcc,
0x6c, 0xdf, 0x35, 0xa1, 0xf2, 0x1a, 0x3d, 0xbe, 0x19, 0xd7, 0x0a, 0xe4,
0x67, 0x75, 0xbb, 0xfa, 0x87, 0xf4, 0x03, 0xb5, 0x7f, 0x69, 0xe4, 0x0b,
0x6a, 0xdc, 0x92, 0x82, 0x54, 0x64, 0x1a, 0x94, 0x2d, 0xe4, 0x63, 0x40,
0xb2, 0xb4, 0x85, 0x6b, 0xc8, 0x34, 0xba, 0xa2, 0x14, 0x30, 0x47, 0x1a,
0xeb, 0x90, 0x62, 0x30, 0x43, 0x44, 0x02, 0xc7, 0x0c, 0x30, 0xc0, 0x7f,
0xa9, 0x47, 0xae, 0xde, 0x68, 0x27, 0x92, 0xaa, 0x11, 0x95, 0xf5, 0x6f,
0xfc, 0x19, 0x8b, 0x49, 0xa0, 0x77, 0x9d, 0xc6, 0x13, 0x5d, 0x73, 0xff,
0x45, 0xa2, 0x4c, 0x3b, 0xf3, 0xe1, 0x2d, 0xd7, 0xc4, 0x70, 0xe2, 0x6c,
0x37, 0x99, 0x4c, 0x7a, 0xa9, 0x27, 0xf8, 0x3a, 0xd6, 0xfd, 0xc5, 0xd8,
0xfa, 0x2d, 0x0e, 0x71, 0x4b, 0x85, 0x7e, 0xce, 0xcb, 0x1c, 0x79, 0x71,
0xbd, 0xff, 0x63, 0x03, 0x6b, 0x58, 0x68, 0xe0, 0x14, 0xca, 0x5e, 0x85,
0xfd, 0xd0, 0xb7, 0xe0, 0x68, 0x14, 0xff, 0x2c, 0x82, 0x22, 0x26, 0x8a,
0x3f, 0xbf, 0xb0, 0x2a, 0x90, 0xff, 0xc7, 0x72, 0xfc, 0x66, 0x51, 0x3e,
0x51, 0x9f, 0x82, 0x68, 0x0e, 0xf3, 0x65, 0x74, 0x88, 0xab, 0xb7, 0xe5,
0x97, 0x5f, 0x0f, 0x3e, 0xe5, 0x3a, 0xbc, 0xa4, 0xa1, 0x50, 0xdd, 0x5c,
0x94, 0x4b, 0x0c, 0x70, 0x71, 0x48, 0x4e, 0xd0, 0xec, 0x46, 0x8f, 0xdf,
0xa2, 0x9a, 0xfe, 0xd8, 0x35, 0x1a, 0x2f, 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
};
const size_t kOEMPrivateKeySize_Prod = sizeof(kOEMPrivateKey_Prod);
// From the team shared drive file
// oem-7913-leaf-and-intermediate-certs-test-key-2-carmichael.p7b, size 2353.
const uint8_t kOEMPublicCert_Prod[] = {
0x30, 0x82, 0x09, 0x2d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x07, 0x02, 0xa0, 0x82, 0x09, 0x1e, 0x30, 0x82, 0x09, 0x1a, 0x02,
0x01, 0x01, 0x31, 0x00, 0x30, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x02, 0x04, 0x00, 0xa0, 0x82, 0x08,
0xfe, 0x30, 0x82, 0x03, 0x71, 0x30, 0x82, 0x02, 0x59, 0xa0, 0x03, 0x02,
0x01, 0x02, 0x02, 0x11, 0x00, 0xc2, 0x8d, 0x20, 0x22, 0x82, 0x8b, 0x9e,
0x63, 0x9d, 0x15, 0x89, 0x2c, 0xa9, 0x8f, 0xd9, 0x5d, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
0x30, 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
0x0c, 0x02, 0x57, 0x41, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x73, 0x79,
0x73, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x39, 0x31,
0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x31, 0x31, 0x31, 0x31,
0x33, 0x32, 0x36, 0x32, 0x32, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x31,
0x30, 0x36, 0x31, 0x33, 0x32, 0x36, 0x32, 0x32, 0x5a, 0x30, 0x65, 0x31,
0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x39,
0x31, 0x33, 0x2d, 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x57, 0x41, 0x31, 0x11, 0x30,
0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b,
0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30,
0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65,
0x76, 0x69, 0x6e, 0x65, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 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, 0xa3, 0x16,
0x30, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
0x79, 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1e, 0xe9, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x88, 0x95, 0xec, 0xcd, 0x8b, 0xa7,
0x51, 0xda, 0x74, 0x81, 0xa5, 0x39, 0x62, 0x1a, 0x0e, 0x2e, 0xde, 0x3c,
0x37, 0xea, 0xad, 0x7c, 0xee, 0x9b, 0x26, 0x8e, 0xe2, 0xd6, 0x34, 0xcd,
0xb7, 0x70, 0xba, 0xbf, 0xa0, 0xa3, 0xfe, 0xb3, 0x4b, 0xbc, 0xf4, 0x1c,
0x72, 0x66, 0x81, 0xd5, 0x09, 0x33, 0x78, 0x0c, 0x61, 0x21, 0xa8, 0xf1,
0xe2, 0xc9, 0xe2, 0x83, 0xc2, 0x19, 0x02, 0xf2, 0xe8, 0xab, 0x17, 0x36,
0x3a, 0x0b, 0x20, 0xaf, 0x0f, 0xae, 0x2e, 0x73, 0x68, 0xac, 0x15, 0xee,
0x9c, 0xc0, 0x92, 0x03, 0x7e, 0x95, 0x63, 0xaa, 0xad, 0x15, 0x96, 0x43,
0x20, 0x3b, 0xe5, 0x9b, 0x1f, 0xca, 0x02, 0xba, 0xf0, 0x07, 0x76, 0x80,
0xd7, 0xa3, 0x1a, 0xeb, 0xc8, 0xdb, 0x03, 0x7b, 0x43, 0x56, 0xe5, 0x96,
0x6b, 0x86, 0xfe, 0x08, 0x58, 0x8a, 0x84, 0xbd, 0xe9, 0x47, 0x18, 0xee,
0xb2, 0xa8, 0x05, 0x7b, 0xf0, 0xfd, 0xaa, 0xb9, 0x85, 0xcd, 0x7a, 0x0e,
0x6b, 0x6c, 0x9f, 0xc6, 0x75, 0xd2, 0x2a, 0xfe, 0x5b, 0xf3, 0xb7, 0x31,
0x6c, 0xac, 0xe3, 0x00, 0x9f, 0xe7, 0xdd, 0xe3, 0x81, 0xc1, 0x36, 0xc3,
0x1c, 0x5f, 0xdf, 0xf2, 0xc3, 0x5e, 0xfa, 0x55, 0x32, 0xd8, 0x5c, 0xa8,
0xe5, 0xcc, 0xb6, 0x4a, 0xe9, 0xe2, 0xcc, 0x38, 0x44, 0x07, 0x46, 0x59,
0x34, 0x84, 0x79, 0xf9, 0xee, 0x3c, 0x4b, 0x48, 0x90, 0xab, 0x73, 0xb0,
0xa1, 0x92, 0xc3, 0xd6, 0x83, 0x87, 0x81, 0xca, 0x12, 0x81, 0xd6, 0x5d,
0xf7, 0x6f, 0x7a, 0x35, 0x5e, 0x4f, 0x02, 0x66, 0x8a, 0x47, 0x88, 0x82,
0xab, 0xf0, 0x12, 0x1d, 0xb9, 0x75, 0x3b, 0x7b, 0xa8, 0x36, 0x15, 0xef,
0xa8, 0x12, 0x0e, 0x53, 0xb4, 0x83, 0x78, 0x53, 0xc0, 0x52, 0xae, 0xa6,
0x0a, 0xa0, 0x53, 0xdc, 0x1c, 0x15, 0x22, 0xdd, 0x17, 0x98, 0x30, 0x82,
0x05, 0x85, 0x30, 0x82, 0x03, 0x6d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
0x10, 0x03, 0xb1, 0xf7, 0x58, 0xdf, 0x1d, 0xe3, 0x25, 0x00, 0x0b, 0x10,
0x3d, 0xd5, 0xe6, 0xe4, 0x64, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61,
0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f,
0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c,
0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76,
0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03,
0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d,
0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x31, 0x31,
0x31, 0x38, 0x30, 0x31, 0x31, 0x33, 0x33, 0x35, 0x5a, 0x17, 0x0d, 0x32,
0x37, 0x31, 0x31, 0x31, 0x38, 0x30, 0x31, 0x31, 0x33, 0x31, 0x33, 0x5a,
0x30, 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
0x0c, 0x02, 0x57, 0x41, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x73, 0x79,
0x73, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x39, 0x31,
0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xae, 0xc8,
0x71, 0xae, 0x08, 0x0c, 0x06, 0x06, 0x2d, 0x81, 0x7c, 0xa9, 0x8b, 0xb3,
0xd6, 0x66, 0xe4, 0xf6, 0x08, 0x5e, 0x5a, 0x75, 0xe8, 0x74, 0x61, 0x7a,
0x88, 0xca, 0x85, 0x14, 0x0d, 0x58, 0xa4, 0x09, 0x19, 0x6c, 0x60, 0xc9,
0xad, 0x91, 0x1c, 0xbf, 0x04, 0xb3, 0x47, 0x10, 0x63, 0x7f, 0x02, 0x58,
0xc2, 0x1e, 0xbd, 0xcc, 0x07, 0x77, 0xaa, 0x7e, 0x14, 0xa8, 0xc2, 0x01,
0xcd, 0xe8, 0x46, 0x60, 0x53, 0x6f, 0x2f, 0xda, 0x17, 0x2d, 0x4d, 0x9d,
0x0e, 0x5d, 0xb5, 0x50, 0x95, 0xae, 0xab, 0x6e, 0x43, 0xe3, 0xb0, 0x00,
0x12, 0xb4, 0x05, 0x82, 0x4a, 0x2b, 0x14, 0x63, 0x0d, 0x1f, 0x06, 0x12,
0xaa, 0xe1, 0x9d, 0xe7, 0xba, 0xda, 0xe3, 0xfc, 0x7c, 0x6c, 0x73, 0xae,
0x56, 0xf8, 0xab, 0xf7, 0x51, 0x93, 0x31, 0xef, 0x8f, 0xe4, 0xb6, 0x01,
0x2c, 0xeb, 0x7b, 0xe4, 0xd8, 0xb3, 0xea, 0x70, 0x37, 0x89, 0x05, 0xa9,
0x51, 0x57, 0x72, 0x98, 0x9e, 0xa8, 0x46, 0xdb, 0xeb, 0x7a, 0x38, 0x2b,
0x2f, 0xc0, 0x27, 0xb7, 0xc2, 0xe1, 0x9a, 0x17, 0xdf, 0xf5, 0xd6, 0x9c,
0xd5, 0x8c, 0xb8, 0x66, 0x42, 0xd5, 0x04, 0x1e, 0x7c, 0x36, 0x4c, 0x1e,
0x3e, 0x45, 0x51, 0x4d, 0x41, 0x72, 0x22, 0x53, 0x3d, 0xf4, 0x57, 0x7c,
0x6c, 0x33, 0x34, 0x24, 0x45, 0xdf, 0x84, 0x87, 0x4a, 0xa6, 0xcb, 0x7c,
0x03, 0xa3, 0xaa, 0x8e, 0x2d, 0x82, 0x01, 0x27, 0x87, 0x74, 0x82, 0x1a,
0xbc, 0x0f, 0x76, 0x69, 0xab, 0xe0, 0x4e, 0x70, 0xbe, 0x37, 0xfc, 0xc8,
0x2c, 0x91, 0x17, 0x4f, 0xd5, 0x26, 0x3b, 0x7b, 0x90, 0xb5, 0x2d, 0x64,
0xba, 0xf7, 0xd2, 0x8a, 0xb4, 0x8f, 0x38, 0x9d, 0x8e, 0xba, 0xe7, 0x5c,
0x52, 0xf1, 0x0a, 0xb8, 0xc0, 0x1b, 0xb6, 0xb1, 0x70, 0x7e, 0x47, 0x59,
0x94, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30,
0x82, 0x01, 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
0x02, 0x02, 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
0x04, 0x14, 0x4b, 0xcb, 0xdf, 0xaa, 0x02, 0xde, 0x8d, 0xc3, 0xe7, 0xe5,
0x85, 0xdb, 0x2e, 0x8a, 0xbe, 0x75, 0x6b, 0x8a, 0x67, 0x58, 0x30, 0x81,
0xb2, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, 0x81, 0xa7,
0x80, 0x14, 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5,
0xf7, 0x13, 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, 0xa1, 0x81,
0x83, 0xa4, 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e,
0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31,
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31,
0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69,
0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f,
0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64,
0x82, 0x09, 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, 0x9a, 0x30,
0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01,
0x01, 0x04, 0x04, 0x02, 0x02, 0x1e, 0xe9, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
0x02, 0x01, 0x00, 0x61, 0x3f, 0x2f, 0x43, 0xe4, 0xbe, 0x66, 0x34, 0xef,
0x92, 0x06, 0xe9, 0x88, 0xba, 0x6a, 0x1d, 0x4f, 0x54, 0x5a, 0x97, 0xb1,
0x75, 0xd7, 0x93, 0xf8, 0x45, 0xc6, 0x83, 0x92, 0x36, 0xfd, 0x55, 0xa9,
0x21, 0x0b, 0xdc, 0xf6, 0xae, 0x11, 0xdc, 0x62, 0x21, 0x44, 0xbd, 0x04,
0x1d, 0x58, 0x2c, 0x03, 0xf8, 0xe4, 0xe2, 0x1e, 0xba, 0xe6, 0xdd, 0x19,
0xdd, 0x56, 0xfd, 0xce, 0x06, 0x73, 0x5f, 0x94, 0x1e, 0xb6, 0x03, 0xdb,
0x3d, 0x7b, 0xab, 0xab, 0x72, 0x64, 0x7b, 0xde, 0x7d, 0x4d, 0xcf, 0x7e,
0xf0, 0x91, 0x29, 0xc1, 0x77, 0x13, 0xc2, 0x6f, 0x80, 0xab, 0x7a, 0xa8,
0xce, 0xb0, 0x1c, 0x2a, 0xc5, 0x9c, 0xfb, 0x0b, 0xe5, 0x9f, 0x9c, 0x1b,
0xc9, 0x4b, 0x58, 0xdf, 0x96, 0x18, 0xf7, 0x67, 0x67, 0x89, 0xa4, 0xe9,
0x14, 0x48, 0xac, 0xfa, 0x9d, 0x86, 0x2a, 0xeb, 0x75, 0x2c, 0x2b, 0xbf,
0x63, 0x7d, 0xc7, 0x4e, 0x7e, 0xad, 0x39, 0x2d, 0xb4, 0x7c, 0x07, 0xa5,
0x5a, 0xe8, 0x3a, 0xd4, 0xf5, 0x0c, 0x4f, 0xf3, 0xa2, 0x9c, 0x3c, 0x32,
0xed, 0x9d, 0x4b, 0x49, 0x05, 0xbc, 0x1f, 0xa0, 0x13, 0xe6, 0xdd, 0x82,
0x79, 0x06, 0x31, 0x3b, 0xc6, 0x97, 0xec, 0x8d, 0xaa, 0x4f, 0xef, 0x14,
0x3c, 0x21, 0xf6, 0x72, 0xb2, 0x09, 0x42, 0xc7, 0x74, 0xfe, 0xef, 0x70,
0xbd, 0xe9, 0x85, 0x41, 0x30, 0x0b, 0xb3, 0x6b, 0x59, 0x0c, 0x0f, 0x11,
0x75, 0xd4, 0xbb, 0xb1, 0xdf, 0xb1, 0xdf, 0xb3, 0xfa, 0xb3, 0x3a, 0x43,
0x17, 0x7d, 0x8a, 0x82, 0xae, 0xa2, 0x07, 0xf8, 0x83, 0x51, 0xfb, 0x16,
0xfb, 0x64, 0xb6, 0x46, 0xda, 0xbe, 0x32, 0x2b, 0xc0, 0xee, 0x78, 0x2a,
0x84, 0xa9, 0x54, 0x0a, 0xf9, 0x2d, 0x61, 0x65, 0xde, 0xa5, 0x97, 0x66,
0x79, 0x02, 0xf8, 0x97, 0x17, 0xe2, 0xd4, 0x9f, 0x9e, 0xac, 0xcc, 0xae,
0x99, 0x9a, 0x03, 0x04, 0xbb, 0x45, 0xfe, 0xb2, 0xf5, 0x80, 0xba, 0xbf,
0xdd, 0x24, 0xe5, 0xe6, 0x1e, 0x5d, 0x36, 0xa5, 0x87, 0x0c, 0xdf, 0x60,
0x81, 0x6f, 0xb7, 0x5f, 0xb9, 0x1f, 0xca, 0x75, 0x3c, 0x1a, 0x63, 0xb0,
0xeb, 0xe6, 0x95, 0x86, 0x0d, 0xae, 0xa6, 0xc9, 0x2a, 0x94, 0xf1, 0xd0,
0xbe, 0x75, 0xc8, 0xf8, 0x07, 0xd7, 0x88, 0xff, 0xec, 0xf9, 0xcd, 0x49,
0xc6, 0xfe, 0x4d, 0x7f, 0x44, 0x1e, 0xd8, 0xaf, 0xa9, 0x72, 0x27, 0x98,
0xe2, 0x5a, 0x08, 0xea, 0x55, 0xd3, 0xb3, 0xea, 0xdc, 0x76, 0x69, 0x51,
0x10, 0x01, 0x46, 0x7d, 0x33, 0x94, 0x9c, 0x94, 0xef, 0xfe, 0x76, 0x1c,
0xc6, 0xd7, 0x15, 0x53, 0x3e, 0x8d, 0x3d, 0x29, 0x9a, 0x58, 0x6a, 0xf1,
0x75, 0x9e, 0xea, 0x1b, 0x4c, 0xf0, 0x47, 0x76, 0xac, 0xc6, 0xa2, 0x32,
0x44, 0x40, 0xdf, 0xfe, 0xff, 0x9d, 0xf4, 0xe2, 0xc2, 0xfa, 0xa1, 0x5f,
0x2e, 0x66, 0xe9, 0x97, 0xcb, 0x27, 0x26, 0x6e, 0x53, 0xe4, 0xe8, 0x86,
0x2c, 0xea, 0xd3, 0x69, 0x6c, 0x61, 0x4f, 0xfe, 0xc1, 0xc9, 0x8b, 0x05,
0x92, 0x6f, 0x47, 0x96, 0xce, 0xf0, 0x33, 0xfa, 0x7c, 0x78, 0x24, 0x9b,
0xd7, 0x8d, 0x36, 0x56, 0x37, 0x86, 0xbc, 0x72, 0x5a, 0xf9, 0xb9, 0xb0,
0x93, 0xf0, 0x81, 0x78, 0x10, 0xf2, 0xb0, 0xc2, 0x79, 0x91, 0x5e, 0xcf,
0xbc, 0x8c, 0xf2, 0x32, 0x0f, 0xf7, 0x2d, 0x30, 0xd8, 0x13, 0x77, 0x4f,
0x78, 0x9e, 0x40, 0x8d, 0xe6, 0x3a, 0x98, 0xb2, 0xaa, 0x13, 0x4d, 0x25,
0x49, 0x34, 0x6c, 0x80, 0x9e, 0x19, 0x03, 0xdb, 0xcd, 0xf5, 0xb1, 0x54,
0x74, 0x1b, 0x67, 0x3c, 0x46, 0xac, 0x3e, 0x5d, 0xa2, 0xd9, 0x13, 0x83,
0x30, 0xeb, 0x82, 0x3b, 0x06, 0xab, 0x3c, 0x39, 0x7d, 0xd0, 0x68, 0x31,
0x00
};
const size_t kOEMPublicCertSize_Prod = sizeof(kOEMPublicCert_Prod);
// Refer to the following in main modules.
// This level of indirection is present so new OEM Certificates can be
// added and then selected for use at compile time or run time.
const uint32_t kOEMSystemId = kOEMSystemId_Prod;
const uint8_t* kOEMPrivateKey = kOEMPrivateKey_Prod;
const uint8_t* kOEMPublicCert = kOEMPublicCert_Prod;
const size_t kOEMPrivateKeySize = kOEMPrivateKeySize_Prod;
const size_t kOEMPublicCertSize = kOEMPublicCertSize_Prod;
} // namespace wvoec_ref

View File

@@ -0,0 +1,21 @@
// 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>
namespace wvoec_ref {
// Refer to the following in main modules
extern const uint32_t kOEMSystemId;
extern const uint8_t* kOEMPrivateKey;
extern const uint8_t* kOEMPublicCert;
extern const size_t kOEMPrivateKeySize;
extern const size_t kOEMPublicCertSize;
} // namespace wvoec_ref
#endif // OEM_CERT_H_

View File

@@ -0,0 +1,203 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_auth_ref.h"
#include <vector>
#include "keys.h"
#include "log.h"
#include "oemcrypto_rsa_key_shared.h"
namespace {
// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
// This is the RSA Test Key. This key is not derived
// from any Widevine authentication root.
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_ref {
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_ref

View File

@@ -0,0 +1,81 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_AUTH_REF_H_
#define OEMCRYPTO_AUTH_REF_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCAS.h" // Needed for enums only.
#include "disallow_copy_and_assign.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_keybox_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_types.h"
namespace wvoec_ref {
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* DeviceToken() {
return keybox().key_data();
}
WvKeybox& keybox() { return use_test_keybox_ ? test_keybox_ : keybox_; }
bool UseTestKeybox(const uint8_t* keybox_data, size_t keybox_length) {
use_test_keybox_ = true;
return test_keybox_.InstallKeybox(keybox_data, keybox_length);
}
RSA_shared_ptr& SharedRsaKey() { return rsa_key_; }
RSA* rsa_key() { return rsa_key_.get(); }
bool LoadTestRsaKey();
void Clear() { use_test_keybox_ = false; }
private:
OEMCrypto_ProvisioningMethod provisioning_method_;
WvKeybox& real_keybox() { return keybox_; }
WvKeybox keybox_;
WvKeybox 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_ref
#endif // OEMCRYPTO_AUTH_REF_H_

View File

@@ -0,0 +1,19 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_engine_ref.h"
#include <utility>
namespace wvoec_ref {
CryptoEngine* CryptoEngine::MakeCryptoEngine(
std::unique_ptr<wvutil::FileSystem>&& file_system) {
return new CryptoEngine(std::move(file_system));
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,40 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
// This file contains oemcrypto engine properties that would be for a
// level 1 device.
#include "oemcrypto_engine_ref.h"
#include <utility>
namespace wvoec_ref {
class L1CryptoEngine : public CryptoEngine {
public:
explicit L1CryptoEngine(std::unique_ptr<wvutil::FileSystem>&& file_system)
: CryptoEngine(std::move(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(
std::unique_ptr<wvutil::FileSystem>&& file_system) {
return new L1CryptoEngine(std::move(file_system));
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,38 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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_ref.h"
namespace wvoec_ref {
class CertOnlyCryptoEngine : public CryptoEngine {
public:
explicit CertOnlyCryptoEngine(
std::unique_ptr<wvutil::FileSystem>&& file_system)
: CryptoEngine(std::move(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(
std::unique_ptr<wvutil::FileSystem>&& file_system) {
return new CertOnlyCryptoEngine(std::move(file_system));
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,86 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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_ref.h"
#include <string.h>
#include <utility>
#include "log.h"
#include "oem_cert.h"
namespace wvoec_ref {
class Prov30CryptoEngine : public CryptoEngine {
public:
explicit Prov30CryptoEngine(std::unique_ptr<wvutil::FileSystem>&& file_system)
: CryptoEngine(std::move(file_system)) {}
bool config_local_display_only() { return true; }
// Returns the max HDCP version supported.
OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() {
return HDCP_NO_DIGITAL_OUTPUT;
}
// Returns true if the client supports persistent storage of
// offline usage table information.
bool config_supports_usage_table() {
return false;
}
// Returns true if the client uses a keybox as the root of trust.
bool config_supports_keybox() {
return false;
}
// This version uses an OEM Certificate.
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 == nullptr) {
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 == nullptr) {
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(
std::unique_ptr<wvutil::FileSystem>&& file_system) {
return new Prov30CryptoEngine(std::move(file_system));
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,337 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_engine_ref.h"
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <openssl/aes.h>
#include <openssl/err.h>
#include "clock.h"
#include "keys.h"
#include "log.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "string_conversions.h"
namespace {
// Lower bits in SessionId are actual session id. The rest higher bits are
// session type.
const uint32_t kSessionIdTypeShift = 28;
const uint32_t kSessionIdMask = (1u << kSessionIdTypeShift) - 1u;
} // namespace
namespace wvoec_ref {
// 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(std::unique_ptr<wvutil::FileSystem>&& file_system)
: root_of_trust_(config_provisioning_method()),
file_system_(std::move(file_system)),
usage_table_(),
entitled_key_session_table_(nullptr) {
ERR_load_crypto_strings();
}
CryptoEngine::~CryptoEngine() {
ERR_free_strings();
}
bool CryptoEngine::Initialize() {
std::string file_path = GetUsageTimeFileFullPath();
LoadOfflineTimeInfo(file_path);
usage_table_.reset(MakeUsageTable());
return true;
}
void CryptoEngine::Terminate() {
std::string file_path = GetUsageTimeFileFullPath();
SaveOfflineTimeInfo(file_path);
std::unique_lock<std::mutex> lock(session_table_lock_);
ActiveSessions::iterator it;
for (it = sessions_.begin(); it != sessions_.end(); ++it) {
delete it->second;
}
sessions_.clear();
root_of_trust_.Clear();
}
SessionId CryptoEngine::OpenSession() {
std::unique_lock<std::mutex> lock(session_table_lock_);
static OEMCrypto_SESSION unique_id = 1;
SessionId id = ++unique_id;
// Check if too many sessions have been opened.
if (SessionTypeBits(id) != 0) {
return 0;
}
// Apply session type to higher bits.
id = (kSessionTypeOEMCrypto << kSessionIdTypeShift) | (id & kSessionIdMask);
sessions_[id] = MakeSession(id);
return id;
}
SessionContext* CryptoEngine::MakeSession(SessionId sid) {
return new SessionContext(this, sid, root_of_trust_.SharedRsaKey());
}
UsageTable* CryptoEngine::MakeUsageTable() { return new UsageTable(this); }
bool CryptoEngine::DestroySession(SessionId sid) {
SessionContext* sctx = FindSession(sid);
std::unique_lock<std::mutex> lock(session_table_lock_);
if (sctx) {
if (entitled_key_session_table_ != nullptr) {
entitled_key_session_table_->OnDestroyOEMCryptoSession(sid);
}
sessions_.erase(sid);
delete sctx;
return true;
} else {
return false;
}
}
SessionContext* CryptoEngine::FindSession(SessionId sid) {
std::unique_lock<std::mutex> lock(session_table_lock_);
SessionId oec_sid = sid;
// Convert entitled key session id to oemcrypto session id.
if (SessionTypeBits(sid) == kSessionTypeEntitledKey) {
if (entitled_key_session_table_ == nullptr ||
entitled_key_session_table_->FindEntitledKeySession(sid) == nullptr) {
return nullptr;
}
oec_sid = entitled_key_session_table_->GetOEMCryptoSessionId(sid);
}
ActiveSessions::iterator it = sessions_.find(oec_sid);
if (it != sessions_.end()) {
return it->second;
}
return nullptr;
}
OEMCryptoResult CryptoEngine::CreateEntitledKeySession(SessionId oec_sid,
SessionId* key_sid) {
std::unique_lock<std::mutex> lock(session_table_lock_);
// Create entitled key session table if it does not exist yet.
if (entitled_key_session_table_ == nullptr) {
entitled_key_session_table_ =
std::unique_ptr<EntitledKeySessionTable>(new EntitledKeySessionTable());
}
// Generate random entitled key session id. 0 is reserved as an invalid id.
SessionId sid = 0;
do {
// Higher bits in session id is session type.
sid = (kSessionTypeEntitledKey << kSessionIdTypeShift) |
(rand() & kSessionIdMask);
} while (sid == 0 ||
entitled_key_session_table_->FindEntitledKeySession(sid) != nullptr);
EntitledKeySession* entitled_key_session =
entitled_key_session_table_->CreateEntitledKeySession(oec_sid, sid);
if (entitled_key_session == nullptr ||
entitled_key_session->GetSessionId() != sid) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
*key_sid = sid;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult CryptoEngine::RemoveEntitledKeySession(SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_table_lock_);
if (entitled_key_session_table_ == nullptr) {
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
}
EntitledKeySession* entitled_key_session =
entitled_key_session_table_->FindEntitledKeySession(key_sid);
if (entitled_key_session == nullptr) {
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
}
entitled_key_session_table_->RemoveEntitledKeySession(key_sid);
return OEMCrypto_SUCCESS;
}
EntitledKeySession* CryptoEngine::FindEntitledKeySession(SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_table_lock_);
if (entitled_key_session_table_ == nullptr) {
return nullptr;
}
return entitled_key_session_table_->FindEntitledKeySession(key_sid);
}
int64_t CryptoEngine::MonotonicTime() {
// Use the monotonic clock for times that don't have to be stable across
// device boots.
int64_t now =
wvutil::Clock().GetCurrentTime() + offline_time_info_.rollback_offset;
static int64_t then = now;
if (now < then) {
LOGW("Clock rollback detected: %lld seconds", then - now);
offline_time_info_.rollback_offset += then - now;
now = then;
}
then = now;
return now;
}
int64_t CryptoEngine::SystemTime() {
const int64_t current_time = MonotonicTime();
// Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
// last write.
if (current_time - offline_time_info_.previous_time >
kTimeInfoUpdateWindowInSeconds) {
std::string file_path = GetUsageTimeFileFullPath();
SaveOfflineTimeInfo(file_path);
}
return current_time;
}
std::string CryptoEngine::GetUsageTimeFileFullPath() const {
std::string file_path;
// Note: file path is OK for a real implementation, but using security
// level 1 would be better.
// TODO(fredgc, jfore): Address how this property is presented to the ref.
// For now, the file path is empty.
/*if (!wvutil::Properties::GetDeviceFilesBasePath(wvutil::kSecurityLevelL3,
&file_path)) {
LOGE("Unable to get base path");
}*/
return file_path + kStoredUsageTimeFileName;
}
bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
memset(&offline_time_info_, 0, sizeof(TimeInfo));
wvutil::FileSystem* file_system = file_system_.get();
if (file_system->Exists(file_path)) {
std::unique_ptr<wvutil::File> file =
file_system->Open(file_path, wvutil::FileSystem::kReadOnly);
if (!file) {
// This error is expected at first initialization.
LOGE("File open failed (this is expected on first initialization): %s",
file_path.c_str());
return false;
}
// Load time info from previous call.
file->Read(reinterpret_cast<char*>(&offline_time_info_), sizeof(TimeInfo));
// Detect offline time rollback after loading from disk.
// Add any time offsets in the past to the current time.
int64_t current_time = MonotonicTime();
if (offline_time_info_.previous_time > current_time) {
// Current time is earlier than the previously saved time. Time has been
// rolled back. Update the rollback offset.
offline_time_info_.rollback_offset +=
offline_time_info_.previous_time - current_time;
// Keep current time at previous recorded time.
current_time = offline_time_info_.previous_time;
}
// The new previous_time will either stay the same or move forward.
offline_time_info_.previous_time = current_time;
}
return true;
}
bool CryptoEngine::SaveOfflineTimeInfo(const std::string& file_path) {
// Add any time offsets in the past to the current time. If there was an
// earlier offline rollback, the rollback offset will be updated in
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
// will never go back.
const int64_t current_time = MonotonicTime();
// The new previous_time will either stay the same or move forward.
if (current_time > offline_time_info_.previous_time)
offline_time_info_.previous_time = current_time;
std::unique_ptr<wvutil::File> file;
wvutil::FileSystem* file_system = file_system_.get();
// Write the current time and offset to disk.
file = file_system->Open(
file_path, wvutil::FileSystem::kCreate | wvutil::FileSystem::kTruncate);
if (!file) {
LOGE("File open failed: %s", file_path.c_str());
return false;
}
file->Write(reinterpret_cast<char*>(&offline_time_info_), sizeof(TimeInfo));
return true;
}
bool CryptoEngine::NonceCollision(uint32_t nonce) {
for (const auto& session_pair : sessions_) {
const SessionContext* session = session_pair.second;
if (nonce == session->nonce()) return true;
}
return false;
}
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(
const 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.address_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.handle_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_ = nullptr;
break;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
const size_t max_allowed = max_sample_size();
if (max_allowed > 0 &&
(max_allowed < max_length || max_allowed < data_length)) {
LOGE("Output too large (or buffer too small).");
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
}
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_ == nullptr)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;
}
uint32_t CryptoEngine::SessionTypeBits(SessionId sid) {
return sid >> kSessionIdTypeShift;
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,270 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_ENGINE_REF_H_
#define REF_OEMCRYPTO_ENGINE_REF_H_
#include <openssl/rsa.h>
#include <stdint.h>
#include <time.h>
#include <map>
#include <memory>
#include <mutex>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCAS.h"
#include "file_store.h"
#include "oemcrypto_auth_ref.h"
#include "oemcrypto_entitled_key_session.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session.h"
#include "oemcrypto_types.h"
#include "oemcrypto_usage_table_ref.h"
namespace wvoec_ref {
typedef std::map<SessionId, SessionContext*> ActiveSessions;
static const std::string kStoredUsageTimeFileName = "StoredUsageTime.dat";
typedef struct {
// The max time recorded
int64_t previous_time;
// If the wall time is rollbacked to before the previous_time, this member
// is updated to reflect the offset.
int64_t rollback_offset;
// Pad the struct so that TimeInfo is a multiple of 16.
uint8_t padding[16 - (2 * sizeof(time_t)) % 16];
} TimeInfo;
// Session types are higher (32 - kSessionIdTypeShift) bits in SessionId.
typedef enum SessionType {
kSessionTypeOEMCrypto = 0,
kSessionTypeEntitledKey = 1,
} SessionType;
class CryptoEngine {
public:
static const uint32_t kApiVersion = 16;
static const uint32_t kMinorApiVersion = 0;
static const int64_t kTimeInfoUpdateWindowInSeconds = 300;
// 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.
// NOTE: The caller must instantiate a FileSystem object - ownership
// will be transferred to the new CryptoEngine object.
static CryptoEngine* MakeCryptoEngine(
std::unique_ptr<wvutil::FileSystem>&& file_system);
virtual ~CryptoEngine();
virtual bool Initialize();
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);
}
bool UseTestKeybox(const uint8_t* keybox_data, size_t keybox_length) {
return root_of_trust_.UseTestKeybox(keybox_data, keybox_length);
}
bool LoadTestRsaKey() { return root_of_trust_.LoadTestRsaKey(); }
KeyboxError ValidateKeybox() { return root_of_trust_.ValidateKeybox(); }
const std::vector<uint8_t>& DeviceRootKey() {
return root_of_trust_.DeviceKey();
}
const std::vector<uint8_t>& DeviceRootId() {
return root_of_trust_.DeviceId();
}
size_t DeviceRootTokenLength() { return root_of_trust_.DeviceTokenLength(); }
const uint8_t* DeviceRootToken() {
return root_of_trust_.DeviceToken();
}
virtual void Terminate();
virtual SessionId OpenSession();
virtual bool DestroySession(SessionId sid);
// |sid| can be either oemcrypto session or entitled key session.
SessionContext* FindSession(SessionId sid);
virtual OEMCryptoResult CreateEntitledKeySession(SessionId oec_sid,
SessionId* key_sid);
virtual OEMCryptoResult RemoveEntitledKeySession(SessionId key_sid);
EntitledKeySession* FindEntitledKeySession(SessionId key_sid);
size_t GetNumberOfOpenSessions() { return sessions_.size(); }
size_t GetMaxNumberOfSessions() {
// An arbitrary limit for ref implementation.
static const size_t kMaxSupportedOEMCryptoSessions = 64;
return kMaxSupportedOEMCryptoSessions;
}
// The OEMCrypto system time. Prevents time rollback.
int64_t SystemTime();
// Verify that this nonce does not collide with another nonce in any session.
virtual bool NonceCollision(uint32_t nonce);
// 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();
// Return true if there might be analog video output enabled.
virtual bool analog_display_active() {
return !config_local_display_only();
}
// Return true if there is an analog display, and CGMS A is turned on.
virtual bool cgms_a_active() { return false; }
// Return the analog output flags.
virtual uint32_t analog_output_flags() {
return config_local_display_only() ? OEMCrypto_No_Analog_Output
: OEMCrypto_Supports_Analog_Output;
}
UsageTable& usage_table() { return *(usage_table_.get()); }
wvutil::FileSystem* file_system() { return file_system_.get(); }
// 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(uint8_t* public_cert,
size_t* public_cert_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult load_oem_private_key(SessionContext* session) {
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 subsample size for
// DecryptCENC. This is not the same as the max sample or buffer size.
virtual size_t max_subsample_size() { return 1024 * 100; } // 100 KiB
// If 0 no restriction, otherwise it's the max sample size for DecryptCENC.
// This is the same as the max input and output buffer size for DecryptCENC
// and CopyBuffer. It is not the same as the max subsample size.
virtual size_t max_sample_size() { return 1024 * 1024; } // 1 MiB
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; }
// Rate limit for nonce generation. Default to 200 nonce/second.
virtual int nonce_flood_count() { return 200; }
// Limit for size of usage table. If this is zero, then the
// size is unlimited -- or limited only by memory size.
virtual size_t max_usage_table_size() { return 0; }
virtual uint32_t resource_rating() { return 1; }
// Set destination pointer based on the output destination description.
OEMCryptoResult SetDestination(
const 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(
const 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(
const OEMCrypto_DestBufferDesc& out_description,
uint8_t subsample_flags) {
return OEMCrypto_SUCCESS;
}
// Get the session type bits from |sid|.
static uint32_t SessionTypeBits(SessionId sid);
protected:
// System clock, measuring time in seconds, including anti-rollback offset.
int64_t MonotonicTime();
bool LoadOfflineTimeInfo(const std::string& file_path);
bool SaveOfflineTimeInfo(const std::string& file_path);
std::string GetUsageTimeFileFullPath() const;
explicit CryptoEngine(std::unique_ptr<wvutil::FileSystem>&& file_system);
virtual SessionContext* MakeSession(SessionId sid);
virtual UsageTable* MakeUsageTable();
uint8_t* destination_;
ActiveSessions sessions_;
AuthenticationRoot root_of_trust_;
std::mutex session_table_lock_;
std::unique_ptr<wvutil::FileSystem> file_system_;
std::unique_ptr<UsageTable> usage_table_;
TimeInfo offline_time_info_;
std::unique_ptr<EntitledKeySessionTable> entitled_key_session_table_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_ENGINE_REF_H_

View File

@@ -0,0 +1,120 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_entitled_key_session.h"
namespace wvoec_ref {
const Key* EntitledKeySession::GetEntitlementKey(
const KeyId& content_key_id) const {
if (entitlement_key_map_.find(content_key_id) == entitlement_key_map_.end()) {
return nullptr;
}
return entitlement_key_map_.at(content_key_id);
}
EntitledKey* EntitledKeySession::GetContentKey(
const KeyId& content_key_id) const {
if (content_key_map_.find(content_key_id) == content_key_map_.end()) {
return nullptr;
}
return content_key_map_.at(content_key_id).get();
}
bool EntitledKeySession::AddOrUpdateContentKey(
Key* entitlement_key, const KeyId& content_key_id,
std::unique_ptr<EntitledKey> content_key) {
if (entitlement_key == nullptr || content_key_id.empty() ||
content_key == nullptr) {
return false;
}
// Remove the entry if |entitlement_key| already exists. Each entitlement key
// can only be referred by one content key within an entitled key session.
for (auto const& content_id_entitlement : entitlement_key_map_) {
if (content_id_entitlement.second == entitlement_key) {
RemoveContentKey(content_id_entitlement.first);
break;
}
}
// |content_key_id| must be unique.
if (content_key_map_.find(content_key_id) != content_key_map_.end()) {
return false;
}
content_key_map_[content_key_id] = std::move(content_key);
entitlement_key_map_[content_key_id] = entitlement_key;
return true;
}
bool EntitledKeySession::RemoveContentKey(const KeyId& content_key_id) {
if (content_key_map_.find(content_key_id) == content_key_map_.end()) {
return false;
}
content_key_map_.erase(content_key_id);
entitlement_key_map_.erase(content_key_id);
return true;
}
/***************************************/
EntitledKeySession* EntitledKeySessionTable::CreateEntitledKeySession(
SessionId oec_sid, SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_lock_);
// The given |key_sid| already exists.
if (entitled_key_sessions_.find(key_sid) != entitled_key_sessions_.end()) {
return nullptr;
}
entitled_key_sessions_[key_sid] =
std::unique_ptr<EntitledKeySession>(new EntitledKeySession(key_sid));
oec_session_to_key_sessions_[oec_sid].insert(oec_sid);
key_session_to_oec_session_[key_sid] = oec_sid;
return entitled_key_sessions_[key_sid].get();
}
EntitledKeySession* EntitledKeySessionTable::FindEntitledKeySession(
SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_lock_);
if (entitled_key_sessions_.find(key_sid) == entitled_key_sessions_.end()) {
return nullptr;
}
return entitled_key_sessions_.at(key_sid).get();
}
void EntitledKeySessionTable::RemoveEntitledKeySession(SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_lock_);
if (entitled_key_sessions_.find(key_sid) == entitled_key_sessions_.end()) {
return;
}
SessionId oec_sid = key_session_to_oec_session_[key_sid];
key_session_to_oec_session_.erase(key_sid);
oec_session_to_key_sessions_[oec_sid].erase(key_sid);
entitled_key_sessions_.erase(key_sid);
}
SessionId EntitledKeySessionTable::GetOEMCryptoSessionId(SessionId key_sid) {
std::unique_lock<std::mutex> lock(session_lock_);
if (key_session_to_oec_session_.find(key_sid) ==
key_session_to_oec_session_.end()) {
return 0;
}
return key_session_to_oec_session_.at(key_sid);
}
void EntitledKeySessionTable::OnDestroyOEMCryptoSession(SessionId oec_sid) {
std::unique_lock<std::mutex> lock(session_lock_);
if (oec_session_to_key_sessions_.find(oec_sid) ==
oec_session_to_key_sessions_.end()) {
return;
}
for (auto entitled_key_sid : oec_session_to_key_sessions_[oec_sid]) {
key_session_to_oec_session_.erase(entitled_key_sid);
entitled_key_sessions_.erase(entitled_key_sid);
}
oec_session_to_key_sessions_.erase(oec_sid);
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,110 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef OEMCRYPTO_ENTITLED_KEY_SESSION_H_
#define OEMCRYPTO_ENTITLED_KEY_SESSION_H_
#include <cstdint>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <unordered_map>
#include <vector>
#include "oemcrypto_key_ref.h"
namespace wvoec_ref {
typedef std::vector<uint8_t> KeyId;
typedef uint32_t SessionId;
class EntitledKeySession {
public:
explicit EntitledKeySession(SessionId key_sid)
: key_sid_(key_sid),
current_content_key_(nullptr),
current_entitlement_key_(nullptr){};
EntitledKeySession(const EntitledKeySession&) = delete;
EntitledKeySession(EntitledKeySession&&) = delete;
~EntitledKeySession() = default;
// Get id of this entitled key session .
SessionId GetSessionId() const { return key_sid_; }
// Get reference of the entitlement key that entitles |content_key_id|.
const Key* GetEntitlementKey(const KeyId& content_key_id) const;
// Get content key whose id is |content_key_id|.
EntitledKey* GetContentKey(const KeyId& content_key_id) const;
// Select current content key to |content_key_id|, and current entitlement
// key to its corresponding entitlement key.
void SetCurrentKey(const KeyId& content_key_id) {
current_content_key_ = GetContentKey(content_key_id);
current_entitlement_key_ = GetEntitlementKey(content_key_id);
}
// Return the current content key selected, nullptr if none is selected.
const EntitledKey* CurrentContentKey() const { return current_content_key_; }
// Return the current entitlement key selected, nullptr if none is selected.
const Key* CurrentEntitlementKey() const { return current_entitlement_key_; }
// Update the content key that refers to |entitlement_key| or insert a new
// entry that refers to |entitlement_key|. |entitlement_key| must not be null.
bool AddOrUpdateContentKey(Key* entitlement_key, const KeyId& content_key_id,
std::unique_ptr<EntitledKey> content_key);
// Remove a content key |content_key_id|.
bool RemoveContentKey(const KeyId& content_key_id);
// Total number of content keys this entitled key session holds.
size_t size() const { return content_key_map_.size(); }
private:
const SessionId key_sid_;
EntitledKey* current_content_key_;
const Key* current_entitlement_key_;
// Map from content key id to content key.
std::map<KeyId, std::unique_ptr<EntitledKey>> content_key_map_;
// Map from content key id to referenced entitlement key.
std::map<KeyId, Key*> entitlement_key_map_;
};
class EntitledKeySessionTable {
public:
EntitledKeySessionTable() = default;
EntitledKeySessionTable(const EntitledKeySessionTable&) = delete;
EntitledKeySessionTable(EntitledKeySessionTable&&) = delete;
~EntitledKeySessionTable() = default;
// Create an entitled key session with given id |key_sid|, which refers to
// OEMCrypto session |oec_sid|.
EntitledKeySession* CreateEntitledKeySession(SessionId oec_sid,
SessionId key_sid);
// Return the entitled key session that has |key_sid| as the id.
EntitledKeySession* FindEntitledKeySession(SessionId key_sid);
// Remove the entitled key session that has |key_sid| as the id.
void RemoveEntitledKeySession(SessionId key_sid);
// Get the referenced OEMCrypto session id of entitled key session |key_sid|.
SessionId GetOEMCryptoSessionId(SessionId key_sid);
// Remove all entitled key sessions that refer to |oec_sid|.
void OnDestroyOEMCryptoSession(SessionId oec_sid);
// Total number of active entitled key sessions.
size_t size() const { return entitled_key_sessions_.size(); }
private:
std::mutex session_lock_;
// Map from entitled key session id to entitled key session.
std::unordered_map<SessionId, std::unique_ptr<EntitledKeySession>>
entitled_key_sessions_;
// Map from entitled key session id to referenced OEMCrypto session id.
std::unordered_map<SessionId, SessionId> key_session_to_oec_session_;
// Map from OEMCrypto session id to the set of entitled key sessions that
// refers to this OEMCrypto session.
std::unordered_map<SessionId, std::set<SessionId>>
oec_session_to_key_sessions_;
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_ENTITLED_KEY_SESSION_H_

View File

@@ -0,0 +1,71 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_key_ref.h"
#include "oemcrypto_types.h"
#include <string.h>
#include <vector>
#include "log.h"
namespace wvoec_ref {
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 12 api
memcmp(verification_, "kc13", 4) && // add in version 13 api
memcmp(verification_, "kc14", 4) && // add in version 14 api
memcmp(verification_, "kc15", 4) && // add in version 15 api
memcmp(verification_, "kc16", 4)) { // add in version 16 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() < wvoec::KEY_CONTROL_SIZE) {
LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(),
wvoec::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);
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_ |= wvoec::kControlHDCPVersionMask;
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,85 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_KEY_REF_H_
#define OEMCRYPTO_KEY_REF_H_
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
namespace wvoec_ref {
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(std::vector<uint8_t> key_string, const KeyControlBlock& control)
: value_(std::move(key_string)), control_(control), ctr_mode_(true){};
Key(const Key&) = default;
Key(Key&&) = default;
virtual ~Key() = default;
void UpdateDuration(const KeyControlBlock& control);
virtual const std::vector<uint8_t>& value() const { return value_; }
const KeyControlBlock& control() const { return control_; }
bool ctr_mode() const { return ctr_mode_; }
void set_ctr_mode(bool ctr_mode) { ctr_mode_ = ctr_mode; }
private:
std::vector<uint8_t> value_;
KeyControlBlock control_;
bool ctr_mode_;
};
// EntitledKey does not own KeyControlBlock.
class EntitledKey {
public:
explicit EntitledKey(std::vector<uint8_t> key_string)
: value_(std::move(key_string)), ctr_mode_(true){};
EntitledKey(const EntitledKey&) = default;
EntitledKey(EntitledKey&&) = default;
~EntitledKey() = default;
const std::vector<uint8_t>& value() const { return value_; }
bool ctr_mode() const { return ctr_mode_; }
void set_ctr_mode(bool ctr_mode) { ctr_mode_ = ctr_mode; }
private:
std::vector<uint8_t> value_;
bool ctr_mode_;
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_KEY_REF_H_

View File

@@ -0,0 +1,74 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_keybox_ref.h"
#include <string.h>
#include <cstdint>
#include <string>
#include "log.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "wvcrc32.h"
namespace wvoec_ref {
WvKeybox::WvKeybox() : loaded_(false) {
static std::string fake_device_id = "device_with_no_keybox";
device_id_.assign(fake_device_id.begin(), fake_device_id.end());
}
KeyboxError WvKeybox::Validate() {
if (!loaded_) {
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;
uint8_t* crc_stored_bytes = (uint8_t*)&crc_stored;
memcpy(crc_stored_bytes, crc_, sizeof(crc_));
wvoec::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 wvoec::WidevineKeybox* keybox =
reinterpret_cast<const wvoec::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_));
loaded_ = true;
return true;
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,46 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_KEYBOX_REF_H_
#define OEMCRYPTO_KEYBOX_REF_H_
#include "oemcrypto_key_ref.h"
namespace wvoec_ref {
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 loaded_;
std::vector<uint8_t> device_id_;
std::vector<uint8_t> device_key_;
WvKeyboxKeyData key_data_;
uint8_t magic_[4];
uint8_t crc_[4];
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_KEYBOX_REF_H_

View File

@@ -0,0 +1,76 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_nonce_table.h"
namespace wvoec_ref {
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;
}
bool NonceTable::NonceCollision(uint32_t nonce) const {
for (int i = 0; i < kTableSize; ++i) {
if (nonce == nonces_[i]) return true;
}
return false;
}
void NonceTable::Flush() {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateFlushPending == state_[i]) {
state_[i] = kNTStateInvalid;
}
}
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,42 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_NONCE_TABLE_H_
#define REF_OEMCRYPTO_NONCE_TABLE_H_
#include <stdint.h>
namespace wvoec_ref {
class NonceTable {
public:
static const int kTableSize = 4;
NonceTable() {
for (int i = 0; i < kTableSize; ++i) {
state_[i] = kNTStateInvalid;
}
}
~NonceTable() {}
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce is not the same as any in this table.
bool NonceCollision(uint32_t nonce) const;
void Flush();
private:
enum NonceTableState {
kNTStateInvalid,
kNTStateValid,
kNTStateFlushPending
};
NonceTableState state_[kTableSize];
uint32_t age_[kTableSize];
uint32_t nonces_[kTableSize];
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_NONCE_TABLE_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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 "log.h"
namespace wvoec_ref {
void dump_boringssl_error() {
int count = 0;
while (unsigned long err = ERR_get_error()) {
count++;
char buffer[120];
ERR_error_string_n(err, buffer, sizeof(buffer));
LOGE("BoringSSL Error %d -- %lu -- %s", count, err, buffer);
}
LOGE("Reported %d BoringSSL Errors", count);
}
void RSA_shared_ptr::reset() {
if (rsa_key_ && key_owned_) {
RSA_free(rsa_key_);
}
key_owned_ = false;
rsa_key_ = nullptr;
}
bool RSA_shared_ptr::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) {
assert(buffer != nullptr);
reset();
uint8_t* pkcs8_rsa_key = const_cast<uint8_t*>(buffer);
BIO* bio = BIO_new_mem_buf(pkcs8_rsa_key, length);
if (bio == nullptr) {
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, nullptr);
if (pkcs8_pki == nullptr) {
BIO_reset(bio);
pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr]");
dump_boringssl_error();
success = false;
}
}
EVP_PKEY* evp = nullptr;
if (success) {
evp = EVP_PKCS82PKEY(pkcs8_pki);
if (evp == nullptr) {
LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned nullptr]");
dump_boringssl_error();
success = false;
}
}
if (success) {
rsa_key_ = EVP_PKEY_get1_RSA(evp);
if (rsa_key_ == nullptr) {
LOGE("[LoadPkcs8RsaKey(): PrivateKeyInfo did not contain an RSA key]");
success = false;
}
key_owned_ = true;
}
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
if (pkcs8_pki != nullptr) {
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_boringssl_error();
return false;
default: // -1 == check failed.
LOGE("[LoadPkcs8RsaKey(): error checking rsa key]");
dump_boringssl_error();
return false;
}
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,42 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference 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_ref {
// 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_(nullptr), 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_;
};
// Log errors from BoringSSL.
void dump_boringssl_error();
} // namespace wvoec_ref
#endif // OEMCRYPTO_RSA_KEY_SHARED_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_SESSION_H_
#define REF_OEMCRYPTO_SESSION_H_
#include <stdint.h>
#include <time.h>
#include <map>
#include <vector>
#include <openssl/rsa.h>
#include "OEMCryptoCAS.h"
#include "odk_structs.h"
#include "oemcrypto_auth_ref.h"
#include "oemcrypto_entitled_key_session.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_types.h"
#include "oemcrypto_usage_table_ref.h"
namespace wvoec_ref {
class CryptoEngine;
typedef uint32_t SessionId;
enum SRMVersionStatus { NoSRMVersion, ValidSRMVersion, InvalidSRMVersion };
class SessionContext {
public:
SessionContext(CryptoEngine* ce, SessionId sid,
const RSA_shared_ptr& rsa_key);
SessionContext() = delete;
virtual ~SessionContext();
bool isValid() { return valid_; }
virtual bool DeriveKeys(const std::vector<uint8_t>& master_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
virtual bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
virtual OEMCryptoResult PrepAndSignLicenseRequest(uint8_t* message,
size_t message_length,
size_t* core_message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult PrepAndSignRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult PrepAndSignProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint8_t* signature, size_t* signature_length);
// The size of an RSA signature. This is used when signing as a CAST
// receiver.
size_t RSASignatureSize();
virtual OEMCryptoResult GenerateRSASignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length, RSA_Padding_Scheme padding_scheme);
virtual bool ValidateMessage(const uint8_t* message, size_t message_length,
const uint8_t* signature,
size_t signature_length);
// |key_session| may be null if the session is content license.
OEMCryptoResult DecryptSamples(
const EntitledKeySession* key_session,
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult Generic_Encrypt(const EntitledKeySession* key_session,
const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Decrypt(const EntitledKeySession* key_session,
const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Sign(const EntitledKeySession* key_session,
const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature, size_t* signature_length);
OEMCryptoResult Generic_Verify(const EntitledKeySession* key_session,
const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult LoadLicense(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult LoadKeys(
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length, OEMCrypto_Substring enc_mac_keys_iv,
OEMCrypto_Substring enc_mac_keys, size_t num_keys,
const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst,
OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadKeysNoSignature(
const uint8_t* message, size_t message_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadEntitledContentKeys(
EntitledKeySession* key_session, const uint8_t* message,
size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array);
virtual OEMCryptoResult LoadEntitledCasKeys(
EntitledKeySession* key_session, const uint8_t* message,
size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledCasKeyObject* key_array);
virtual 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 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);
virtual OEMCryptoResult LoadRenewal(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult RefreshKey(
const KeyId& key_id, const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv);
virtual bool UpdateMacKeys(const std::vector<uint8_t>& mac_keys,
const std::vector<uint8_t>& iv);
virtual bool QueryKeyControlBlock(EntitledKeySession* key_session,
const KeyId& key_id, uint32_t* data);
virtual OEMCryptoResult SelectEntitledContentKey(
EntitledKeySession* key_session, const KeyId& key_id,
OEMCryptoCipherMode cipher_mode);
virtual OEMCryptoResult SelectContentKey(const KeyId& key_id,
OEMCryptoCipherMode cipher_mode);
virtual OEMCryptoResult SetDecryptHash(uint32_t frame_number,
const uint8_t* hash,
size_t hash_length);
virtual OEMCryptoResult GetHashErrorCode(uint32_t* failed_frame_number);
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_; }
// Return true if nonce was set.
bool set_nonce(uint32_t nonce);
uint32_t nonce() const { return nonce_values_.nonce; }
ODK_NonceValues& nonce_values() { return nonce_values_; }
bool CheckNonce(uint32_t nonce) const {
return nonce != 0 && nonce == nonce_values_.nonce;
};
virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number);
virtual OEMCryptoResult LoadUsageEntry(uint32_t index,
const std::vector<uint8_t>& buffer);
virtual OEMCryptoResult UpdateUsageEntry(uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
virtual OEMCryptoResult DeactivateUsageEntry(const std::vector<uint8_t>& pst);
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
OEMCryptoResult MoveEntry(uint32_t new_index);
bool usage_entry_present() const { return usage_entry_ != nullptr; }
protected:
// Signature size of the currently loaded private key.
size_t CertSignatureSize();
// Signature size when using a keybox or OEM Cert's private key.
size_t ROTSignatureSize();
virtual OEMCryptoResult GenerateCertSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
virtual OEMCryptoResult GenerateSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
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,
uint32_t key_size); // AES key size, in bits.
// 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 DecryptSubsample(
const EntitledKeySession* key_session,
const OEMCrypto_SubSampleDescription& subsample,
const uint8_t* cipher_data, uint8_t* clear_data,
OEMCryptoBufferType buffer_type, const uint8_t (&iv)[wvoec::KEY_IV_SIZE],
const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult ChooseDecrypt(const EntitledKeySession* key_session,
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,
OEMCryptoBufferType buffer_type);
OEMCryptoResult PatternDecryptCBC(
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 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);
OEMCryptoResult CheckKeyControlBlockUse(const KeyControlBlock& control,
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_;
std::unique_ptr<SessionKeyTable> session_keys_;
ODK_NonceValues nonce_values_;
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
RSA_shared_ptr rsa_key_;
uint32_t allowed_schemes_; // for RSA signatures.
bool decrypt_started_; // If the license has been used in this session.
ODK_TimerLimits timer_limits_;
ODK_ClockValues clock_values_;
std::unique_ptr<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_;
// These are used when doing full decrypt path testing.
bool compute_hash_; // True if the current frame needs a hash.
uint32_t current_hash_; // Running CRC hash of frame.
uint32_t given_hash_; // True CRC hash of frame.
uint32_t current_frame_number_; // Current frame for CRC hash.
uint32_t bad_frame_number_; // Frame number with bad hash.
OEMCryptoResult hash_error_; // Error code for first bad frame.
// The bare minimum state machine is to only call each of these function
// categories at most once.
bool state_nonce_created_;
bool state_request_signed_;
bool state_response_loaded_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_SESSION_H_

View File

@@ -0,0 +1,44 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_session_key_table.h"
namespace wvoec_ref {
bool SessionKeyTable::Insert(const KeyId& key_id,
std::unique_ptr<Key> key_data) {
if (keys_.find(key_id) != keys_.end()) {
return false;
}
keys_[key_id] = std::move(key_data);
return true;
}
Key* SessionKeyTable::Find(const KeyId& key_id) {
if (keys_.find(key_id) == keys_.end()) {
return nullptr;
}
return keys_[key_id].get();
}
Key* SessionKeyTable::FirstKey() {
return keys_.empty() ? nullptr : keys_.begin()->second.get();
}
void SessionKeyTable::Remove(const KeyId& key_id) {
if (keys_.find(key_id) == keys_.end()) {
return;
}
keys_.erase(key_id);
}
void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
for (auto& key : keys_) {
key.second->UpdateDuration(control);
}
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,47 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_SESSION_KEY_TABLE_H_
#define REF_OEMCRYPTO_SESSION_KEY_TABLE_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <vector>
#include "OEMCryptoCAS.h"
#include "oemcrypto_key_ref.h"
namespace wvoec_ref {
typedef std::vector<uint8_t> KeyId;
// SessionKeyTable holds the keys for the current session
class SessionKeyTable {
public:
explicit SessionKeyTable(OEMCrypto_LicenseType type) : type_(type){};
SessionKeyTable(const SessionKeyTable&) = delete;
SessionKeyTable(SessionKeyTable&&) = delete;
~SessionKeyTable() = default;
bool Insert(const KeyId& key_id, std::unique_ptr<Key> key_data);
Key* Find(const KeyId& key_id);
Key* FirstKey();
void Remove(const KeyId& key_id);
void UpdateDuration(const KeyControlBlock& control);
OEMCrypto_LicenseType type() { return type_; }
size_t size() const { return keys_.size(); }
private:
std::map<KeyId, std::unique_ptr<Key>> keys_;
const OEMCrypto_LicenseType type_;
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_SESSION_KEY_TABLE_H_

View File

@@ -0,0 +1,703 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_usage_table_ref.h"
#include <string.h>
#include <time.h>
#include <string>
#include <vector>
#include <openssl/aes.h>
#include <openssl/crypto.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "file_store.h"
#include "log.h"
#include "odk.h"
#include "oemcrypto_engine_ref.h"
// TODO(fredgc): Setting the device files base bath is currently broken as
// wvutil::Properties is no longer used by the reference code.
//#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
namespace wvoec_ref {
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 || !pst_length) return OEMCrypto_ERROR_INVALID_CONTEXT;
memcpy(data_.pst, pst, pst_length);
data_.time_of_license_received = usage_table_->ce_->SystemTime();
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 || !pst_length) return false;
return 0 == CRYPTO_memcmp(pst, data_.pst, pst_length);
}
bool UsageTableEntry::VerifyMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client) {
return (server.size() == wvoec::MAC_KEY_SIZE) &&
(client.size() == wvoec::MAC_KEY_SIZE) &&
(0 == CRYPTO_memcmp(&server[0], data_.mac_key_server,
wvoec::MAC_KEY_SIZE)) &&
(0 ==
CRYPTO_memcmp(&client[0], data_.mac_key_client, wvoec::MAC_KEY_SIZE));
}
bool UsageTableEntry::SetMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& client) {
if ((server.size() != wvoec::MAC_KEY_SIZE) ||
(client.size() != wvoec::MAC_KEY_SIZE))
return false;
memcpy(data_.mac_key_server, &server[0], wvoec::MAC_KEY_SIZE);
memcpy(data_.mac_key_client, &client[0], wvoec::MAC_KEY_SIZE);
return true;
}
void UsageTableEntry::ForbidReport() {
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 (CRYPTO_memcmp(&pst[0], data_.pst, data_.pst_length)) {
LOGE("ReportUsage: wrong pst %s, should be %s.", wvutil::b2a_hex(pst).c_str(),
wvutil::HexEncode(data_.pst, data_.pst_length).c_str());
return OEMCrypto_ERROR_WRONG_PST;
}
size_t length_needed = wvutil::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;
}
wvutil::Unpacked_PST_Report pst_report(buffer);
int64_t now = usage_table_->ce_->SystemTime();
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, wvoec::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(ODK_ClockValues* clock_values) {
if (recent_decrypt_) {
data_.time_of_last_decrypt = usage_table_->ce_->SystemTime();
recent_decrypt_ = false;
}
data_.time_of_license_received = clock_values->time_of_license_signed;
data_.time_of_first_decrypt = clock_values->time_of_first_decrypt;
// Use the most recent time_of_last_decrypt.
if (static_cast<uint64_t>(data_.time_of_last_decrypt) <
clock_values->time_of_last_decrypt) {
// For the reference implementation, we update the clock_values on every
// decrypt.
data_.time_of_last_decrypt = clock_values->time_of_last_decrypt;
} else {
// For this reference implementation of OEMCrypto, we regularly update
// clock_values->time_of_last_decrypt and we could just update
// data_.time_of_last_decrypt here. However, I'm including the line below to
// make it clear that you could do it the other way around. When this
// function is called, the two values should be synced so that the usage
// entry can be saved with the correct value.
clock_values->time_of_last_decrypt = data_.time_of_last_decrypt;
}
data_.status = clock_values->status;
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 = 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 std::vector<uint8_t>& key = ce->DeviceRootKey();
if (key.empty()) {
LOGE("SaveUsageEntry: DeviceRootKey is unexpectedly empty.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE);
uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvoec::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,
ODK_ClockValues* clock_values) {
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 std::vector<uint8_t>& key = ce->DeviceRootKey();
if (key.empty()) {
LOGE("LoadUsageEntry: DeviceRootKey is unexpectedly empty.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// 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 (CRYPTO_memcmp(clear->signature, encrypted->signature,
SHA256_DIGEST_LENGTH)) {
LOGE("LoadUsageEntry: Signature did not match.");
LOGE("LoadUsageEntry: Invalid signature given: %s",
wvutil::HexEncode(encrypted->signature, sig_length).c_str());
LOGE("LoadUsageEntry: Invalid signature computed: %s",
wvutil::HexEncode(clear->signature, sig_length).c_str());
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Next, decrypt the entry.
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted->iv, wvoec::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",
wvutil::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvutil::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;
}
data_ = clear->data;
return ODK_ReloadClockValues(
clock_values, data_.time_of_license_received, data_.time_of_first_decrypt,
data_.time_of_last_decrypt, data_.status, ce->SystemTime());
}
size_t UsageTableEntry::SignedEntrySize() {
size_t base = sizeof(SignedEntryBlock);
// round up to make even number of blocks:
size_t blocks = (base - 1) / wvoec::KEY_IV_SIZE + 1;
return blocks * wvoec::KEY_IV_SIZE;
}
UsageTable::~UsageTable() {}
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) / wvoec::KEY_IV_SIZE + 1;
return blocks * wvoec::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, ODK_ClockValues* clock_values) {
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(clock_values);
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;
}
UsageTableEntry* UsageTable::MakeEntry(uint32_t index) {
return new UsageTableEntry(this, index, master_generation_number_);
}
OEMCryptoResult UsageTable::CreateNewUsageEntry(
SessionContext* session, std::unique_ptr<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();
size_t max = ce_->max_usage_table_size();
if (max > 0 && index >= max) {
LOGE("Too many usage entries: %d/%d", index, max);
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
UsageTableEntry* new_entry = MakeEntry(index);
generation_numbers_.push_back(master_generation_number_);
sessions_.push_back(session);
master_generation_number_++;
entry->reset(new_entry);
*usage_entry_number = index;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::LoadUsageEntry(
SessionContext* session, std::unique_ptr<UsageTableEntry>* entry,
uint32_t index, const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
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;
}
size_t max = ce_->max_usage_table_size();
if (max > 0 && index >= max) {
LOGE("Too many usage entries: %d/%d", index, max);
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
std::unique_ptr<UsageTableEntry> new_entry(MakeEntry(index));
OEMCryptoResult status =
new_entry->LoadData(ce_, index, buffer, clock_values);
if (status != OEMCrypto_SUCCESS) {
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])) {
return OEMCrypto_ERROR_GENERATION_SKEW;
}
status = OEMCrypto_WARNING_GENERATION_SKEW;
}
sessions_[index] = session;
*entry = std::move(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 std::vector<uint8_t>& key = ce_->DeviceRootKey();
if (key.empty()) {
LOGE("SaveUsageTableHeader: DeviceRootKey is unexpectedly empty.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Encrypt the entry.
RAND_bytes(encrypted->iv, wvoec::KEY_IV_SIZE);
uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; // working iv buffer.
memcpy(iv_buffer, encrypted->iv, wvoec::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;
size_t max = ce_->max_usage_table_size();
if (max > 0 && buffer.size() > SignedHeaderSize(max)) {
LOGE("Header too big: %zd bytes/%zd bytes",
buffer.size(), SignedHeaderSize(max));
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
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 std::vector<uint8_t>& key = ce_->DeviceRootKey();
if (key.empty()) {
LOGE("LoadUsageTableHeader: DeviceRootKey is unexpectedly empty.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// 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 (CRYPTO_memcmp(clear->signature, encrypted->signature,
SHA256_DIGEST_LENGTH)) {
LOGE("LoadUsageTableHeader: Signature did not match.");
LOGE("LoadUsageTableHeader: Invalid signature given: %s",
wvutil::HexEncode(encrypted->signature, sig_length).c_str());
LOGE("LoadUsageTableHeader: Invalid signature computed: %s",
wvutil::HexEncode(clear->signature, sig_length).c_str());
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Next, decrypt the entry.
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted->iv, wvoec::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",
wvutil::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvutil::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() {
wvutil::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.
// TODO(jfore, rfrias): Address how this property is presented to the ref.
// For now, the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(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";
auto file = file_system->Open(
filename, wvutil::FileSystem::kCreate | wvutil::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));
return true;
}
bool UsageTable::LoadGenerationNumber(bool or_make_new_one) {
wvutil::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.
// TODO(jfore, rfrias): Address how this property is presented to the ref.
// For now, the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(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";
auto file = file_system->Open(filename, wvutil::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));
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] != nullptr) {
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);
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,131 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef OEMCRYPTO_USAGE_TABLE_REF_H_
#define OEMCRYPTO_USAGE_TABLE_REF_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include "OEMCryptoCAS.h"
#include "odk_structs.h"
#include "oemcrypto_types.h"
#include "openssl/sha.h"
namespace wvoec_ref {
class SessionContext;
class CryptoEngine;
class UsageTable;
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[wvoec::MAC_KEY_SIZE];
uint8_t mac_key_client[wvoec::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);
virtual ~UsageTableEntry(); // Free memory, remove reference in header.
bool Inactive() { return data_.status >= kInactive; }
// Mark this entry as modified and forbid a usage report until the data has
// been saved. This is done on important events like first decrypt and
// deactivation.
void ForbidReport();
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);
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
virtual void UpdateAndIncrement(ODK_ClockValues* clock_values);
// Save all data to the given buffer. This should be called after updating the
// data.
OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session,
uint8_t* signed_buffer, size_t buffer_size);
// Load all data from the buffer, and then update clock_values.
OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values);
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; }
void set_recent_decrypt(bool recent_decrypt) {
recent_decrypt_ = recent_decrypt;
}
static size_t SignedEntrySize();
const uint8_t* mac_key_server() const { return data_.mac_key_server; }
const uint8_t* mac_key_client() const { return data_.mac_key_client; }
protected:
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){};
virtual ~UsageTable();
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
std::unique_ptr<UsageTableEntry>* entry,
uint32_t* usage_entry_number);
OEMCryptoResult LoadUsageEntry(SessionContext* session,
std::unique_ptr<UsageTableEntry>* entry,
uint32_t index,
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values);
OEMCryptoResult UpdateUsageEntry(
SessionContext* session, UsageTableEntry* entry, uint8_t* header_buffer,
size_t* header_buffer_length, uint8_t* entry_buffer,
size_t* entry_buffer_length, ODK_ClockValues* clock_values);
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);
protected:
virtual UsageTableEntry* MakeEntry(uint32_t index);
virtual OEMCryptoResult SaveUsageTableHeader(uint8_t* signed_buffer,
size_t buffer_size);
virtual bool SaveGenerationNumber();
virtual 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_;
friend class UsageTableEntry;
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_USAGE_TABLE_REF_H_

View File

@@ -0,0 +1,57 @@
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "ts_parser.h"
namespace wvoec_ref {
uint8_t TransportStreamParser::sync_byte() const { return data_[kSyncByte]; }
uint16_t TransportStreamParser::pid_number() const {
return ((data_[kFlagByte] << 8) | data_[kPIDByte]) & kPIDMask;
}
bool TransportStreamParser::is_valid_pid() const {
return pid_number() >= kValidPIDLowBound &&
pid_number() <= kValidPIDUpperBound;
}
bool TransportStreamParser::is_uint_start() const {
return kPUSIndicator & data_[kFlagByte];
}
bool TransportStreamParser::is_transport_error() const {
return KTErrIndicator & data_[kFlagByte];
}
bool TransportStreamParser::is_transport_priority() const {
return kTprioIndicator & data_[kFlagByte];
}
uint8_t TransportStreamParser::scrambling_bits() const {
return (data_[kScrCtlByte] & kScrambleMask) >> kScrambleShift;
}
uint8_t TransportStreamParser::adaption_bits() const {
return (data_[kScrCtlByte] & kAdaptMask) >> kAdaptShift;
}
uint8_t TransportStreamParser::continuity_counter() const {
return data_[kScrCtlByte] & 0xF;
}
uint8_t TransportStreamParser::header_size() const {
// The 4th byte is the adaptation field length (excluding the byte itself).
return adaption_bits() >> 1 ? kTSHeaderSize + data_[kTSHeaderSize] + 1
: kTSHeaderSize;
}
const uint8_t* TransportStreamParser::payload_data() const {
return data_ + header_size();
}
uint8_t TransportStreamParser::payload_data_size() const {
return kTSPacketSize > header_size() ? kTSPacketSize - header_size() : 0;
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,87 @@
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef WVOEC_REF_TS_PARSER_H_
#define WVOEC_REF_TS_PARSER_H_
#include <cstddef>
#include <cstdint>
#include "disallow_copy_and_assign.h"
namespace wvoec_ref {
static const size_t kTSPacketSize = 188;
static const uint8_t kTSSyncByte = 0x47;
static const size_t kTSHeaderSize = 4;
static const uint16_t kValidPIDLowBound = 0x0010;
static const uint16_t kValidPIDUpperBound = 0x1FFE;
// Class wrapping packet header of MPEG-2 TS
// +========================================
// | 8 | sync byte
// +----------------------------------------
// | 1 | transport error indicator
// +----------------------------------------
// | 1 | payload unit start indicator
// +----------------------------------------
// | 1 | transport priority
// +----------------------------------------
// | 13 | PID
// +----------------------------------------
// | 2 | transport scrambling
// +----------------------------------------
// | 2 | adaptation field control
// +----------------------------------------
// | 4 | continuity counter
// +======= OPTIONAL =======================
// | ~~~ | adaptation field
// +----------------------------------------
// | ~~~ | payload data
// +========================================
class TransportStreamParser {
public:
enum { kSyncByte = 0, kFlagByte = 1, kPIDByte = 2, kScrCtlByte = 3 };
typedef enum {
kClear = 0x0,
kRSVD = 0x40,
kEvenKey = 0x80,
kOddKey = 0xC0,
kScrambleMask = 0xC0,
kScrambleShift = 6
} ScramblingBits_t;
typedef enum { kAdaptMask = 0x30, kAdaptShift = 4 } AdaptionBits_t;
static const uint16_t kPIDMask = 0x1FFF;
static const uint8_t KTErrIndicator = 0x80;
static const uint8_t kPUSIndicator = 0x40;
static const uint8_t kTprioIndicator = 0x20;
explicit TransportStreamParser(const uint8_t* buffer) : data_(buffer) {}
virtual ~TransportStreamParser() = default;
uint8_t sync_byte() const;
uint16_t pid_number() const;
bool is_uint_start() const;
bool is_transport_error() const;
bool is_transport_priority() const;
uint8_t scrambling_bits() const;
uint8_t adaption_bits() const;
uint8_t continuity_counter() const;
// Check if pid value is within the expected range.
bool is_valid_pid() const;
// Returns the pointer to the payload start.
const uint8_t* payload_data() const;
// Payload data size not counting TS header nor adaptation filed.
uint8_t payload_data_size() const;
private:
uint8_t header_size() const;
const uint8_t* data_;
CORE_DISALLOW_COPY_AND_ASSIGN(TransportStreamParser);
};
} // namespace wvoec_ref
#endif // WVOEC_REF_TS_PARSER_H_

108
oemcrypto/ref/src/wvcrc.cpp Normal file
View File

@@ -0,0 +1,108 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
//
#include "platform.h"
#include "wvcrc32.h"
namespace wvoec_ref {
#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 wvcrc32Init() {
return INIT_CRC32;
}
uint32_t wvcrc32Cont(const uint8_t* p_begin, int i_count, uint32_t prev_crc) {
return(wvrunningcrc32(p_begin, i_count, prev_crc));
}
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count) {
return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,23 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Compute CRC32 Checksum. Needed for verification of WV Keybox.
//
#ifndef WVCRC32_H_
#define WVCRC32_H_
#include <stdint.h>
namespace wvoec_ref {
uint32_t wvcrc32(const uint8_t* p_begin, int i_count);
uint32_t wvcrc32Init();
uint32_t wvcrc32Cont(const uint8_t* p_begin, int i_count, uint32_t prev_crc);
// Convert to network byte order
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count);
} // namespace wvoec_ref
#endif // WVCRC32_H_