From 5070a7b949bcdde5ca86543611134f8204d62e58 Mon Sep 17 00:00:00 2001 From: Kyle Zhang Date: Thu, 27 Apr 2023 19:12:09 +0000 Subject: [PATCH] Exposing the Cast Signing Algorithm 1. Exposing the Cast Signing Algorithm in cdm core. 2. Update core Cast tests to use new core CDM Cast signing API. Bug: 279671867 Bug: 279672538 Change-Id: Ia73c4b5e6dd61edf790bca97a321881d310e7a99 --- libwvdrmengine/cdm/core/include/cdm_engine.h | 7 ++ libwvdrmengine/cdm/core/include/cdm_session.h | 7 ++ .../cdm/core/include/crypto_session.h | 7 +- .../cdm/core/include/wv_cdm_types.h | 1 + libwvdrmengine/cdm/core/src/cdm_engine.cpp | 44 ++++++++++ libwvdrmengine/cdm/core/src/cdm_session.cpp | 12 +++ .../cdm/core/src/crypto_session.cpp | 6 +- libwvdrmengine/cdm/core/src/wv_cdm_types.cpp | 2 + .../cdm/core/test/policy_integration_test.cpp | 81 ++++++------------- libwvdrmengine/include/mapErrors-inl.h | 1 + 10 files changed, 108 insertions(+), 60 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 9e00f191..f854e8e3 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -388,6 +388,13 @@ class CdmEngine { virtual void SetDefaultOtaKeyboxFallbackDurationRules(); virtual void SetFastOtaKeyboxFallbackDurationRules(); + // A signing method specifically used by Cast. + // This method should not be used otherwise. + virtual CdmResponseType SignRSA(const std::string& wrapped_key, + const std::string& message, + std::string* signature, + RSA_Padding_Scheme padding_scheme); + protected: friend class CdmEngineFactory; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 3e4e7083..02e05b29 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -219,6 +219,13 @@ class CdmSession { virtual metrics::SessionMetrics* GetMetrics() { return metrics_.get(); } + virtual CdmResponseType LoadCastPrivateKey( + const CryptoWrappedKey& private_key); + + virtual CdmResponseType GenerateRSASignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme); + private: friend class CdmSessionTest; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index e74e736a..3abdebcb 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -347,6 +347,11 @@ class CryptoSession { virtual CdmResponseType LoadOtaProvisioning(bool use_test_key, const std::string& response); + // Cast specific generate signature method. + virtual CdmResponseType GenerateRsaSignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme); + protected: // Creates an instance of CryptoSession with the given |crypto_metrics|. // |crypto_metrics| is owned by the caller, must NOT be null, and must @@ -385,8 +390,6 @@ class CryptoSession { // Note: This function will lock the global static field lock in write mode. bool SetUpUsageTable(RequestedSecurityLevel requested_security_level); - CdmResponseType GenerateRsaSignature(const std::string& message, - std::string* signature); size_t GetMaxSubsampleRegionSize(); bool SetDestinationBufferType(); diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 6a9fcfe0..3d9fde2d 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -460,6 +460,7 @@ enum CdmResponseEnum : int32_t { STORE_ATSC_LICENSE_DEVICE_FILES_INIT_ERROR = 394, STORE_ATSC_LICENSE_ERROR = 395, SESSION_NOT_FOUND_GENERIC_CRYPTO = 396, + SESSION_NOT_FOUND_24 = 397, // Don't forget to add new values to // * core/src/wv_cdm_types.cpp // * android/include/mapErrors-inl.h diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 2f47e3ec..012e76c8 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -2340,4 +2340,48 @@ void CdmEngine::SetFastOtaKeyboxFallbackDurationRules() { } system_fallback_policy->SetFastBackoffDurationRules(); } + +CdmResponseType CdmEngine::SignRSA(const std::string& wrapped_key, + const std::string& message, + std::string* signature, + RSA_Padding_Scheme padding_scheme) { + // Try to open cdm session. + CdmSessionId session_id; + auto sts = OpenSession("com.widevine", nullptr, nullptr, &session_id); + if (sts != NO_ERROR) { + LOGE("OpenSession failed, status: %d", static_cast(sts)); + return sts; + } + + // Retrieve the cdm session + std::shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { + LOGE("Session not found: session_id = %s", IdToString(session_id)); + return CdmResponseType(SESSION_NOT_FOUND_24); + } + + // Load cast private key for signing + CryptoWrappedKey key(CryptoWrappedKey::kRsa, wrapped_key); + sts = session->LoadCastPrivateKey(key); + if (sts != NO_ERROR) { + LOGE("LoadCastPrivateKey failed, status: %d", static_cast(sts)); + return sts; + } + + // Generate Rsa signature for cast message + sts = session->GenerateRSASignature(message, signature, padding_scheme); + if (sts != NO_ERROR) { + LOGE("GenerateRSASignature failed, status: %d", static_cast(sts)); + return sts; + } + + // Try to close cdm session. + sts = CloseSession(session_id); + if (sts != NO_ERROR) { + LOGE("CloseSession failed, status: %d", static_cast(sts)); + return sts; + } + + return sts; +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 90024c50..05129ba8 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -1302,6 +1302,18 @@ bool CdmSession::HasRootOfTrustBeenRenewed() { return true; } +CdmResponseType CdmSession::LoadCastPrivateKey( + const CryptoWrappedKey& private_key) { + return crypto_session_->LoadCertificatePrivateKey(private_key); +} + +CdmResponseType CdmSession::GenerateRSASignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme) { + return crypto_session_->GenerateRsaSignature(message, signature, + scheme); +} + // For testing only - takes ownership of pointers void CdmSession::set_license_parser(CdmLicense* license_parser) { diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index b748e757..a46c49d5 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -1635,7 +1635,8 @@ CdmResponseType CryptoSession::GenerateDerivedKeys( } CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message, - std::string* signature) { + std::string* signature, + RSA_Padding_Scheme scheme) { LOGV("Generating RSA signature: id = %u", oec_session_id_); RETURN_IF_NULL(signature, PARAMETER_NULL); @@ -1652,7 +1653,7 @@ CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message, oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), - &length, kSign_RSASSA_PSS), + &length, scheme), metrics_, oemcrypto_generate_rsa_signature_, sts, metrics::Pow2Bucket(length)); }); @@ -3401,4 +3402,5 @@ CryptoSession* CryptoSessionFactory::MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics) { return new CryptoSession(crypto_metrics); } + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp index adfed848..6015f1f7 100644 --- a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp +++ b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp @@ -867,6 +867,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) { return "STORE_ATSC_LICENSE_ERROR"; case SESSION_NOT_FOUND_GENERIC_CRYPTO: return "SESSION_NOT_FOUND_GENERIC_CRYPTO"; + case SESSION_NOT_FOUND_24: + return "SESSION_NOT_FOUND_24"; } return UnknownValueRep(cdm_response_enum); } diff --git a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp index f254e934..ff500342 100644 --- a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp +++ b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp @@ -114,11 +114,13 @@ TEST_F(CorePIGTest, OfflineHWSecureRequired) { ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); } -TEST_F(CorePIGTest, CastReceiverProvisioning) { - auto digest = wvutil::a2b_hex( // digest info header +TEST_F(CorePIGTest, CastReceiverProvisioningUsingCdm) { + std::string digest_hex_str = + // digest info header "3021300906052b0e03021a05000414" // sha1 of kMessage - "d2662f893aaec72f3ca6decc2aa942f3949e8b21"); + "d2662f893aaec72f3ca6decc2aa942f3949e8b21"; + auto digest = wvutil::a2b_hex(digest_hex_str); if (!wvoec::global_features.cast_receiver) { GTEST_SKIP() << "OEMCrypto does not support CAST Receiver functionality"; @@ -129,56 +131,30 @@ TEST_F(CorePIGTest, CastReceiverProvisioning) { 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)); + // cdm_engine_.SignRSA + std::string signature_str; + std::string digest_str(digest.begin(), digest.end()); + ASSERT_EQ(NO_ERROR, cdm_engine_.SignRSA(provisioner.wrapped_key(), digest_str, + &signature_str, kSign_PKCS1_Block1)); - 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)); + // Verify the generated signature + std::vector signature(signature_str.begin(), signature_str.end()); 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()); + std::unique_ptr bio(BIO_new(BIO_s_mem()), BIO_free_all); ASSERT_NE(bio, nullptr); - ASSERT_GT(BIO_puts(bio, cert_str_ptr), 0); - X509* x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + ASSERT_GT(BIO_puts(bio.get(), cert_str_ptr), 0); + std::unique_ptr x509( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free); ASSERT_NE(x509, nullptr); - EVP_PKEY* pubkey = X509_get_pubkey(x509); + std::unique_ptr pubkey( + X509_get_pubkey(x509.get()), EVP_PKEY_free); ASSERT_NE(pubkey, nullptr); // remove digest info header for verification @@ -188,27 +164,20 @@ TEST_F(CorePIGTest, CastReceiverProvisioning) { // 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 */); + std::unique_ptr ctx( + EVP_PKEY_CTX_new(pubkey.get(), nullptr /* no engine */), EVP_PKEY_CTX_free); + 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); + ASSERT_GT(EVP_PKEY_verify_init(ctx.get()), 0); + ASSERT_GT(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING), 0); + ASSERT_GT(EVP_PKEY_CTX_set_signature_md(ctx.get(), 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); + EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), sig, siglen, md, mdlen)); } } // namespace wvcdm diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index 62b5cc4d..c61d1e0b 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -253,6 +253,7 @@ static inline WvStatus mapCdmResponseType(wvcdm::CdmResponseType res) { case wvcdm::SESSION_NOT_FOUND_14: case wvcdm::SESSION_NOT_FOUND_15: case wvcdm::SESSION_NOT_FOUND_16: + case wvcdm::SESSION_NOT_FOUND_24: case wvcdm::SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE: case wvcdm::STORAGE_PROHIBITED: case wvcdm::STORE_LICENSE_ERROR_2: