Widevine MediaCas client code that works with Android R
This commit is contained in:
37
oemcrypto/ref/Android.bp
Normal file
37
oemcrypto/ref/Android.bp
Normal 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
5
oemcrypto/ref/README.md
Normal 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.**
|
||||
10
oemcrypto/ref/src/keys.cpp
Normal file
10
oemcrypto/ref/src/keys.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// If you are using a baked-in certificate instead of supporting keyboxes,
|
||||
// you should have received a keys.cpp from Widevine that replaces this file.
|
||||
//
|
||||
// If you are not using a baked-in certificate, then you may ignore this file,
|
||||
// as it intentionally defines an empty key.
|
||||
|
||||
#include "keys.h"
|
||||
|
||||
const uint8_t kPrivateKey[] = { 0, };
|
||||
const size_t kPrivateKeySize = 0;
|
||||
11
oemcrypto/ref/src/keys.h
Normal file
11
oemcrypto/ref/src/keys.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// This header is used to access the baked-in certificate if one is in use.
|
||||
#ifndef KEYS_H_
|
||||
#define KEYS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern const uint8_t kPrivateKey[];
|
||||
extern const size_t kPrivateKeySize;
|
||||
|
||||
#endif // KEYS_H_
|
||||
334
oemcrypto/ref/src/oem_cert.cpp
Normal file
334
oemcrypto/ref/src/oem_cert.cpp
Normal 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
|
||||
21
oemcrypto/ref/src/oem_cert.h
Normal file
21
oemcrypto/ref/src/oem_cert.h
Normal 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_
|
||||
203
oemcrypto/ref/src/oemcrypto_auth_ref.cpp
Normal file
203
oemcrypto/ref/src/oemcrypto_auth_ref.cpp
Normal 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
|
||||
81
oemcrypto/ref/src/oemcrypto_auth_ref.h
Normal file
81
oemcrypto/ref/src/oemcrypto_auth_ref.h
Normal 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_
|
||||
19
oemcrypto/ref/src/oemcrypto_engine_device_properties.cpp
Normal file
19
oemcrypto/ref/src/oemcrypto_engine_device_properties.cpp
Normal 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
|
||||
40
oemcrypto/ref/src/oemcrypto_engine_device_properties_L1.cpp
Normal file
40
oemcrypto/ref/src/oemcrypto_engine_device_properties_L1.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
337
oemcrypto/ref/src/oemcrypto_engine_ref.cpp
Normal file
337
oemcrypto/ref/src/oemcrypto_engine_ref.cpp
Normal 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
|
||||
270
oemcrypto/ref/src/oemcrypto_engine_ref.h
Normal file
270
oemcrypto/ref/src/oemcrypto_engine_ref.h
Normal 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_
|
||||
120
oemcrypto/ref/src/oemcrypto_entitled_key_session.cpp
Normal file
120
oemcrypto/ref/src/oemcrypto_entitled_key_session.cpp
Normal 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
|
||||
110
oemcrypto/ref/src/oemcrypto_entitled_key_session.h
Normal file
110
oemcrypto/ref/src/oemcrypto_entitled_key_session.h
Normal 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_
|
||||
71
oemcrypto/ref/src/oemcrypto_key_ref.cpp
Normal file
71
oemcrypto/ref/src/oemcrypto_key_ref.cpp
Normal 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
|
||||
85
oemcrypto/ref/src/oemcrypto_key_ref.h
Normal file
85
oemcrypto/ref/src/oemcrypto_key_ref.h
Normal 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_
|
||||
74
oemcrypto/ref/src/oemcrypto_keybox_ref.cpp
Normal file
74
oemcrypto/ref/src/oemcrypto_keybox_ref.cpp
Normal 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
|
||||
46
oemcrypto/ref/src/oemcrypto_keybox_ref.h
Normal file
46
oemcrypto/ref/src/oemcrypto_keybox_ref.h
Normal 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_
|
||||
76
oemcrypto/ref/src/oemcrypto_nonce_table.cpp
Normal file
76
oemcrypto/ref/src/oemcrypto_nonce_table.cpp
Normal 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
|
||||
42
oemcrypto/ref/src/oemcrypto_nonce_table.h
Normal file
42
oemcrypto/ref/src/oemcrypto_nonce_table.h
Normal 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_
|
||||
1993
oemcrypto/ref/src/oemcrypto_ref.cpp
Normal file
1993
oemcrypto/ref/src/oemcrypto_ref.cpp
Normal file
File diff suppressed because it is too large
Load Diff
101
oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp
Normal file
101
oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp
Normal 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
|
||||
42
oemcrypto/ref/src/oemcrypto_rsa_key_shared.h
Normal file
42
oemcrypto/ref/src/oemcrypto_rsa_key_shared.h
Normal 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_
|
||||
1889
oemcrypto/ref/src/oemcrypto_session.cpp
Normal file
1889
oemcrypto/ref/src/oemcrypto_session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
300
oemcrypto/ref/src/oemcrypto_session.h
Normal file
300
oemcrypto/ref/src/oemcrypto_session.h
Normal 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_
|
||||
44
oemcrypto/ref/src/oemcrypto_session_key_table.cpp
Normal file
44
oemcrypto/ref/src/oemcrypto_session_key_table.cpp
Normal 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
|
||||
47
oemcrypto/ref/src/oemcrypto_session_key_table.h
Normal file
47
oemcrypto/ref/src/oemcrypto_session_key_table.h
Normal 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_
|
||||
703
oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp
Normal file
703
oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp
Normal 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
|
||||
131
oemcrypto/ref/src/oemcrypto_usage_table_ref.h
Normal file
131
oemcrypto/ref/src/oemcrypto_usage_table_ref.h
Normal 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_
|
||||
57
oemcrypto/ref/src/ts_parser.cpp
Normal file
57
oemcrypto/ref/src/ts_parser.cpp
Normal 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
|
||||
87
oemcrypto/ref/src/ts_parser.h
Normal file
87
oemcrypto/ref/src/ts_parser.h
Normal 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
108
oemcrypto/ref/src/wvcrc.cpp
Normal 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
|
||||
23
oemcrypto/ref/src/wvcrc32.h
Normal file
23
oemcrypto/ref/src/wvcrc32.h
Normal 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_
|
||||
Reference in New Issue
Block a user