diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index e85cc9ba..afa73db2 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -303,6 +303,12 @@ class CdmEngine { virtual size_t SessionSize() const { return session_map_.Size(); } + // This tells the OEMCrypto adapter to ignore the next |count| keyboxes and + // report that it needs provisioning instead. + static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count) { + return CryptoSession::SetDebugIgnoreKeyboxCount(count); + } + static CdmResponseType ParseDecryptHashString(const std::string& hash_string, CdmSessionId* id, uint32_t* frame_number, diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index f45b547f..b48cd4aa 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -295,6 +295,10 @@ class CryptoSession { bool needs_keybox_provisioning() const { return needs_keybox_provisioning_; } + // This tells the OEMCrypto adapter to ignore the next |count| keyboxes and + // report that it needs provisioning instead. + static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); + // Returns a system-wide singleton instance of SystemFallbackPolicy // to be used for communicating OTA keybox provisioning state between // apps. Returns a null pointer if OTA provisioning is not supported, diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index e12a2b99..1687fd11 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -15,6 +15,10 @@ namespace wvcdm { OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( bool* needs_keybox_provisioning); +// This tells the OEMCrypto adapter to ignore the next |count| keyboxes and +// report that it needs provisioning instead. +OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count); + // This attempts to open a session at the desired security level. // If one level is not available, the other will be used instead. OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 11d5d9f3..349cd368 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -3031,6 +3031,11 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks( return sts; } +CdmResponseType CryptoSession::SetDebugIgnoreKeyboxCount(uint32_t count) { + OEMCryptoResult status = OEMCrypto_SetDebugIgnoreKeyboxCount(count); + return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetDebugIgnoreKeyboxCount"); +} + okp::SystemFallbackPolicy* CryptoSession::GetOkpFallbackPolicy() { const auto getter = [&]() -> okp::SystemFallbackPolicy* { // If not set, then OTA keybox provisioning is not supported or diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index d28bf1b4..8d57fb3e 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -626,6 +627,70 @@ class WatchDog { uint32_t uid_; }; +// The DebugIgnoreKeyboxCount functions are related to testing the Keybox OTA +// Reprovisioning solution. If a positive counter is set, then the keybox will +// be ignored through that many initializations. +std::string GetIgnoreCountFile() { + std::string path; + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL1, + &path)) { + LOGW("DebugIgnoreKeyboxCount: Unable to get base path"); + path = "/data/"; + } + path += "debug_ignore_keybox_count.txt"; + return path; +} + +uint32_t GetDebugIgnoreKeyboxCount() { + const std::string filename = GetIgnoreCountFile(); + wvcdm::FileSystem file_system; + if (!file_system.Exists(filename)) { + return 0; + } + auto file = file_system.Open(filename, file_system.kReadOnly); + if (!file) { + LOGE("Error opening %s", filename.c_str()); + return 0; + } + ssize_t size = file_system.FileSize(filename); + std::string contents(size, ' '); + ssize_t size_read = file->Read(const_cast(contents.data()), size); + if (size != size_read) { + LOGE("Short ignore_debug_keybox_count = %zu", size_read); + return 0; + } + std::istringstream ss(contents); + uint32_t count = 0; + ss >> count; + if (ss.fail()) { + LOGE("Could not parse an integer from '%s'", contents.c_str()); + count = 0; + } + LOGD("Using IgnoreDebugKeyboxCount = %u", count); + return count; +} + +OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) { + const std::string filename = GetIgnoreCountFile(); + wvcdm::FileSystem file_system; + auto file = + file_system.Open(filename, file_system.kCreate | file_system.kTruncate); + if (!file) { + LOGE("Could not create file %s", filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::string contents = std::to_string(count); + ssize_t size = contents.size(); + ssize_t size_written = file->Write(contents.data(), size); + if (size != size_written) { + LOGE("Wrote %zd bytes of %s, not %zd, to file %s", size_written, + contents.c_str(), size, filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + LOGD("Wrote %u to %s", count, filename.c_str()); + return OEMCrypto_SUCCESS; +} + struct LevelSession { FunctionPointers* fcn; OEMCrypto_SESSION session; @@ -1218,7 +1283,12 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( if (status != OEMCrypto_SUCCESS) return status; const OEMCryptoResult keybox_status = gAdapter->ValidateOrInstallKeyboxOrCert(); - if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING) { + uint32_t ignore_count = GetDebugIgnoreKeyboxCount(); + if (keybox_status == OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING || + ignore_count > 0) { + if (ignore_count > 0) { + LOGD("Ignoring keybox status %d", static_cast(keybox_status)); + } *needs_keybox_provisioning = true; return OEMCrypto_SUCCESS; } @@ -1494,6 +1564,10 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, } return OEMCrypto_ERROR_NOT_IMPLEMENTED; } + +OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) { + return SetDebugIgnoreKeyboxCount(count); +} } // namespace wvcdm extern "C" OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, @@ -2818,5 +2892,23 @@ extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session, const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault); if (!fcn) return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (fcn->ProcessOTAKeybox == nullptr) return OEMCrypto_ERROR_NOT_IMPLEMENTED; - return fcn->ProcessOTAKeybox(session, buffer, buffer_length, use_test_key); + const OEMCryptoResult result = + fcn->ProcessOTAKeybox(session, buffer, buffer_length, use_test_key); + if (result == OEMCrypto_SUCCESS) { + // If the new keybox was installed, and we had been told to ignore a valid + // keybox, then we should now decrement the ignore counter. + int32_t ignore_count = GetDebugIgnoreKeyboxCount(); + if (ignore_count > 0) { + ignore_count--; + const OEMCryptoResult save_result = + SetDebugIgnoreKeyboxCount(ignore_count); + if (save_result == OEMCrypto_SUCCESS) { + LOGD("Installed OTA keybox. Ignore count is now %u", ignore_count); + } else { + LOGE("Installed OTA keybox. save ignore count failed %d, count=%u", + static_cast(save_result), ignore_count); + } + } + } + return result; } diff --git a/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp index 36dd09a9..d7c965e6 100644 --- a/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp +++ b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp @@ -82,6 +82,27 @@ TEST_F(CdmOtaKeyboxTest, BasicTest) { FileSystem file_system; TestCdmEngine cdm_engine(&file_system, std::shared_ptr(new EngineMetrics)); + // Create a singleton crypto session to prevent OEMCrypto from being + // terminated. This is really only needed when running the test against the + // testbed OEMCrypto which does save its keybox across init/terminate. + metrics::CryptoMetrics crypto_metrics; + std::unique_ptr crypto_session( + CryptoSession::MakeCryptoSession(&crypto_metrics)); + // Remove any existing certificate. + cdm_engine.Unprovision(kSecurityLevelL1); + std::string system_id; + CdmResponseType system_id_status = + cdm_engine.QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &system_id); + if (system_id_status == NO_ERROR) { + std::cout << " " + << "System ID before test: " << system_id << "\n"; + } else { + std::cout << " " + << "Could not find system id before test. "; + PrintTo(system_id_status, &std::cout); + std::cout << "\n"; + } + CdmSessionId session_id; ConfigTestEnv config = WvCdmTestBase::default_config_; @@ -97,6 +118,17 @@ TEST_F(CdmOtaKeyboxTest, BasicTest) { } std::cout << "First provisioning process.\n"; Provision(&cdm_engine); + system_id_status = + cdm_engine.QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &system_id); + if (system_id_status == NO_ERROR) { + std::cout << " " + << "System ID after first provisioning: " << system_id << "\n"; + } else { + std::cout << " " + << "Could not find system id after first provisioning. "; + PrintTo(system_id_status, &std::cout); + std::cout << "\n"; + } // After the first provisioning pass, we try to open a session again. If the // first provisioning was to install a keybox, this provisioning should be to @@ -111,6 +143,17 @@ TEST_F(CdmOtaKeyboxTest, BasicTest) { } std::cout << "Second provisioning process.\n"; Provision(&cdm_engine); + system_id_status = + cdm_engine.QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &system_id); + if (system_id_status == NO_ERROR) { + std::cout << " " + << "System ID after second provisioning: " << system_id << "\n"; + } else { + std::cout << " " + << "Could not find system id after second provisioning. "; + PrintTo(system_id_status, &std::cout); + std::cout << "\n"; + } // After the second provisioning pass, we should be able to open a session // and continue. diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 1027e598..0b1b6b1e 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -1213,6 +1213,16 @@ void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os) { case ODK_STALE_RENEWAL: *os << "STALE_RENEWAL"; break; + // OPK Values. + case OPK_ERROR_INCOMPATIBLE_VERSION: + *os << "INCOMPATIBLE_VERSION"; + break; + case OPK_ERROR_REMOTE_CALL: + *os << "REMOTE_CALL"; + break; + case OPK_ERROR_NO_PERSISTENT_DATA: + *os << "NO_PERSISTENT_DATA"; + break; } } namespace okp { diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 4095a6ca..06a75613 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -159,6 +159,8 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // Closes the CdmEngine and sessions associated with the given CdmIdentifier. virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier); + virtual CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); + virtual CdmResponseType SetDecryptHash(const std::string& hash_data, CdmSessionId* session_id); virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 53763827..ded47c25 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -500,6 +500,11 @@ CdmResponseType WvContentDecryptionModule::CloseCdm( return NO_ERROR; } +CdmResponseType WvContentDecryptionModule::SetDebugIgnoreKeyboxCount( + uint32_t count) { + return CdmEngine::SetDebugIgnoreKeyboxCount(count); +} + CdmResponseType WvContentDecryptionModule::SetDecryptHash( const std::string& hash_data, CdmSessionId* id) { if (id == nullptr) { diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 71ec8a31..b7e67d53 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -667,6 +668,17 @@ status_t WVDrmPlugin::setPropertyString(const String8& name, } else { mCdmIdentifier.origin = value.string(); } + } else if (name == "debugIgnoreKeyboxCount") { + std::istringstream ss(value.string()); + uint32_t count = 0; + ss >> count; + if (ss.fail()) { + ALOGE("Could not parse an integer from '%s'", value.string()); + count = 0; + return android::BAD_VALUE; + } + CdmResponseType res = mCDM->SetDebugIgnoreKeyboxCount(count); + return mapCdmResponseType(res); } else if (name == "decryptHash") { CdmSessionId sessionId; CdmResponseType res = diff --git a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp index cb680fff..cb3709f9 100644 --- a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "WVDrmPlugin.h" @@ -1431,6 +1432,17 @@ Return WVDrmPlugin::setPropertyString(const hidl_string& propertyName, return Status::BAD_VALUE; } } + } else if (name == "debugIgnoreKeyboxCount") { + std::istringstream ss(_value); + uint32_t count = 0; + ss >> count; + if (ss.fail()) { + ALOGE("Could not parse an integer from '%s'", _value.c_str()); + count = 0; + return Status::BAD_VALUE; + } + CdmResponseType res = mCDM->SetDebugIgnoreKeyboxCount(count); + return mapCdmResponseType(res); } else if (name == "decryptHash") { wvcdm::CdmSessionId sessionId; CdmResponseType res = diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 439b5b76..0d1bba3b 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -57,7 +57,7 @@ * a root of trust to secure content on a device that uses Provisioning * 3.0. Factory Provisioning a device is related to manufacturing methods. This * section describes the API that installs the Widevine Keybox and the - * recommended methods for the OEM’s factory provisioning procedure. + * recommended methods for the OEM's factory provisioning procedure. * * Starting with API version 10, devices should have two keyboxes. One is the * production keybox which may be installed in the factory, or using @@ -69,7 +69,7 @@ * OEMCrypto_Terminate has been called, the production keybox should be active * again. * - * API functions marked as optional may be used by the OEM’s factory + * API functions marked as optional may be used by the OEM's factory * provisioning procedure and implemented in the library, but are not called * from the Widevine DRM Plugin during normal operation. * @@ -90,7 +90,7 @@ * functions in this section are for devices that are provisioned with an OEM * Certificate, i.e. Provisioning 3.0. * - * API functions marked as optional may be used by the OEM’s factory + * API functions marked as optional may be used by the OEM's factory * provisioning procedure and implemented in the library, but are not called * from the Widevine DRM Plugin during normal operation. * @@ -624,10 +624,17 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_MinorAPIVersion _oecc108 #define OEMCrypto_AllocateSecureBuffer _oecc109 #define OEMCrypto_FreeSecureBuffer _oecc110 -// Reserved 111-112 +#define OEMCrypto_CreateEntitledKeySession _oecc111 +#define OEMCrypto_RemoveEntitledKeySession _oecc112 #define OEMCrypto_GenerateOTARequest _oecc113 #define OEMCrypto_ProcessOTAKeybox _oecc114 -// Reserved 115-121 +#define OEMCrypto_OPK_SerializationVersion _oecc115 +#define OEMCrypto_GetBootCertificateChain _oecc116 +#define OEMCrypto_GenerateCertificateKeyPair _oecc117 +#define OEMCrypto_InstallOemPrivateKey _oecc118 +#define OEMCrypto_ReassociateEntitledKeySession _oecc119 +#define OEMCrypto_LoadCasECMKeys _oecc120 +#define OEMCrypto_LoadEntitledContentKeys_v17 _oecc121 // place holder for v17. // clang-format on /// @addtogroup initcontrol @@ -4728,6 +4735,39 @@ OEMCryptoResult OEMCrypto_FreeSecureBuffer( /// @} +/* + * OEMCrypto_OPK_SerializationVersion + * Check the serialization protocol version used by the OEMCrypto Porting Kit + * (OPK). If the OPK is not used, this function must return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. The serialization version is expressed as + * |major.minor|, where |major| and |minor| are integers. The TEE and REE + * serialization versions must match in order for OEMCrypto to communicate + * with the TEE. If the serialization versions do not match, calls to other + * OEMCrypto functions will return OPK_ERROR_INCOMPATIBLE_VERSION. A match is + * achieved if the |major| fields of the TEE and REE versions are the + * same. Differences in only the |minor| fields indicates that the protocols + * are different but are still compatible. + * + * @param[in,out] ree_major: pointer to memory to receive the REE's |major| + * version. On input, *ree_major may be zero to request the serialization + * version of the REE. If *ree_major is non-zero, this function will test the + * TEE's compatibility using the specified REE major version. + * @param[in,out] ree_minor: pointer to memory to receive the REE's |minor| + * version. On input, *ree_minor may be zero to request the serialization + * version of the REE. If *ree_minor is non-zero, this function will test the + * TEE's compatibility using the specified REE minor version. + * @param[out] tee_major: pointer to memory to receive the TEE's |major| version + * @param[out] tee_minor: pointer to memory to receive the TEE's |minor| version + * + * @retval OEMCrypto_SUCCESS success + * @retval OPK_ERROR_INCOMPATIBLE_VERSION + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + */ +OEMCryptoResult OEMCrypto_OPK_SerializationVersion(uint32_t* ree_major, + uint32_t* ree_minor, + uint32_t* tee_major, + uint32_t* tee_minor); + /****************************************************************************/ /****************************************************************************/ /* The following functions are optional. They are only used if the device diff --git a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h index b1ad6e97..2715ea27 100644 --- a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -97,6 +97,11 @@ typedef enum OEMCryptoResult { ODK_TIMER_EXPIRED = ODK_ERROR_BASE + 3, ODK_UNSUPPORTED_API = ODK_ERROR_BASE + 4, ODK_STALE_RENEWAL = ODK_ERROR_BASE + 5, + /* OPK return values */ + OPK_ERROR_BASE = 2000, + OPK_ERROR_REMOTE_CALL = OPK_ERROR_BASE, + OPK_ERROR_INCOMPATIBLE_VERSION = OPK_ERROR_BASE + 1, + OPK_ERROR_NO_PERSISTENT_DATA = OPK_ERROR_BASE + 2, } OEMCryptoResult; /* clang-format on */ diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp index d96d3c45..118829d2 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp @@ -482,11 +482,6 @@ void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message, {ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"}, }; - uint32_t header_size = 0; - for (auto& field : total_fields) { - header_size += ODK_FieldLength(field.type); - } - total_fields.insert(total_fields.end(), extra_fields.begin(), extra_fields.end()); for (auto& field : total_fields) {