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) {