Update OEMCrypto Offset Tests

Merge from widevine repo of http://go/wvgerrit/14321

It is expected that OEMCrypto will only be given subsamples with a
block offset when there are multiple subsamples, so that the entire
sample may be decrypted after all calls are made.  This CL modifies
the existing tests so that the result of DecryptCTR is only checked
after all subsamples have been decrypted.

Also, the QueryKeyControl test has been modified so that failure does
not require a specific error code.

bug: 20757848
bug: 21063276
Change-Id: Ie2b12b287b0c9c661cd14111b2ae9eab004cd8b8
This commit is contained in:
Fred Gylys-Colwell
2015-05-29 11:37:52 -07:00
parent 93d326129a
commit 994a7fc4c1

View File

@@ -1342,7 +1342,7 @@ class Session {
int status = RSA_public_encrypt(session_key.size(), &session_key[0],
&(enc_session_key->front()), public_rsa_,
RSA_PKCS1_OAEP_PADDING);
if (status != RSA_size(public_rsa_)) {
if (static_cast<unsigned>(status) != RSA_size(public_rsa_)) {
cout << "GenerateRSASessionKey error encrypting session key. ";
dump_openssl_error();
return false;
@@ -2282,7 +2282,7 @@ TEST_F(OEMCryptoSessionTests, QueryKeyControl) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
const char *key_id = "no_key";
size = sizeof(block);
ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY,
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_QueryKeyControl(
s.session_id(), reinterpret_cast<const uint8_t*>(key_id),
strlen(key_id), reinterpret_cast<uint8_t*>(&block), &size));
@@ -2444,148 +2444,180 @@ TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) {
s.TestDecryptCTR();
}
TEST_F(OEMCryptoSessionTests, DecryptWithOffset) {
OEMCryptoResult sts;
Session s;
s.open();
s.GenerateTestSessionKeys();
s.FillSimpleMessage(kDuration, 0, 0);
s.EncryptAndSign();
s.LoadTestKeys();
// Select the key (from FillSimpleMessage)
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Set up our expected input and output
// This is dummy encrypted data.
vector<uint8_t> encryptedData = wvcdm::a2b_hex(
"c17055d4e3ab8e892b40ca2deed7cd46b406cd41d50f23d5877b36"
"ad351887df2b3774dc413904afd958ba766cc6ab51a3ffd8f845296c5d8326ee"
"39c9d0fec79885515e6b8a12911831d9fb158ca2fd3dfcfcf228741a63734685"
"8dffc30f5871260c5cef8be61cfa08b191c837901f077046664c0c56db81d412"
"98b59e5655cd94871c3c226dc3565144297f1459cddba069d5d2d6206cfd5798"
"eda4b82e01a9966d48984d6ef3fbd326ba0f6fcbe52c95786d478c2f33398c62"
"ae5210c7472d7d8dc7d12f981679f4ea9793736f354747ef14165367b94e07fc"
"4bcc7bd14746304fea100dc6465ab51241355bb19e6c2cfb2bb6bbf709765d13");
vector<uint8_t> encryptionIv = wvcdm::a2b_hex(
"c09454479a280829c946df3c22f25539");
// This is the expected decrypted data.
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
"f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8"
"a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f"
"7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b"
"dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4"
"b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f"
"f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def"
"eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e"
"a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9");
// Describe the output
uint8_t outputBuffer[256];
OEMCrypto_DestBufferDesc destBuffer;
destBuffer.type = OEMCrypto_BufferType_Clear;
destBuffer.buffer.clear.address = outputBuffer;
destBuffer.buffer.clear.max_length = sizeof(outputBuffer);
// Decrypt the data
sts = OEMCrypto_DecryptCTR(
s.session_id(), &encryptedData[0], encryptedData.size(), true,
&encryptionIv[0], 5, &destBuffer,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer,
unencryptedData.size()));
}
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This is
// different from the OpenSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(uint8_t* counter) {
uint32_t n = 16;
do {
if (++counter[--n] != 0) return;
} while (n > 8);
}
vector<uint8_t> EncryptCTR(const vector<uint8_t>& key,
const vector<uint8_t>& iv, const vector<uint8_t>& in,
size_t block_offset) {
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
uint8_t aes_iv[AES_BLOCK_SIZE];
memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE);
// Encrypt the IV.
uint8_t ecount_buf[AES_BLOCK_SIZE];
vector<uint8_t> out(in.size());
size_t cipher_data_length = in.size();
size_t l = 0;
while (l < cipher_data_length) {
AES_encrypt(aes_iv, ecount_buf, &aes_key);
for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length;
++n, ++l) {
out[l] = in[l] ^ ecount_buf[n];
}
ctr128_inc64(aes_iv);
block_offset = 0;
class OEMCryptoSessionTestsDecryptEdgeCases : public OEMCryptoSessionTests {
public:
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This is
// different from the OpenSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(int64_t increaseBy, uint8_t* iv) {
uint64_t* counterBuffer = reinterpret_cast<uint64_t*>(&iv[8]);
(*counterBuffer) = wvcdm::ntohll64(wvcdm::ntohll64(*counterBuffer) +
increaseBy);
}
return out;
size_t FindTotalSize(const vector<size_t>& subsample_size) {
size_t total_size = 0;
for(size_t i=0; i < subsample_size.size(); i++)
total_size += subsample_size[i];
return total_size;
}
void EncryptCTR(const vector<uint8_t>& key, const vector<uint8_t>& iv,
const vector<uint8_t>& in, vector<uint8_t>* out) {
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key);
uint8_t aes_iv[AES_BLOCK_SIZE];
memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE);
// Encrypt the IV.
uint8_t ecount_buf[AES_BLOCK_SIZE];
out->resize(in.size());
size_t cipher_data_length = in.size();
size_t l = 0;
while (l < cipher_data_length) {
AES_encrypt(aes_iv, ecount_buf, &aes_key);
for (int n = 0; n < AES_BLOCK_SIZE && l < cipher_data_length;
++n, ++l) {
(*out)[l] = in[l] ^ ecount_buf[n];
}
ctr128_inc64(1, aes_iv);
}
}
void TestDecrypt(const vector<uint8_t>& unencryptedData,
const vector<uint8_t>& encryptedData,
const vector<uint8_t>& encryptionIv,
size_t total_size, const vector<size_t> subsample_size) {
OEMCryptoResult sts;
Session s;
s.open();
s.GenerateTestSessionKeys();
s.FillSimpleMessage(kDuration, 0, 0);
s.EncryptAndSign();
s.LoadTestKeys();
// Select the key (from FillSimpleMessage)
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// We decrypt three subsamples, each with a block offset.
vector<uint8_t> outputBuffer(total_size, 0xaa);
// buffer_offset is the offset of the current subsample from the beginning
// of the whole sample's buffer.
size_t buffer_offset = 0;
for(size_t i=0; i < subsample_size.size(); i++) {
const size_t block_offset = buffer_offset % AES_BLOCK_SIZE;
uint8_t subsample_flags = 0;
if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample;
if (i == subsample_size.size()-1) {
subsample_flags |= OEMCrypto_LastSubsample;
}
OEMCrypto_DestBufferDesc destBuffer;
destBuffer.type = OEMCrypto_BufferType_Clear;
destBuffer.buffer.clear.address = &outputBuffer[buffer_offset];
destBuffer.buffer.clear.max_length = subsample_size[i];
uint8_t aes_iv[AES_BLOCK_SIZE];
// encryptionIv is the IV for the beginning of the whole sample.
memcpy(aes_iv, &encryptionIv[0], AES_BLOCK_SIZE);
// iv_increment is the number of blocks from the beginning of the sample.
size_t iv_increment = buffer_offset / AES_BLOCK_SIZE;
// Their sum is the IV for the block at the beginning of this subsample.
ctr128_inc64(iv_increment, aes_iv);
sts = OEMCrypto_DecryptCTR(
s.session_id(), &encryptedData[buffer_offset], subsample_size[i],
true, aes_iv, block_offset, &destBuffer, subsample_flags);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
buffer_offset += subsample_size[i];
}
EXPECT_EQ(0, memcmp(&unencryptedData[0], &outputBuffer[0], total_size));
// If there was a problem, compare the outputBuffer at the offset with the
// correct data at 0. A common error is to ignore the offset when
// decrypting.
if (0 != memcmp(&unencryptedData[0], &outputBuffer[0], total_size)
&& 2*subsample_size[0] < total_size
&& 0 == memcmp(&unencryptedData[0], &outputBuffer[subsample_size[0]],
subsample_size[0])){
printf("The first %zd bytes are repeating. This is an indication \n",
subsample_size[0]);
printf("that DecryptCTR is ignoring the offset.\n");
}
}
};
TEST_F(OEMCryptoSessionTestsDecryptEdgeCases, EvenOffset) {
vector<size_t> subsample_size;
subsample_size.push_back(8);
subsample_size.push_back(32);
subsample_size.push_back(50);
const size_t total_size = FindTotalSize(subsample_size);
vector<uint8_t> unencryptedData(total_size, 0);
vector<uint8_t> encryptedData(total_size, 0);
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE, 0);
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
// Note: DecryptCTR is self-inverse -- ie it's the same as EncryptCTR.
// So we can pick the encrypted data and compute the unencrypted data if we
// want. By picking the encrypted data to be all 0, it is easier to
// re-encrypt the data and debug problems.
EncryptCTR(key, encryptionIv, encryptedData, &unencryptedData);
TestDecrypt(unencryptedData, encryptedData, encryptionIv, total_size,
subsample_size);
}
TEST_F(OEMCryptoSessionTests, DecryptWithNearWrap) {
OEMCryptoResult sts;
Session s;
s.open();
s.GenerateTestSessionKeys();
s.FillSimpleMessage(kDuration, 0, 0);
s.EncryptAndSign();
s.LoadTestKeys();
// Select the key (from FillSimpleMessage)
vector<uint8_t> keyId = wvcdm::a2b_hex("000000000000000000000000");
sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Set up our expected input and output
// This tests the ability to decrypt multiple subsamples with no offset.
TEST_F(OEMCryptoSessionTestsDecryptEdgeCases, NoOffset) {
vector<size_t> subsample_size;
subsample_size.push_back(64);
subsample_size.push_back(64);
subsample_size.push_back(64);
const size_t total_size = FindTotalSize(subsample_size);
vector<uint8_t> unencryptedData(total_size, 0);
vector<uint8_t> encryptedData(total_size, 0);
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE, 0);
encryptionIv = wvcdm::a2b_hex("c09454479a280829c946df3c22f25539");
for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256;
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
vector<uint8_t> encryptionIv = wvcdm::a2b_hex(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
// This is dummy decrypted data.
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
"f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8"
"a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f"
"7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b"
"dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4"
"b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f"
"f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def"
"eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e"
"a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9");
size_t block_offset = 5;
vector<uint8_t> encryptedData =
EncryptCTR(key, encryptionIv, unencryptedData, block_offset);
EncryptCTR(key, encryptionIv, unencryptedData, &encryptedData);
TestDecrypt(unencryptedData, encryptedData, encryptionIv, total_size,
subsample_size);
}
// Describe the output
uint8_t outputBuffer[256];
OEMCrypto_DestBufferDesc destBuffer;
destBuffer.type = OEMCrypto_BufferType_Clear;
destBuffer.buffer.clear.address = outputBuffer;
destBuffer.buffer.clear.max_length = sizeof(outputBuffer);
// If the EvenOffset test passes, but this one doesn't, then DecryptCTR might
// be using the wrong definition of offset. Adding the offset to the block
// boundary should give you the beginning of the encrypted data.
TEST_F(OEMCryptoSessionTestsDecryptEdgeCases, OddOffset) {
vector<size_t> subsample_size;
subsample_size.push_back(50);
subsample_size.push_back(75);
subsample_size.push_back(25);
const size_t total_size = FindTotalSize(subsample_size);
vector<uint8_t> unencryptedData(total_size, 0);
vector<uint8_t> encryptedData(total_size, 0);
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE, 0);
encryptionIv = wvcdm::a2b_hex("c09454479a280829c946df3c22f25539");
for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256;
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
EncryptCTR(key, encryptionIv, unencryptedData, &encryptedData);
TestDecrypt(unencryptedData, encryptedData, encryptionIv, total_size,
subsample_size);
}
// Decrypt the data
sts = OEMCrypto_DecryptCTR(
s.session_id(), &encryptedData[0], encryptedData.size(), true,
&encryptionIv[0], block_offset, &destBuffer,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer,
unencryptedData.size()));
TEST_F(OEMCryptoSessionTestsDecryptEdgeCases, DecryptWithNearWrap) {
vector<size_t> subsample_size;
subsample_size.push_back(150);
const size_t total_size = FindTotalSize(subsample_size);
vector<uint8_t> unencryptedData(total_size, 0);
vector<uint8_t> encryptedData(total_size, 0);
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE, 0);
encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256;
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
EncryptCTR(key, encryptionIv, unencryptedData, &encryptedData);
TestDecrypt(unencryptedData, encryptedData, encryptionIv, total_size,
subsample_size);
}
TEST_F(OEMCryptoSessionTests, DecryptUnencrypted) {
@@ -2638,9 +2670,7 @@ TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) {
OEMCryptoResult sts;
Session s;
s.open();
// CLear data should be copied even if there is no key selected.
// Clear data should be copied even if there is no key selected.
// Set up our expected input and output
// This is dummy decrypted data.
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(