diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 13f07be2..2125d2ff 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -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(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(key_id), strlen(key_id), reinterpret_cast(&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 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 encryptedData = wvcdm::a2b_hex( - "c17055d4e3ab8e892b40ca2deed7cd46b406cd41d50f23d5877b36" - "ad351887df2b3774dc413904afd958ba766cc6ab51a3ffd8f845296c5d8326ee" - "39c9d0fec79885515e6b8a12911831d9fb158ca2fd3dfcfcf228741a63734685" - "8dffc30f5871260c5cef8be61cfa08b191c837901f077046664c0c56db81d412" - "98b59e5655cd94871c3c226dc3565144297f1459cddba069d5d2d6206cfd5798" - "eda4b82e01a9966d48984d6ef3fbd326ba0f6fcbe52c95786d478c2f33398c62" - "ae5210c7472d7d8dc7d12f981679f4ea9793736f354747ef14165367b94e07fc" - "4bcc7bd14746304fea100dc6465ab51241355bb19e6c2cfb2bb6bbf709765d13"); - vector encryptionIv = wvcdm::a2b_hex( - "c09454479a280829c946df3c22f25539"); - // This is the expected decrypted data. - vector 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 EncryptCTR(const vector& key, - const vector& iv, const vector& 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 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(&iv[8]); + (*counterBuffer) = wvcdm::ntohll64(wvcdm::ntohll64(*counterBuffer) + + increaseBy); } - return out; + + size_t FindTotalSize(const vector& 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& key, const vector& iv, + const vector& in, vector* 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& unencryptedData, + const vector& encryptedData, + const vector& encryptionIv, + size_t total_size, const vector 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 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 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 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 unencryptedData(total_size, 0); + vector encryptedData(total_size, 0); + vector encryptionIv(AES_BLOCK_SIZE, 0); + vector 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 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 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 unencryptedData(total_size, 0); + vector encryptedData(total_size, 0); + vector encryptionIv(AES_BLOCK_SIZE, 0); + encryptionIv = wvcdm::a2b_hex("c09454479a280829c946df3c22f25539"); + for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256; vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); - vector encryptionIv = wvcdm::a2b_hex( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); - // This is dummy decrypted data. - vector unencryptedData = wvcdm::a2b_hex( - "f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8" - "a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f" - "7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b" - "dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4" - "b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f" - "f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def" - "eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e" - "a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9"); - size_t block_offset = 5; - vector 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 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 unencryptedData(total_size, 0); + vector encryptedData(total_size, 0); + vector encryptionIv(AES_BLOCK_SIZE, 0); + encryptionIv = wvcdm::a2b_hex("c09454479a280829c946df3c22f25539"); + for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256; + vector 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 subsample_size; + subsample_size.push_back(150); + const size_t total_size = FindTotalSize(subsample_size); + vector unencryptedData(total_size, 0); + vector encryptedData(total_size, 0); + vector encryptionIv(AES_BLOCK_SIZE, 0); + encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); + for(size_t i=0; i < total_size; i++) unencryptedData[i] = i % 256; + vector 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 unencryptedData = wvcdm::a2b_hex(