From 18da273c42c1051e2adedbed74d916541fc1e030 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Tue, 18 Feb 2020 14:29:05 -0800 Subject: [PATCH] Adjust provisioning retry Merge from Widevine repo of http://go/wvgerrit/94243 When generating a second provisioning request, a new session should be opened because a session can only have one nonce for v16. For Provisioning 3.0 devices, the OEM Cert's private key must be loaded in the new session. Test: WvCdmRequestLicenseTest.ProvisioningInterposedRetryTest Bug: 135288420 Nonce reuse Bug: 141655126 Cert reload Change-Id: I8a96566142c4d4380e2bdd571e8d363a7a1f74d4 --- .../cdm/core/src/certificate_provisioning.cpp | 1 + .../cdm/core/src/crypto_session.cpp | 17 ++-- .../cdm/test/request_license_test.cpp | 89 +++++++------------ 3 files changed, 41 insertions(+), 66 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index f62b5687..7f35608f 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -199,6 +199,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( default_url->assign(kProvisioningServerUrl); + if (crypto_session_->IsOpen()) crypto_session_->Close(); CdmResponseType status = crypto_session_->Open(requested_security_level); if (NO_ERROR != status) { LOGE("Failed to create a crypto session: status = %d", diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 4f1b13b7..52b71b57 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -359,14 +359,6 @@ CdmResponseType CryptoSession::GetTokenFromOemCert(std::string* token) { return NO_ERROR; } - // TODO(b/141655126): This function can be optimized to not load private - // key when it isn't needed. - status = OEMCrypto_LoadOEMPrivateKey(oec_session_id_); - if (status != OEMCrypto_SUCCESS) { - return MapOEMCryptoResult(status, GET_TOKEN_FROM_OEM_CERT_ERROR, - "GetTokenFromOemCert"); - } - std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0'); bool retrying = false; while (true) { @@ -1089,6 +1081,15 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest( if (pre_provision_token_type_ == kClientTokenKeybox) { const CdmResponseType status = GenerateDerivedKeys(message); if (status != NO_ERROR) return status; + } else if (pre_provision_token_type_ == kClientTokenOemCert) { + const OEMCryptoResult status = OEMCrypto_LoadOEMPrivateKey(oec_session_id_); + if (status != OEMCrypto_SUCCESS) { + return MapOEMCryptoResult(status, GET_TOKEN_FROM_OEM_CERT_ERROR, + "GetTokenFromOemCert"); + } + } else { + LOGE("Unknown method %d", pre_provision_token_type_); + return UNKNOWN_CLIENT_TOKEN_TYPE; } OEMCryptoResult sts; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 9410d8cb..16ae4535 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -2303,6 +2303,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) { kEmptyServiceCertificate, &key_msg2, &provisioning_server)); EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl); + // Second message should succeed. key_msg_ = key_msg2; std::string response = GetCertRequestResponse(config_.provisioning_server()); EXPECT_NE(0, static_cast(response.size())); @@ -2312,6 +2313,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) { EXPECT_EQ(0, static_cast(cert.size())); EXPECT_EQ(0, static_cast(wrapped_key.size())); + // First message is ignored because second already succeeded. key_msg_ = key_msg1; response = GetCertRequestResponse(config_.provisioning_server()); EXPECT_NE(0, static_cast(response.size())); @@ -2330,68 +2332,39 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningInterspersedRetryTest) { CdmCertificateType cert_type = kCertificateWidevine; std::string cert_authority, cert, wrapped_key; - std::string provisioning_model; + CdmKeyMessage key_msg1, key_msg2; + EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_->QueryStatus(kLevelDefault, - wvcdm::QUERY_KEY_PROVISIONING_MODEL, - &provisioning_model)); + decryptor_->GetProvisioningRequest( + cert_type, cert_authority, kDefaultCdmIdentifier, + kEmptyServiceCertificate, &key_msg1, &provisioning_server)); + EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl); - std::string value; EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_->QueryStatus( - kLevelDefault, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value)); - std::istringstream ss(value); - uint32_t api_version; - ss >> api_version; - ASSERT_FALSE(ss.fail()); - EXPECT_TRUE(ss.eof()); + decryptor_->GetProvisioningRequest( + cert_type, cert_authority, kDefaultCdmIdentifier, + kEmptyServiceCertificate, &key_msg2, &provisioning_server)); + EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl); - if (provisioning_model == wvcdm::QUERY_VALUE_KEYBOX || - (provisioning_model == wvcdm::QUERY_VALUE_OEM_CERTIFICATE && - api_version >= 15)) { - // From OEMCrypto v15.2 onwards, the nonce table size is fixed. We can't - // test this for API versions before that if they use OEM certificates. - std::vector key_msgs; + // First request expected to fail, because only one message may be active. + key_msg_ = key_msg1; + std::string response = GetCertRequestResponse(config_.provisioning_server()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(LOAD_PROVISIONING_ERROR, + decryptor_->HandleProvisioningResponse( + kDefaultCdmIdentifier, response, &cert, &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); - // TODO(b/135288420): There can be only one request per OEMCrypto session. - // This code must change. - wvcdm::CdmResponseType first_request_error; - // For keyboxes we use derived keys as part of the provisioning request. - // These get updated each request, therefore any request before the latest - // fails, so we only need 2 requests. - key_msgs.resize(2); - first_request_error = wvcdm::REWRAP_DEVICE_RSA_KEY_ERROR; - - for (size_t i = 0; i < key_msgs.size(); i++) { - EXPECT_EQ( - wvcdm::NO_ERROR, - decryptor_->GetProvisioningRequest( - cert_type, cert_authority, kDefaultCdmIdentifier, - kEmptyServiceCertificate, &key_msgs[i], &provisioning_server)); - EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl); - } - - // First request that we expect to fail. - key_msg_ = key_msgs[0]; - std::string response = - GetCertRequestResponse(config_.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); - EXPECT_EQ(first_request_error, - decryptor_->HandleProvisioningResponse( - kDefaultCdmIdentifier, response, &cert, &wrapped_key)); - EXPECT_EQ(0, static_cast(cert.size())); - EXPECT_EQ(0, static_cast(wrapped_key.size())); - - // Second request that we expect to succeed. - key_msg_ = key_msgs[1]; - response = GetCertRequestResponse(config_.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); - EXPECT_EQ(wvcdm::NO_ERROR, - decryptor_->HandleProvisioningResponse( - kDefaultCdmIdentifier, response, &cert, &wrapped_key)); - EXPECT_EQ(0, static_cast(cert.size())); - EXPECT_EQ(0, static_cast(wrapped_key.size())); - } + // Second message should succeed. + key_msg_ = key_msg2; + response = GetCertRequestResponse(config_.provisioning_server()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_->HandleProvisioningResponse( + kDefaultCdmIdentifier, response, &cert, &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); decryptor_->CloseSession(session_id_); } @@ -3981,7 +3954,7 @@ TEST_F(WvCdmRequestLicenseTest, RemoveCorruptedUsageInfoTest) { CdmSecurityLevel security_level = GetDefaultSecurityLevel(); FileSystem file_system; DeviceFiles handle(&file_system); - EXPECT_TRUE(handle.Init(security_level)); + ASSERT_TRUE(handle.Init(security_level)); std::vector psts; EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( DeviceFiles::GetUsageInfoFileName(""), &psts));