Update partner repo.
This includes: - Trap key changes - Disable PKV2 Bug: b/378970789
This commit is contained in:
@@ -24,7 +24,9 @@ cc_library(
|
||||
"//:is_old_vmpra": [],
|
||||
"//conditions:default": [ # Chrome, including ChromeOS
|
||||
"HAS_PROVIDER_KEYS",
|
||||
"PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE",
|
||||
"KEY_TRAP",
|
||||
# Disable the flag since we are using PKV1
|
||||
# "PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE",
|
||||
],
|
||||
}) + select({
|
||||
"//:is_chromeos": ["WV_ENABLE_HW_VERIFICATION=1"],
|
||||
@@ -293,6 +295,37 @@ cc_library(
|
||||
alwayslink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "trap_keys",
|
||||
srcs = [
|
||||
"plain_aes.cc",
|
||||
"trap_keys.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"plain_aes.h",
|
||||
"trap_keys.h",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":shared_settings"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "trap_key_test",
|
||||
srcs = [
|
||||
"trap_key_test.cc",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":license_whitebox",
|
||||
":license_whitebox_test_base",
|
||||
":shared_settings",
|
||||
":test_license_builder",
|
||||
":trap_keys",
|
||||
"//chromium_deps/testing",
|
||||
],
|
||||
alwayslink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "remote_attestation_and_verification_test",
|
||||
srcs = [
|
||||
|
||||
431
whitebox/api/plain_aes.cc
Normal file
431
whitebox/api/plain_aes.cc
Normal file
@@ -0,0 +1,431 @@
|
||||
#include "plain_aes.h"
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t kSbox[256] = {
|
||||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
|
||||
0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
|
||||
0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
|
||||
0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
||||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
|
||||
0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
|
||||
0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
|
||||
0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
||||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
|
||||
0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
|
||||
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
|
||||
0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
|
||||
0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
|
||||
0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
|
||||
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
||||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
|
||||
0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
|
||||
0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
|
||||
0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
||||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
|
||||
0xB0, 0x54, 0xBB, 0x16};
|
||||
|
||||
static const uint8_t kInvSbox[256] = {
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e,
|
||||
0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
|
||||
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32,
|
||||
0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49,
|
||||
0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
|
||||
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50,
|
||||
0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05,
|
||||
0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
|
||||
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41,
|
||||
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
|
||||
0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
|
||||
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b,
|
||||
0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59,
|
||||
0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
|
||||
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
|
||||
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63,
|
||||
0x55, 0x21, 0x0c, 0x7d};
|
||||
|
||||
unsigned int GetRoundCount(const size_t key_size) {
|
||||
unsigned int round_cnt = 0;
|
||||
|
||||
if (key_size == 16) {
|
||||
round_cnt = 10;
|
||||
} else if (key_size == 32) {
|
||||
round_cnt = 14;
|
||||
} else if (key_size == 24) {
|
||||
round_cnt = 12;
|
||||
}
|
||||
|
||||
return round_cnt;
|
||||
}
|
||||
|
||||
void ExpandKey(const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* expanded_key) {
|
||||
const unsigned int round_cnt = GetRoundCount(key_size);
|
||||
const unsigned int n = key_size / 4;
|
||||
|
||||
memcpy(expanded_key, key, key_size);
|
||||
|
||||
const uint8_t r_con[10] = {0x01, 0x02, 0x04, 0x08, 0x10,
|
||||
0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||
|
||||
for (unsigned int i = key_size; i < 16 * (round_cnt + 1); ++i) {
|
||||
const unsigned int word_ind = i / 4;
|
||||
|
||||
if (word_ind % n == 0) {
|
||||
const unsigned int byte_ind_in_word = i & 3;
|
||||
|
||||
if (byte_ind_in_word == 0) {
|
||||
expanded_key[i] = expanded_key[i - key_size] ^
|
||||
kSbox[expanded_key[i - 3]] ^
|
||||
r_con[(i / key_size) - 1];
|
||||
} else if (byte_ind_in_word == 3) {
|
||||
expanded_key[i] =
|
||||
expanded_key[i - key_size] ^ kSbox[expanded_key[i - 7]];
|
||||
} else {
|
||||
expanded_key[i] =
|
||||
expanded_key[i - key_size] ^ kSbox[expanded_key[i - 3]];
|
||||
}
|
||||
|
||||
} else if (n > 6 && word_ind % n == 4) {
|
||||
expanded_key[i] = expanded_key[i - key_size] ^ kSbox[expanded_key[i - 4]];
|
||||
} else {
|
||||
expanded_key[i] = expanded_key[i - key_size] ^ expanded_key[i - 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddRoundKey(uint8_t* state, const uint8_t* key) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
state[i] ^= key[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void SubBytes(uint8_t* state) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
state[i] = kSbox[state[i]];
|
||||
}
|
||||
}
|
||||
|
||||
void InvSubBytes(uint8_t* state) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
state[i] = kInvSbox[state[i]];
|
||||
}
|
||||
}
|
||||
|
||||
void ShiftRows(uint8_t* state) {
|
||||
uint8_t shifted_state[16];
|
||||
|
||||
// 0 row
|
||||
shifted_state[0] = state[0];
|
||||
shifted_state[4] = state[4];
|
||||
shifted_state[8] = state[8];
|
||||
shifted_state[12] = state[12];
|
||||
|
||||
// 1 row
|
||||
shifted_state[1] = state[5];
|
||||
shifted_state[5] = state[9];
|
||||
shifted_state[9] = state[13];
|
||||
shifted_state[13] = state[1];
|
||||
|
||||
// 2 row
|
||||
shifted_state[2] = state[10];
|
||||
shifted_state[6] = state[14];
|
||||
shifted_state[10] = state[2];
|
||||
shifted_state[14] = state[6];
|
||||
|
||||
// 3 row
|
||||
shifted_state[3] = state[15];
|
||||
shifted_state[7] = state[3];
|
||||
shifted_state[11] = state[7];
|
||||
shifted_state[15] = state[11];
|
||||
|
||||
memcpy(state, shifted_state, 16);
|
||||
}
|
||||
|
||||
void InvShiftRows(uint8_t* state) {
|
||||
uint8_t shifted_state[16];
|
||||
|
||||
// 0 row
|
||||
shifted_state[0] = state[0];
|
||||
shifted_state[4] = state[4];
|
||||
shifted_state[8] = state[8];
|
||||
shifted_state[12] = state[12];
|
||||
|
||||
// 1 row
|
||||
shifted_state[5] = state[1];
|
||||
shifted_state[9] = state[5];
|
||||
shifted_state[13] = state[9];
|
||||
shifted_state[1] = state[13];
|
||||
|
||||
// 2 row
|
||||
shifted_state[10] = state[2];
|
||||
shifted_state[14] = state[6];
|
||||
shifted_state[2] = state[10];
|
||||
shifted_state[6] = state[14];
|
||||
|
||||
// 3 row
|
||||
shifted_state[15] = state[3];
|
||||
shifted_state[3] = state[7];
|
||||
shifted_state[7] = state[11];
|
||||
shifted_state[11] = state[15];
|
||||
|
||||
memcpy(state, shifted_state, 16);
|
||||
}
|
||||
|
||||
static uint8_t gf_mul_memoization[256][256] = {{0}};
|
||||
static bool gf_mul_computed[256][256] = {{false}};
|
||||
|
||||
uint8_t GfMul(uint8_t x, uint8_t y) {
|
||||
if (gf_mul_computed[x][y]) {
|
||||
return gf_mul_memoization[x][y];
|
||||
}
|
||||
|
||||
unsigned int tmp = (unsigned int)x;
|
||||
unsigned int res = 0;
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
if ((y & (1 << i)) != 0) {
|
||||
res ^= tmp;
|
||||
}
|
||||
tmp <<= 1;
|
||||
if (tmp >= (1 << 8)) {
|
||||
tmp ^= 283; // 283 = 0b100011011
|
||||
}
|
||||
}
|
||||
|
||||
gf_mul_memoization[x][y] = res;
|
||||
gf_mul_memoization[y][x] = res;
|
||||
gf_mul_computed[x][y] = true;
|
||||
gf_mul_computed[y][x] = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void MixColumn(uint8_t* column) {
|
||||
uint8_t mix_column[4][4] = {
|
||||
{0x02, 0x03, 0x01, 0x01},
|
||||
{0x01, 0x02, 0x03, 0x01},
|
||||
{0x01, 0x01, 0x02, 0x03},
|
||||
{0x03, 0x01, 0x01, 0x02},
|
||||
};
|
||||
|
||||
uint8_t result[4];
|
||||
result[0] =
|
||||
GfMul(mix_column[0][0], column[0]) ^ GfMul(mix_column[0][1], column[1]) ^
|
||||
GfMul(mix_column[0][2], column[2]) ^ GfMul(mix_column[0][3], column[3]);
|
||||
result[1] =
|
||||
GfMul(mix_column[1][0], column[0]) ^ GfMul(mix_column[1][1], column[1]) ^
|
||||
GfMul(mix_column[1][2], column[2]) ^ GfMul(mix_column[1][3], column[3]);
|
||||
result[2] =
|
||||
GfMul(mix_column[2][0], column[0]) ^ GfMul(mix_column[2][1], column[1]) ^
|
||||
GfMul(mix_column[2][2], column[2]) ^ GfMul(mix_column[2][3], column[3]);
|
||||
result[3] =
|
||||
GfMul(mix_column[3][0], column[0]) ^ GfMul(mix_column[3][1], column[1]) ^
|
||||
GfMul(mix_column[3][2], column[2]) ^ GfMul(mix_column[3][3], column[3]);
|
||||
|
||||
memcpy(column, result, 4);
|
||||
}
|
||||
|
||||
void MixColumns(uint8_t* state) {
|
||||
MixColumn(state + 0);
|
||||
MixColumn(state + 4);
|
||||
MixColumn(state + 8);
|
||||
MixColumn(state + 12);
|
||||
}
|
||||
|
||||
void InvMixColumn(uint8_t* column) {
|
||||
uint8_t mix_column[4][4] = {
|
||||
{0x0e, 0x0b, 0x0d, 0x09},
|
||||
{0x09, 0x0e, 0x0b, 0x0d},
|
||||
{0x0d, 0x09, 0x0e, 0x0b},
|
||||
{0x0b, 0x0d, 0x09, 0x0e},
|
||||
};
|
||||
|
||||
uint8_t result[4];
|
||||
result[0] =
|
||||
GfMul(mix_column[0][0], column[0]) ^ GfMul(mix_column[0][1], column[1]) ^
|
||||
GfMul(mix_column[0][2], column[2]) ^ GfMul(mix_column[0][3], column[3]);
|
||||
result[1] =
|
||||
GfMul(mix_column[1][0], column[0]) ^ GfMul(mix_column[1][1], column[1]) ^
|
||||
GfMul(mix_column[1][2], column[2]) ^ GfMul(mix_column[1][3], column[3]);
|
||||
result[2] =
|
||||
GfMul(mix_column[2][0], column[0]) ^ GfMul(mix_column[2][1], column[1]) ^
|
||||
GfMul(mix_column[2][2], column[2]) ^ GfMul(mix_column[2][3], column[3]);
|
||||
result[3] =
|
||||
GfMul(mix_column[3][0], column[0]) ^ GfMul(mix_column[3][1], column[1]) ^
|
||||
GfMul(mix_column[3][2], column[2]) ^ GfMul(mix_column[3][3], column[3]);
|
||||
|
||||
memcpy(column, result, 4);
|
||||
}
|
||||
|
||||
void InvMixColumns(uint8_t* state) {
|
||||
InvMixColumn(state + 0);
|
||||
InvMixColumn(state + 4);
|
||||
InvMixColumn(state + 8);
|
||||
InvMixColumn(state + 12);
|
||||
}
|
||||
|
||||
void PlainAesEncrypt(const uint8_t* input,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
const unsigned int round_cnt = GetRoundCount(key_size);
|
||||
|
||||
uint8_t* expanded_key = new unsigned char[(round_cnt + 1) * 16];
|
||||
if (!is_key_expanded) {
|
||||
ExpandKey(key, key_size, expanded_key);
|
||||
} else {
|
||||
memcpy(expanded_key, key, (round_cnt + 1) * 16);
|
||||
}
|
||||
|
||||
uint8_t state[16];
|
||||
memcpy(state, input, 16);
|
||||
|
||||
AddRoundKey(state, expanded_key);
|
||||
|
||||
for (unsigned int i = 0; i < round_cnt - 1; i++) {
|
||||
SubBytes(state);
|
||||
ShiftRows(state);
|
||||
MixColumns(state);
|
||||
AddRoundKey(state, expanded_key + 16 + 16 * i);
|
||||
}
|
||||
|
||||
SubBytes(state);
|
||||
ShiftRows(state);
|
||||
AddRoundKey(state, expanded_key + 16 * round_cnt);
|
||||
|
||||
memcpy(output, state, 16);
|
||||
delete[] expanded_key;
|
||||
}
|
||||
|
||||
void PlainAesDecrypt(const uint8_t* input,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
const unsigned int round_cnt = GetRoundCount(key_size);
|
||||
unsigned char* expanded_key;
|
||||
expanded_key = new unsigned char[(round_cnt + 1) * 16];
|
||||
if (!is_key_expanded) {
|
||||
ExpandKey(key, key_size, expanded_key);
|
||||
} else {
|
||||
memcpy(expanded_key, key, (round_cnt + 1) * 16);
|
||||
}
|
||||
|
||||
uint8_t state[16];
|
||||
memcpy(state, input, 16);
|
||||
|
||||
AddRoundKey(state, expanded_key + 16 * round_cnt);
|
||||
|
||||
for (int i = round_cnt - 2; i >= 0; i--) {
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(state, expanded_key + 16 + 16 * i);
|
||||
InvMixColumns(state);
|
||||
}
|
||||
|
||||
InvShiftRows(state);
|
||||
InvSubBytes(state);
|
||||
AddRoundKey(state, expanded_key);
|
||||
|
||||
memcpy(output, state, 16);
|
||||
delete[] expanded_key;
|
||||
}
|
||||
|
||||
static void IncrementCtr(uint8_t* iv) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
iv[15 - i]++;
|
||||
if (iv[15 - i] != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void PlainAesCtrEncrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
uint8_t ctr[16];
|
||||
memcpy(ctr, iv, 16);
|
||||
|
||||
for (unsigned int i = 0; i < input_size; i += 16) {
|
||||
uint8_t aes_output[16];
|
||||
PlainAesEncrypt(ctr, key, key_size, aes_output, is_key_expanded);
|
||||
IncrementCtr(ctr);
|
||||
|
||||
for (unsigned int j = 0; j < ((input_size - i >= 16) ? 16 : input_size - i);
|
||||
j++) {
|
||||
output[i + j] = input[i + j] ^ aes_output[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlainAesCtrDecrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
PlainAesCtrEncrypt(input, input_size, iv, key, key_size, output,
|
||||
is_key_expanded);
|
||||
}
|
||||
|
||||
void PlainAesCbcEncrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
for (unsigned int i = 0; i < input_size; i += 16) {
|
||||
uint8_t xor_block[16];
|
||||
|
||||
if (i == 0) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
xor_block[j] = iv[j] ^ input[i + j];
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
xor_block[j] = output[i - 16 + j] ^ input[i + j];
|
||||
}
|
||||
}
|
||||
PlainAesEncrypt(xor_block, key, key_size, output + i, is_key_expanded);
|
||||
}
|
||||
}
|
||||
|
||||
void PlainAesCbcDecrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded) {
|
||||
uint8_t prev_ciphertext[16];
|
||||
uint8_t current_ciphertext[16];
|
||||
memcpy(prev_ciphertext, iv, 16);
|
||||
|
||||
for (size_t i = 0; i < input_size; i += 16) {
|
||||
memcpy(current_ciphertext, input + i, 16);
|
||||
|
||||
uint8_t aes_output[16];
|
||||
PlainAesDecrypt(current_ciphertext, key, key_size, aes_output,
|
||||
is_key_expanded);
|
||||
|
||||
for (int j = 0; j < 16; j++) {
|
||||
output[i + j] = prev_ciphertext[j] ^ aes_output[j];
|
||||
}
|
||||
|
||||
memcpy(prev_ciphertext, current_ciphertext, 16);
|
||||
}
|
||||
}
|
||||
47
whitebox/api/plain_aes.h
Normal file
47
whitebox/api/plain_aes.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
void ExpandKey(const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* expanded_key);
|
||||
|
||||
void PlainAesEncrypt(const uint8_t* input,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
void PlainAesDecrypt(const uint8_t* input,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
|
||||
void PlainAesCtrEncrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
void PlainAesCtrDecrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
|
||||
void PlainAesCbcEncrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
void PlainAesCbcDecrypt(const uint8_t* input,
|
||||
size_t input_size,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* key,
|
||||
const size_t key_size,
|
||||
uint8_t* output,
|
||||
bool is_key_expanded = false);
|
||||
260
whitebox/api/trap_key_test.cc
Normal file
260
whitebox/api/trap_key_test.cc
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
|
||||
The following is the description of the trap key feature.
|
||||
|
||||
There are three trap keys, each of them is checked in one or more places in the
|
||||
whitebox code. When a certain trap check is reached, the content key value is
|
||||
compared to the hardcoded trap key value, and if the values match, the content
|
||||
key gets substituted with the corresponding modified key.
|
||||
|
||||
The point of the trap keys is to get hints on the StreamFab attack location. If
|
||||
a license response containing a trap key is sent to the StreamFab server, and
|
||||
the modified trap key is received, it means that the attack has happened after
|
||||
the trap check. If the original key is received, it means that the attack has
|
||||
happened before the check.
|
||||
|
||||
All the trap checks are located inside the algorithm instances that handle
|
||||
content keys wrapped with provider keys. Therefore, only licenses with valid
|
||||
provider key ID should be sent to the StreamFab server.
|
||||
|
||||
The changes in reference implementation reflect the behavior of the trap keys in
|
||||
the whitebox. The outputs of the reference and the whitebox implementations
|
||||
should match for licenses containing valid provider key ID. The test provided in
|
||||
trap_key_test.cc should succeed for both the reference and whitebox
|
||||
implementations. The variables containing all trap keys are defined in the
|
||||
trap_keys.cc file. These variables are used for the test and the reference
|
||||
implementation, and match the trap key values inserted in the whitebox.
|
||||
|
||||
The first trap key check is inserted right after the provider path unwrap. When
|
||||
this check is reached and the corresponding content key value is
|
||||
0x453372610fdbfcea066b2521f1e5746c (referred as trap_key_post_unwrap in code),
|
||||
the key gets substituted with 0xc1fbd5862c073482a9907870b478cd3f
|
||||
(trap_key_post_unwrap_modified). Therefore, when the license response containing
|
||||
trap_key_post_unwrap is sent to the StreamFab server, we expect to receive
|
||||
either the trap_key_post_unwrap_modified if the attack happens after the unwrap,
|
||||
or trap_key_post_unwrap otherwise.
|
||||
|
||||
The remaining trap checks are inserted after the content key expansion
|
||||
procedure, and they operate on the corresponding round key values.
|
||||
|
||||
When the license response containing the content key
|
||||
0x13fb3f51a80f8cfe3059be304b918054 (trap_key_post_key_scheduling_multi) is sent
|
||||
to the StreamFab server, there are multiple key values that we can expect to
|
||||
receive back:
|
||||
|
||||
* 0x677f8aaa100a79a535e0832b195f94e1
|
||||
(trap_key_post_key_scheduling_multi_cbc_modified_0_3) - the attack targets
|
||||
round keys 0-3 in CBC decryption
|
||||
* 0x1dccc69c1025d26e423792a4aaef3624
|
||||
(trap_key_post_key_scheduling_multi_cbc_modified_4_6) - the attack targets
|
||||
round keys 4-6 in CBC decryption
|
||||
* 0xbd552637be61c64d5a9b549c0567be89
|
||||
(trap_key_post_key_scheduling_multi_cbc_modified_7_10) - the attack targets
|
||||
round keys 7-10 in CBC decryption
|
||||
* 0xfc123d9871d633688763a7e161a8df27
|
||||
(trap_key_post_key_scheduling_multi_ctr_modified_0_3) - the attack targets
|
||||
round keys 0-3 in CTR decryption
|
||||
* 0xa177851298422cf8657322da4e76fe84
|
||||
(trap_key_post_key_scheduling_multi_ctr_modified_4_6) - the attack targets
|
||||
round keys 4-6 in CTR decryption
|
||||
* 0xc23b78c3b21c94266539d8d32fcf84e4
|
||||
(trap_key_post_key_scheduling_multi_ctr_modified_7_10) - the attack targets
|
||||
round keys 7-10 in CTR decryption
|
||||
* 0x13fb3f51a80f8cfe3059be304b918054 (trap_key_post_key_scheduling_multi) -
|
||||
the attack happens before key expansion
|
||||
* other values - the attack happens after key expansion, but it is impossible
|
||||
to tell in which round
|
||||
|
||||
Finally, when the license containing content key
|
||||
0xa6b0fd045f89c793658c0865f160aaae (trap_key_post_key_scheduling_single) is sent
|
||||
to the StreamFab server, we expect to receive back either the content key
|
||||
0xf8d0147dcd4089d7f5565ad252d791f9
|
||||
(trap_key_post_key_scheduling_single_modified), if the attack happens after the
|
||||
check (after the content key expansion procedure), or the
|
||||
trap_key_post_key_scheduling_single if the attack happens before.
|
||||
|
||||
Note that while both trap_key_post_key_scheduling_multi and
|
||||
trap_key_post_key_scheduling_single checks happen after key expansion, the
|
||||
trap_key_post_key_scheduling_single check is located later in the execution
|
||||
sequence. As a consequence, it is possible to receive the modified key for the
|
||||
TrapKeyPostKeySchedulingMulti check, and unmodified key for the
|
||||
trap_key_post_key_scheduling_single check.
|
||||
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "api/license_whitebox.h"
|
||||
#include "api/license_whitebox_test_base.h"
|
||||
#include "api/test_license_builder.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "api/plain_aes.h"
|
||||
#include "api/trap_keys.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
enum class TrapKeyType {
|
||||
PostUnwrap,
|
||||
PostKeySchedulingMulti,
|
||||
PostKeySchedulingSingle,
|
||||
NoTrap,
|
||||
};
|
||||
|
||||
class TrapTest : public LicenseWhiteboxTestBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<TrapKeyType, WB_CipherMode>> {
|
||||
protected:
|
||||
void SetUp() {
|
||||
LicenseWhiteboxTestBase::SetUp();
|
||||
ExpandTrapKeys(); // prepares expanded trap keys for the test and the
|
||||
// reference implementation
|
||||
}
|
||||
|
||||
public:
|
||||
const std::array<uint8_t, 16> kTrapTestIv = {
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35};
|
||||
const std::vector<uint8_t> kTrapTestPlaintext = {
|
||||
't', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e',
|
||||
' ', 'p', 'l', 'a', 'i', 'n', 't', 'e', 'x', 't', ' ',
|
||||
':', ' ', '3', '2', ' ', 'b', 'y', 't', 'e', 's',
|
||||
};
|
||||
const AesKey kNoTrapKey = {0x59, 0x45, 0x05, 0xf8, 0xa1, 0x6a, 0x2d, 0xea,
|
||||
0xec, 0xf0, 0x1d, 0x83, 0x50, 0x30, 0xe5, 0x59};
|
||||
};
|
||||
|
||||
// add the trap key to the license response, parse the license response and
|
||||
// perform masked decryption observe the output and verify that the actual key
|
||||
// used in the decryption was the corresponding modified key
|
||||
TEST_P(TrapTest, Run) {
|
||||
TrapKeyType trap_key_type;
|
||||
WB_CipherMode mode;
|
||||
std::tie(trap_key_type, mode) = GetParam();
|
||||
|
||||
TestLicenseBuilder builder;
|
||||
|
||||
TestLicenseBuilder::Settings settings;
|
||||
|
||||
#if defined(HAS_PROVIDER_KEYS)
|
||||
const size_t kProviderKeyId = 1;
|
||||
#else
|
||||
const size_t kProviderKeyId = 0;
|
||||
#endif
|
||||
|
||||
settings.provider_key_id = kProviderKeyId;
|
||||
builder.SetSettings(settings);
|
||||
|
||||
std::vector<uint8_t> ciphertext;
|
||||
const std::vector<uint8_t>* expected_plaintext;
|
||||
|
||||
const AesKey*
|
||||
kTrapKeyValue; // trap key that will be included in the license response
|
||||
const uint8_t* kTrapKeyValueModified; // corresponding substituted key
|
||||
bool is_expanded_key = false;
|
||||
#if !defined(KEY_TRAP)
|
||||
trap_key_type = TrapKeyType::NoTrap;
|
||||
#endif
|
||||
switch (trap_key_type) {
|
||||
case TrapKeyType::PostUnwrap:
|
||||
kTrapKeyValue = &trap_key_post_unwrap;
|
||||
kTrapKeyValueModified = trap_key_post_unwrap_modified.data();
|
||||
break;
|
||||
case TrapKeyType::PostKeySchedulingMulti:
|
||||
kTrapKeyValue = &trap_key_post_key_scheduling_multi;
|
||||
kTrapKeyValueModified =
|
||||
mode == WB_CIPHER_MODE_CBC
|
||||
? trap_key_post_key_scheduling_multi_cbc_modified_expanded.data()
|
||||
: trap_key_post_key_scheduling_multi_ctr_modified_expanded.data();
|
||||
is_expanded_key = true; // this is a custom expanded key; full expanded
|
||||
// key will be passed to the encrypt function
|
||||
break;
|
||||
case TrapKeyType::PostKeySchedulingSingle:
|
||||
kTrapKeyValue = &trap_key_post_key_scheduling_single;
|
||||
kTrapKeyValueModified =
|
||||
trap_key_post_key_scheduling_single_modified.data();
|
||||
break;
|
||||
case TrapKeyType::NoTrap: // random key, not matching any of the trap keys
|
||||
kTrapKeyValue = &kNoTrapKey;
|
||||
kTrapKeyValueModified =
|
||||
kNoTrapKey.data(); // we expect to observe the same key
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
}
|
||||
|
||||
// add original trap key to the license
|
||||
ContentKeyData trap_key = {
|
||||
{0xFF, 2, 0, 0}, SecurityLevel::kSoftwareSecureDecode, *kTrapKeyValue};
|
||||
builder.AddContentKey(trap_key);
|
||||
auto server = TestServer::CreateDualKey();
|
||||
License license;
|
||||
builder.Build(*server, &license);
|
||||
|
||||
// create the ciphertext using the modified key
|
||||
expected_plaintext = &kTrapTestPlaintext;
|
||||
ciphertext.resize(expected_plaintext->size());
|
||||
auto encryptFunc =
|
||||
mode == WB_CIPHER_MODE_CBC ? PlainAesCbcEncrypt : PlainAesCtrEncrypt;
|
||||
encryptFunc(expected_plaintext->data(), expected_plaintext->size(),
|
||||
kTrapTestIv.data(), kTrapKeyValueModified, 16, ciphertext.data(),
|
||||
is_expanded_key);
|
||||
|
||||
// process the license
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY,
|
||||
license.core_message.data(), license.core_message.size(),
|
||||
license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
kProviderKeyId, license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> output;
|
||||
size_t output_size = ciphertext.size();
|
||||
output.resize(output_size);
|
||||
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, mode, trap_key.id.data(),
|
||||
trap_key.id.size(), ciphertext.data(),
|
||||
ciphertext.size(), kTrapTestIv.data(),
|
||||
kTrapTestIv.size(), output.data(), &output_size),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_EQ(output, *expected_plaintext);
|
||||
#else
|
||||
ASSERT_EQ(WB_License_MaskedDecrypt(
|
||||
whitebox_, mode, trap_key.id.data(), trap_key.id.size(),
|
||||
ciphertext.data(), ciphertext.size(), kTrapTestIv.data(),
|
||||
kTrapTestIv.size(), output.data(), &output_size),
|
||||
WB_RESULT_OK);
|
||||
size_t secret_string_size = 16;
|
||||
std::vector<uint8_t> secret_string(16);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, mode, trap_key.id.data(), trap_key.id.size(),
|
||||
secret_string.data(), &secret_string_size);
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
secret_string.resize(secret_string_size);
|
||||
|
||||
std::vector<uint8_t> plaintext;
|
||||
plaintext.resize(output_size);
|
||||
WB_License_Unmask(output.data(), 0, output_size, secret_string.data(),
|
||||
secret_string.size(), plaintext.data());
|
||||
ASSERT_EQ(plaintext, *expected_plaintext);
|
||||
#endif
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
,
|
||||
TrapTest,
|
||||
::testing::Combine(::testing::Values(TrapKeyType::PostUnwrap,
|
||||
TrapKeyType::PostKeySchedulingMulti,
|
||||
TrapKeyType::PostKeySchedulingSingle,
|
||||
TrapKeyType::NoTrap),
|
||||
::testing::Values(WB_CIPHER_MODE_CBC,
|
||||
WB_CIPHER_MODE_CTR)));
|
||||
|
||||
} // namespace widevine
|
||||
119
whitebox/api/trap_keys.cc
Normal file
119
whitebox/api/trap_keys.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "trap_keys.h"
|
||||
#include <cstring>
|
||||
#include "api/plain_aes.h"
|
||||
|
||||
const std::array<uint8_t, 16> trap_key_post_unwrap = {
|
||||
0x45, 0x33, 0x72, 0x61, 0x0f, 0xdb, 0xfc, 0xea,
|
||||
0x06, 0x6b, 0x25, 0x21, 0xf1, 0xe5, 0x74, 0x6c};
|
||||
|
||||
const std::array<uint8_t, 16> trap_key_post_unwrap_modified = {
|
||||
0xc1, 0xfb, 0xd5, 0x86, 0x2c, 0x07, 0x34, 0x82,
|
||||
0xa9, 0x90, 0x78, 0x70, 0xb4, 0x78, 0xcd, 0x3f};
|
||||
|
||||
const std::array<uint8_t, 16> trap_key_post_key_scheduling_multi = {
|
||||
0x13, 0xfb, 0x3f, 0x51, 0xa8, 0x0f, 0x8c, 0xfe,
|
||||
0x30, 0x59, 0xbe, 0x30, 0x4b, 0x91, 0x80, 0x54};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_0_3 = {
|
||||
0x67, 0x7f, 0x8a, 0xaa, 0x10, 0x0a, 0x79, 0xa5,
|
||||
0x35, 0xe0, 0x83, 0x2b, 0x19, 0x5f, 0x94, 0xe1};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_4_6 = {
|
||||
0x1d, 0xcc, 0xc6, 0x9c, 0x10, 0x25, 0xd2, 0x6e,
|
||||
0x42, 0x37, 0x92, 0xa4, 0xaa, 0xef, 0x36, 0x24};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_7_10 = {
|
||||
0xbd, 0x55, 0x26, 0x37, 0xbe, 0x61, 0xc6, 0x4d,
|
||||
0x5a, 0x9b, 0x54, 0x9c, 0x05, 0x67, 0xbe, 0x89};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_0_3 = {
|
||||
0xfc, 0x12, 0x3d, 0x98, 0x71, 0xd6, 0x33, 0x68,
|
||||
0x87, 0x63, 0xa7, 0xe1, 0x61, 0xa8, 0xdf, 0x27};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_4_6 = {
|
||||
0xa1, 0x77, 0x85, 0x12, 0x98, 0x42, 0x2c, 0xf8,
|
||||
0x65, 0x73, 0x22, 0xda, 0x4e, 0x76, 0xfe, 0x84};
|
||||
|
||||
const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_7_10 = {
|
||||
0xc2, 0x3b, 0x78, 0xc3, 0xb2, 0x1c, 0x94, 0x26,
|
||||
0x65, 0x39, 0xd8, 0xd3, 0x2f, 0xcf, 0x84, 0xe4};
|
||||
|
||||
const std::array<uint8_t, 16> trap_key_post_key_scheduling_single = {
|
||||
0xa6, 0xb0, 0xfd, 0x04, 0x5f, 0x89, 0xc7, 0x93,
|
||||
0x65, 0x8c, 0x08, 0x65, 0xf1, 0x60, 0xaa, 0xae};
|
||||
|
||||
const std::array<uint8_t, 16> trap_key_post_key_scheduling_single_modified = {
|
||||
0xf8, 0xd0, 0x14, 0x7d, 0xcd, 0x40, 0x89, 0xd7,
|
||||
0xf5, 0x56, 0x5a, 0xd2, 0x52, 0xd7, 0x91, 0xf9};
|
||||
|
||||
std::array<uint8_t, 11 * 16> trap_key_post_key_scheduling_multi_expanded = {0};
|
||||
std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_expanded = {0};
|
||||
std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_expanded = {0};
|
||||
std::array<uint8_t, 11 * 16> trap_key_post_key_scheduling_single_expanded = {0};
|
||||
std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_single_modified_expanded = {0};
|
||||
|
||||
void ExpandTrapKeys() {
|
||||
ExpandKey(trap_key_post_key_scheduling_multi.data(), 16,
|
||||
trap_key_post_key_scheduling_multi_expanded.data());
|
||||
|
||||
// prepare expanded trap_key_post_key_scheduling_multi_cbc_modified_expanded
|
||||
{
|
||||
unsigned char expanded_key_temp[11 * 16];
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_cbc_modified_0_3.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_cbc_modified_expanded.data(),
|
||||
expanded_key_temp, 4 * 16);
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_cbc_modified_4_6.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_cbc_modified_expanded.data() +
|
||||
4 * 16,
|
||||
expanded_key_temp + 4 * 16, 3 * 16);
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_cbc_modified_7_10.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_cbc_modified_expanded.data() +
|
||||
7 * 16,
|
||||
expanded_key_temp + 7 * 16, 4 * 16);
|
||||
}
|
||||
|
||||
// prepare expanded trap_key_post_key_scheduling_multi_ctr_modified_expanded
|
||||
{
|
||||
unsigned char expanded_key_temp[11 * 16];
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_ctr_modified_0_3.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_ctr_modified_expanded.data(),
|
||||
expanded_key_temp, 4 * 16);
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_ctr_modified_4_6.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_ctr_modified_expanded.data() +
|
||||
4 * 16,
|
||||
expanded_key_temp + 4 * 16, 3 * 16);
|
||||
ExpandKey(trap_key_post_key_scheduling_multi_ctr_modified_7_10.data(), 16,
|
||||
expanded_key_temp);
|
||||
memcpy(trap_key_post_key_scheduling_multi_ctr_modified_expanded.data() +
|
||||
7 * 16,
|
||||
expanded_key_temp + 7 * 16, 4 * 16);
|
||||
}
|
||||
|
||||
ExpandKey(trap_key_post_key_scheduling_single.data(), 16,
|
||||
trap_key_post_key_scheduling_single_expanded.data());
|
||||
ExpandKey(trap_key_post_key_scheduling_single_modified.data(), 16,
|
||||
trap_key_post_key_scheduling_single_modified_expanded.data());
|
||||
}
|
||||
|
||||
void CompareToTrapKeyAndModify(uint8_t* key,
|
||||
const uint8_t* trap_key,
|
||||
const uint8_t* replacement_key,
|
||||
size_t n) {
|
||||
if (std::memcmp(key, trap_key, n) == 0) {
|
||||
std::memcpy(key, replacement_key, n);
|
||||
}
|
||||
}
|
||||
40
whitebox/api/trap_keys.h
Normal file
40
whitebox/api/trap_keys.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
extern const std::array<uint8_t, 16> trap_key_post_unwrap;
|
||||
extern const std::array<uint8_t, 16> trap_key_post_unwrap_modified;
|
||||
|
||||
extern const std::array<uint8_t, 16> trap_key_post_key_scheduling_multi;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_0_3;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_4_6;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_7_10;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_0_3;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_4_6;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_7_10;
|
||||
|
||||
extern const std::array<uint8_t, 16> trap_key_post_key_scheduling_single;
|
||||
extern const std::array<uint8_t, 16>
|
||||
trap_key_post_key_scheduling_single_modified;
|
||||
|
||||
extern std::array<uint8_t, 11 * 16> trap_key_post_key_scheduling_multi_expanded;
|
||||
extern std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_expanded;
|
||||
extern std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_expanded;
|
||||
extern std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_single_expanded;
|
||||
extern std::array<uint8_t, 11 * 16>
|
||||
trap_key_post_key_scheduling_single_modified_expanded;
|
||||
|
||||
void ExpandTrapKeys();
|
||||
void CompareToTrapKeyAndModify(uint8_t* key,
|
||||
const uint8_t* trap_key,
|
||||
const uint8_t* replacement_key,
|
||||
size_t n);
|
||||
@@ -69,6 +69,7 @@ cc_library(
|
||||
":renewal_key",
|
||||
"//api:result",
|
||||
"//api:shared_settings",
|
||||
"//api:trap_keys",
|
||||
"//chromium_deps/cdm/protos",
|
||||
"//crypto_utils:aes_cbc_decryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
@@ -89,8 +90,8 @@ cc_library(
|
||||
|
||||
cc_library(
|
||||
name = "protobuf_license_parser",
|
||||
hdrs = ["protobuf_license_parser.h"],
|
||||
srcs = ["protobuf_license_parser.cc"],
|
||||
hdrs = ["protobuf_license_parser.h"],
|
||||
deps = [
|
||||
":license_parser",
|
||||
"//api:shared_settings",
|
||||
@@ -171,6 +172,7 @@ cc_library(
|
||||
"//api:license_whitebox",
|
||||
"//api:result",
|
||||
"//api:shared_settings",
|
||||
"//api:trap_keys",
|
||||
"//chromium_deps/cdm/keys:dev_certs",
|
||||
"//chromium_deps/cdm/protos",
|
||||
"//crypto_utils:aes_cbc_decryptor",
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
|
||||
#ifdef KEY_TRAP
|
||||
#include "api/trap_keys.h"
|
||||
#endif
|
||||
|
||||
namespace widevine {
|
||||
|
||||
bool LicenseParser::Decrypt(const std::string& key,
|
||||
@@ -86,6 +90,13 @@ bool LicenseParser::UnwrapKey(
|
||||
DVLOG(1) << "Failed to decrypt content key using Provider Key.";
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef KEY_TRAP
|
||||
CompareToTrapKeyAndModify(
|
||||
reinterpret_cast<uint8_t*>(&final_key[0]), trap_key_post_unwrap.data(),
|
||||
trap_key_post_unwrap_modified.data(), final_key.length());
|
||||
#endif
|
||||
|
||||
unwrapped_key->swap(final_key);
|
||||
return true;
|
||||
}
|
||||
@@ -117,8 +128,11 @@ WB_KeyStatus LicenseParser::GetKeyStatus(
|
||||
}
|
||||
|
||||
InternalKey LicenseParser::CreateInternalKey(
|
||||
KeyType key_type, video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified, const std::string& key, uint32_t kcb_flags) {
|
||||
KeyType key_type,
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified,
|
||||
const std::string& key,
|
||||
uint32_t kcb_flags) {
|
||||
CHECK((kcb_flags & WB_KCB_FLAGS_GENERIC_MASK) == 0 ||
|
||||
key_type == KeyType::kGenericCryptoKey);
|
||||
|
||||
|
||||
@@ -40,6 +40,11 @@
|
||||
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
||||
#include "third_party/boringssl/src/include/openssl/sha.h"
|
||||
|
||||
#ifdef KEY_TRAP
|
||||
#include "api/plain_aes.h"
|
||||
#include "api/trap_keys.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
using AesCbcDecryptor = widevine::AesCbcDecryptor;
|
||||
using AesCbcEncryptor = widevine::AesCbcEncryptor;
|
||||
@@ -220,18 +225,79 @@ WB_Result DecryptBuffer(WB_CipherMode mode,
|
||||
// By this point, we have verified everything that need to be verified.
|
||||
// Decryption should just work.
|
||||
if (mode == WB_CIPHER_MODE_CBC) {
|
||||
#ifdef KEY_TRAP
|
||||
std::array<uint8_t, 11 * 16> expanded_key;
|
||||
ExpandKey(key, key_size, expanded_key.data());
|
||||
for (int i = 0; i < 11; i++) {
|
||||
CompareToTrapKeyAndModify(
|
||||
/*key_to_compare=*/expanded_key.data() + 16 * i,
|
||||
/*trap_key=*/trap_key_post_key_scheduling_multi_expanded.data() +
|
||||
16 * i,
|
||||
/*modified_trap_key=*/
|
||||
trap_key_post_key_scheduling_multi_cbc_modified_expanded.data() +
|
||||
16 * i,
|
||||
/*key_size=*/16);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
CompareToTrapKeyAndModify(
|
||||
/*key_to_compare=*/expanded_key.data() + 16 * i,
|
||||
/*trap_key=*/trap_key_post_key_scheduling_single_expanded.data() +
|
||||
16 * i,
|
||||
/*modified_trap_key=*/
|
||||
trap_key_post_key_scheduling_single_modified_expanded.data() + 16 * i,
|
||||
/*key_size=*/16);
|
||||
}
|
||||
|
||||
PlainAesCbcDecrypt(input_data, input_data_size, iv, expanded_key.data(),
|
||||
key_size, output_data, /*is_key_expanded=*/true);
|
||||
#else
|
||||
AesCbcDecryptor decryptor;
|
||||
CHECK(decryptor.SetKey(key, key_size));
|
||||
|
||||
CHECK(decryptor.Decrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data));
|
||||
#endif
|
||||
} else if (mode == WB_CIPHER_MODE_CTR) {
|
||||
#ifdef KEY_TRAP
|
||||
std::array<uint8_t, 11 * 16> expanded_key;
|
||||
ExpandKey(key, key_size, expanded_key.data());
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
CompareToTrapKeyAndModify(
|
||||
/*key_to_compare=*/expanded_key.data() + 16 * i,
|
||||
/*trap_key=*/trap_key_post_key_scheduling_multi_expanded.data() +
|
||||
16 * i,
|
||||
/*modified_trap_key=*/
|
||||
trap_key_post_key_scheduling_multi_ctr_modified_expanded.data() +
|
||||
16 * i,
|
||||
/*key_size=*/16);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
CompareToTrapKeyAndModify(
|
||||
/*key_to_compare=*/expanded_key.data() + 16 * i,
|
||||
/*trap_key=*/trap_key_post_key_scheduling_single_expanded.data() +
|
||||
16 * i,
|
||||
/*modified_trap_key=*/
|
||||
trap_key_post_key_scheduling_single_modified_expanded.data() + 16 * i,
|
||||
/*key_size=*/16);
|
||||
}
|
||||
|
||||
PlainAesCtrDecrypt(input_data, input_data_size, iv, expanded_key.data(),
|
||||
key_size, output_data, /*is_key_expanded=*/true);
|
||||
#else
|
||||
AesCtrDecryptor decryptor;
|
||||
CHECK(decryptor.SetKey(key, key_size));
|
||||
|
||||
// Encrypt and Decrypt for CTR use the same interface.
|
||||
CHECK(decryptor.Encrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data));
|
||||
#endif
|
||||
} else {
|
||||
DVLOG(1) << "Invalid parameter: invalid cipher mode.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
|
||||
@@ -74,6 +74,16 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "trap_key_test",
|
||||
size = "small",
|
||||
deps = [
|
||||
"//api:trap_key_test",
|
||||
"//reference/impl:general_license_whitebox",
|
||||
"//reference/impl:license_whitebox_provider_keys_test_data"
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "license_whitebox_uat_test",
|
||||
size = "small",
|
||||
|
||||
Reference in New Issue
Block a user