diff --git a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp index f82160d4..f254e934 100644 --- a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp +++ b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp @@ -6,6 +6,9 @@ // would do. They verify that policies specified on UAT are honored on the // device. +#include +#include +#include #include #include @@ -16,9 +19,9 @@ #include "license_holder.h" #include "log.h" #include "oec_device_features.h" +#include "provisioning_holder.h" #include "test_base.h" #include "test_printers.h" - #include "wv_cdm_types.h" namespace wvcdm { @@ -111,4 +114,101 @@ TEST_F(CorePIGTest, OfflineHWSecureRequired) { ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); } +TEST_F(CorePIGTest, CastReceiverProvisioning) { + auto digest = wvutil::a2b_hex( // digest info header + "3021300906052b0e03021a05000414" + // sha1 of kMessage + "d2662f893aaec72f3ca6decc2aa942f3949e8b21"); + + if (!wvoec::global_features.cast_receiver) { + GTEST_SKIP() << "OEMCrypto does not support CAST Receiver functionality"; + } + + // Provision x509 cert for CAST Receiver. + ProvisioningHolder provisioner(&cdm_engine_, config_.provisioning_server(), + config_.provisioning_service_certificate()); + provisioner.Provision(kCertificateX509, binary_provisioning_); + + // cdm_engine_.OpenSession here is to load test keybox + // in order to successfully OEMCrypto_LoadDRMPrivateKey + std::string session_id; + CdmResponseType status = cdm_engine_.OpenSession( + config_.key_system(), nullptr, nullptr, &session_id); + ASSERT_EQ(NO_ERROR, status); + ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id)); + + std::string wrapped_key_str = provisioner.wrapped_key(); + std::vector wrapped_key(wrapped_key_str.begin(), + wrapped_key_str.end()); + OEMCrypto_SESSION oemcrypto_session; + OEMCryptoResult sts = + OEMCrypto_OpenSession(&oemcrypto_session, kLevelDefault); + if (sts != OEMCrypto_SUCCESS) { + LOGE("Fail in OEMCrypto_OpenSession"); + } + + // Stop test when OEMCrypto_LoadDRMPrivateKey fails. + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey( + oemcrypto_session, OEMCrypto_RSA_Private_Key, + wrapped_key.data(), wrapped_key.size())); + + // Generate signature for the digest + size_t signatureSize = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_GenerateRSASignature(oemcrypto_session, digest.data(), + digest.size(), nullptr, + &signatureSize, kSign_PKCS1_Block1)); + std::vector signature; + signature.resize(signatureSize); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateRSASignature(oemcrypto_session, digest.data(), + digest.size(), signature.data(), + &signatureSize, kSign_PKCS1_Block1)); + LOGI("digest.size(): %zu, signature.size(): %zu", digest.size(), + signature.size()); + + // Verify the generated signature + std::string cert = provisioner.certificate(); + const char* cert_str_ptr = cert.c_str(); + LOGI("cert: %s", cert_str_ptr); + + // Extract the public key from the x509 cert chain + BIO* bio = BIO_new(BIO_s_mem()); + ASSERT_NE(bio, nullptr); + ASSERT_GT(BIO_puts(bio, cert_str_ptr), 0); + X509* x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + ASSERT_NE(x509, nullptr); + EVP_PKEY* pubkey = X509_get_pubkey(x509); + ASSERT_NE(pubkey, nullptr); + + // remove digest info header for verification + // SHA1 is 20 bytes long + digest.erase(digest.begin(), digest.begin() + digest.size() - 20); + + // Modified from openssl example + // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_verify_init.html + // Set RSA padding as RSA_PKCS1_PADDING and digest algo to SHA1. + EVP_PKEY_CTX* ctx; + unsigned char* md = digest.data(); + unsigned char* sig = signature.data(); + size_t mdlen = digest.size(); + size_t siglen = signature.size(); + + ctx = EVP_PKEY_CTX_new(pubkey, nullptr /* no engine */); + ASSERT_NE(ctx, nullptr); + ASSERT_GT(EVP_PKEY_verify_init(ctx), 0); + ASSERT_GT(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING), 0); + ASSERT_GT(EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()), 0); + + /* Perform operation */ + EXPECT_EQ(1, EVP_PKEY_verify(ctx, sig, siglen, md, mdlen)); + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pubkey); + BIO_free(bio); + X509_free(x509); + + OEMCrypto_CloseSession(oemcrypto_session); + cdm_engine_.CloseSession(session_id); +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/provisioning_holder.h b/libwvdrmengine/cdm/core/test/provisioning_holder.h index 3e730834..073b46a8 100644 --- a/libwvdrmengine/cdm/core/test/provisioning_holder.h +++ b/libwvdrmengine/cdm/core/test/provisioning_holder.h @@ -20,6 +20,8 @@ class ProvisioningHolder { provisioning_server_url_(provisioning_server_url), provisioning_service_certificate_(provisioning_service_certificate) {} void Provision(CdmCertificateType cert_type, bool binary_provisioning); + std::string certificate() const { return certificate_; } + std::string wrapped_key() const { return wrapped_key_; } protected: TestCdmEngine* cdm_engine_; diff --git a/libwvdrmengine/cdm/core/test/test_base.h b/libwvdrmengine/cdm/core/test/test_base.h index 8436724f..c8d48776 100644 --- a/libwvdrmengine/cdm/core/test/test_base.h +++ b/libwvdrmengine/cdm/core/test/test_base.h @@ -88,6 +88,7 @@ class TestCdmEngine : public CdmEngine { TestCdmEngine(wvutil::FileSystem* file_system, std::shared_ptr metrics) : CdmEngine(file_system, metrics) {} + const CdmSession* GetCdmSession(std::string sessionId) const; }; class WvCdmTestBaseWithEngine : public WvCdmTestBase { diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 22eb3bc5..71672ec3 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -2534,33 +2534,6 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterspersedRetryTest) { decryptor_->CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, DISABLED_X509ProvisioningTest) { - decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, - nullptr, &session_id_); - std::string provisioning_server; - CdmCertificateType cert_type = kCertificateX509; - // TODO(rfrias): insert appropriate CA here - std::string cert_authority = "cast.google.com"; - std::string cert, wrapped_key; - - EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_->GetProvisioningRequest( - cert_type, cert_authority, kDefaultCdmIdentifier, - kEmptyServiceCertificate, kLevelDefault, &key_msg_, - &provisioning_server)); - EXPECT_THAT(provisioning_server, - IsSimilarUrlTo(kDefaultProvisioningServerUrl)); - - std::string response = GetCertRequestResponse(config_.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); - EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse( - kDefaultCdmIdentifier, response, kLevelDefault, - &cert, &wrapped_key)); - EXPECT_NE(0, static_cast(cert.size())); - EXPECT_NE(0, static_cast(wrapped_key.size())); - decryptor_->CloseSession(session_id_); -} - TEST_F(WvCdmRequestLicenseTest, ProvisioningSpoidTest) { CdmProvisioningResponse provisioning_response;