From 053ff5bd3c5e4ffe51ff78dd54b804c4d621a2c5 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Tue, 29 Nov 2016 15:15:08 -0800 Subject: [PATCH] OEMCrypto Tests Provisioning Method Merge from widevine repo of http://go/wvgerrit/21682 This CL updates oemcrypto/test/oec_device_features.cpp to figure out the provisioning method and filter out tests that are not relevant to the device's method. This CL also introduces unit tests for GetOEMPublicCertificate. Unit tests for RewrapDeviceRSAKey30 will be in a future CL. Change-Id: Ib7065ce866d1171ca61b9aa08188fa2ac8d90fc2 --- .../oemcrypto/test/oec_device_features.cpp | 72 +++++++++++- .../oemcrypto/test/oec_device_features.h | 3 + .../oemcrypto/test/oec_session_util.cpp | 48 ++++++++ .../oemcrypto/test/oec_session_util.h | 3 + .../oemcrypto/test/oemcrypto_test.cpp | 110 +++++++++++++++++- .../oemcrypto/test/oemcrypto_test_android.cpp | 37 +++--- 6 files changed, 251 insertions(+), 22 deletions(-) diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp index a5cc1a5f..2308d5cc 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp @@ -31,23 +31,43 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, uint32_t nonce = 0; uint8_t buffer[1]; size_t size = 0; + provisioning_method = OEMCrypto_GetProvisioningMethod(); + printf("provisioning_method = %s\n", + ProvisioningMethodName(provisioning_method)); uses_keybox = (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_GetKeyData(buffer, &size)); printf("uses_keybox = %s.\n", uses_keybox ? "true" : "false"); - loads_certificate = uses_keybox && (OEMCrypto_ERROR_NOT_IMPLEMENTED != - OEMCrypto_RewrapDeviceRSAKey( - 0, buffer, 0, buffer, 0, &nonce, - buffer, 0, buffer, buffer, &size)); + OEMCrypto_SESSION session; + OEMCryptoResult result = OEMCrypto_OpenSession(&session); + if (result != OEMCrypto_SUCCESS) { + printf("--- ERROR: Could not open session: %d ----\n", result); + } + // If the device uses a keybox, check to see if loading a certificate is + // installed. + if (provisioning_method == OEMCrypto_Keybox) { + loads_certificate = + (OEMCrypto_ERROR_NOT_IMPLEMENTED != + OEMCrypto_RewrapDeviceRSAKey(session, buffer, 0, buffer, 0, &nonce, + buffer, 0, buffer, buffer, &size)); + } else if (provisioning_method == OEMCrypto_OEMCertificate) { + // If the device says it uses Provisioning 3.0, then it should be able to + // load a DRM certificate. These devices must support RewrapDeviceRSAKey30. + loads_certificate = true; + } else { + // Other devices are either broken, or they have a baked in certificate. + loads_certificate = false; + } printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); uses_certificate = (OEMCrypto_ERROR_NOT_IMPLEMENTED != - OEMCrypto_GenerateRSASignature(0, buffer, 0, buffer, + OEMCrypto_GenerateRSASignature(session, buffer, 0, buffer, &size, kSign_RSASSA_PSS)); printf("uses_certificate = %s.\n", uses_certificate ? "true" : "false"); generic_crypto = (OEMCrypto_ERROR_NOT_IMPLEMENTED != - OEMCrypto_Generic_Encrypt(0, buffer, 0, buffer, + OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer, OEMCrypto_AES_CBC_128_NO_PADDING, buffer)); printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false"); + OEMCrypto_CloseSession(session); api_version = OEMCrypto_APIVersion(); printf("api_version = %d.\n", api_version); usage_table = OEMCrypto_SupportsUsageTable(); @@ -80,6 +100,9 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, case FORCE_TEST_KEYBOX: printf("FORCE_TEST_KEYBOX: User requested calling InstallKeybox.\n"); break; + case TEST_PROVISION_30: + printf("TEST_PROVISION_30: Device provisioed with OEM Cert.\n"); + break; } OEMCrypto_Terminate(); } @@ -95,8 +118,11 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { if (!cast_receiver) FilterOut(&filter, "*CastReceiver*"); if (!usage_table) FilterOut(&filter, "*UsageTable*"); if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); + if (provisioning_method + != OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*"); if (api_version < 10) FilterOut(&filter, "*API10*"); if (api_version < 11) FilterOut(&filter, "*API11*"); + if (api_version < 12) FilterOut(&filter, "*API12*"); // Performance tests take a long time. Filter them out if they are not // specifically requested. if (filter.find("Performance") == std::string::npos) { @@ -106,6 +132,27 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { } void DeviceFeatures::PickDerivedKey() { + if (api_version >= 12) { + switch (provisioning_method) { + case OEMCrypto_OEMCertificate: + derive_key_method = TEST_PROVISION_30; + return; + case OEMCrypto_DrmCertificate: + if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { + derive_key_method = LOAD_TEST_RSA_KEY; + } + return; + case OEMCrypto_Keybox: + // Fall through to api_version < 12 case. + break; + case OEMCrypto_ProvisioningError: + printf( + "ERROR: OEMCrypto_GetProvisioningMethod() returns " + "OEMCrypto_ProvisioningError\n"); + // Then fall through to api_version < 12 case. + break; + } + } if (uses_keybox) { // If device uses a keybox, try to load the test keybox. if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox()) { @@ -145,4 +192,17 @@ void DeviceFeatures::FilterOut(std::string* current_filter, } } +const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) { + switch (method) { + case OEMCrypto_ProvisioningError: + return "OEMCrypto_ProvisioningError"; + case OEMCrypto_DrmCertificate: + return "OEMCrypto_DrmCertificate"; + case OEMCrypto_Keybox: + return "OEMCrypto_Keybox"; + case OEMCrypto_OEMCertificate: + return "OEMCrypto_OEMCertificate"; + } +} + } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.h b/libwvdrmengine/oemcrypto/test/oec_device_features.h index 76a3fc03..c1fd2d2d 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.h +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.h @@ -16,6 +16,7 @@ class DeviceFeatures { LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys. EXISTING_TEST_KEYBOX, // Keybox is already the test keybox. FORCE_TEST_KEYBOX, // User requested calling InstallKeybox. + TEST_PROVISION_30, // Device has OEM Certificate installed. }; enum DeriveMethod derive_key_method; @@ -26,6 +27,7 @@ class DeviceFeatures { bool cast_receiver; // Device supports alternate rsa signature padding. bool usage_table; // Device saves usage information. uint32_t api_version; + OEMCrypto_ProvisioningMethod provisioning_method; void Initialize(bool is_cast_receiver, bool force_load_test_keybox); std::string RestrictFilter(const std::string& initial_filter); @@ -37,6 +39,7 @@ class DeviceFeatures { }; extern DeviceFeatures global_features; +const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method); } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 5e4b1381..a4103e3f 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -531,6 +531,54 @@ void Session::TestDecryptCTR(bool select_key_first, } } +void Session::LoadOEMCert(bool verify_cert) { + vector public_cert; + size_t public_cert_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_GetOEMPublicCertificate(session_id(), NULL, + &public_cert_length)); + ASSERT_GT(public_cert_length, 0); + public_cert.resize(public_cert_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetOEMPublicCertificate(session_id(), &public_cert[0], + &public_cert_length)); + // load the cert into rsa_key_. + openssl_ptr bio( + BIO_new_mem_buf(&public_cert[0], public_cert_length)); + ASSERT_TRUE(bio.NotNull()); + openssl_ptr cert( + PEM_read_bio_X509(bio.get(), NULL, 0, NULL)); + ASSERT_TRUE(cert.NotNull()); + openssl_ptr pubkey(X509_get_pubkey(cert.get())); + ASSERT_TRUE(pubkey.NotNull()); + public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get()); + if (!public_rsa_) { + cout << "d2i_RSAPrivateKey failed.\n"; + dump_openssl_error(); + ASSERT_TRUE(NULL != public_rsa_); + } + if (verify_cert) { + vector buffer(80); + X509_NAME* name = X509_get_subject_name(cert.get()); + printf(" OEM Certificate Name: %s\n", + X509_NAME_oneline(name, &buffer[0], buffer.size())); + openssl_ptr store(X509_STORE_new()); + ASSERT_TRUE(store.NotNull()); + openssl_ptr store_ctx( + X509_STORE_CTX_new()); + ASSERT_TRUE(store_ctx.NotNull()); + X509_STORE_CTX_init(store_ctx.get(), store.get(), cert.get(), NULL); + // TODO(fredgc): Verify cert is signed by Google. + int result = X509_verify_cert(store_ctx.get()); + ASSERT_GE(0, result) << " OEM Cert not valid. " + << X509_verify_cert_error_string(store_ctx->error); + if (result == 0) { + printf("Cert not verified: %s.\n", + X509_verify_cert_error_string(store_ctx->error)); + } + } +} + void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, size_t message_size, std::vector* signature, diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index a45c2d20..652f8359 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -203,6 +203,9 @@ class Session { void TestDecryptCTR(bool select_key_first = true, OEMCryptoResult expected_result = OEMCrypto_SUCCESS, int key_index = 0); + // Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public + // rsa key into public_rsa_. + void LoadOEMCert(bool verify_cert = false); // Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with // the specified encryption key, and then signs it with the server's mac key. // If encryption_key is null, use the session's enc_key_. diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 9170fb5a..e460bcc7 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -90,7 +90,15 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { cout << " OEMCrypto does not support usage tables." << endl; } ASSERT_GE(version, 8u); - ASSERT_LE(version, 11u); + ASSERT_LE(version, 12u); +} + +TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { + OEMCrypto_ProvisioningMethod provisioning_method = + OEMCrypto_GetProvisioningMethod(); + cout << " Provisioning method = " + << ProvisioningMethodName(provisioning_method) << endl; + ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method); } const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { @@ -473,6 +481,102 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { enc_context.size())); } +class OEMCryptoProv30Test : public OEMCryptoClientTest {}; + +TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) { + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); +} + +TEST_F(OEMCryptoProv30Test, OEMCertValid) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + bool kVerify = true; + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. +} + +TEST_F(OEMCryptoProv30Test, OEMCertSignature) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + OEMCryptoResult sts; + // Sign a Message + vector data(500); + RAND_pseudo_bytes(&data[0], data.size()); + size_t signature_length = 0; + vector signature(1); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + signature.resize(signature_length, 0); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + data, &signature[0], signature_length, kSign_RSASSA_PSS)); +} + +TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + OEMCryptoResult sts; + // Sign a Message + vector data(500); + RAND_pseudo_bytes(&data[0], data.size()); + size_t signature_length = 0; + vector signature(1); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_PKCS1_Block1); + if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { + signature.resize(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_PKCS1_Block1); + } + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; + vector zero(signature_length, 0); + ASSERT_EQ(zero, signature); // signature should not be computed. +} + +TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + OEMCryptoResult sts; + // Sign a Message + static size_t kMaxMessageSize = 8 * 1024; + vector data(kMaxMessageSize); + RAND_pseudo_bytes(&data[0], data.size()); + size_t signature_length = 0; + vector signature(1); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + signature.resize(signature_length); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + data, &signature[0], signature_length, kSign_RSASSA_PSS)); +} + // // AddKey Tests // @@ -968,8 +1072,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithFutureVerification) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - // OEMCrypto should reject API12 until the spec has been defined. - memcpy(s.license().keys[1].control.verification, "kc12", 4); + // OEMCrypto should reject API13 until the spec has been defined. + memcpy(s.license().keys[1].control.verification, "kc13", 4); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp index d00cf419..58c9ede1 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp @@ -21,21 +21,21 @@ namespace wvoec { // These tests are required for LollyPop Android devices. class OEMCryptoAndroidLMPTest : public ::testing::Test { protected: - virtual void SetUp() { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); - } + virtual void SetUp() { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } - virtual void TearDown() { - OEMCrypto_Terminate(); - } + virtual void TearDown() { OEMCrypto_Terminate(); } }; -// Android devices must have a keybox. +// Android devices must have a keybox, or use provisioning 3.0. TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) { uint8_t key_data[256]; size_t key_data_len = sizeof(key_data); - ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, - OEMCrypto_GetKeyData(key_data, &key_data_len)); + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_GetKeyData(key_data, &key_data_len)); + } else { + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); + } } TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) { @@ -48,9 +48,15 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) { } TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) { - ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, - OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, - NULL, 0, NULL, NULL, NULL)); + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, NULL, 0, + NULL, NULL, NULL)); + } else { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_RewrapDeviceRSAKey30(0, NULL, NULL, 0, NULL, 0, NULL, + NULL, NULL)); + } } TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) { @@ -96,7 +102,12 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { } TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); + } else { + // Android should use keybox or provisioning 3.0. + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); + } } TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {