Added an OTA keybox provisioner.
[ Merge of http://go/wvgerrit/133729 ] [ Cherry pick of http://ag/15836224 ] The OtaKeyboxProvisioner is a system-wide provisioner for sharing the provisioning workflow between CDM engines. Bug: 189232882 Test: GtsMediaTestCases Change-Id: I873af3087cc05e1831bdd1d2c14fb002b73e6902 Added keybox provisioning proto fields. [ Merge of http://go/wvgerrit/133730 and http://go/ag/15113032 ] This CL copies over the required license_protocol.proto changes that are required for OTA keybox provisioning. These fields are defined in the server-side certificate_provisioning.proto, defined in http://cl/377533774. Note, changes are slightly different from server proto due to the RVC version of license_protocol.proto being out of date with SC and newer changes. Bug: 189232882 Test: run_x86_64_tests Change-Id: I55fcf6a7ac2ba4b6026b9acc63e822ff33c431d9 Added OTA keybox provisioning device files. [ Merge of http://go/wvgerrit/133743 and http://go/ag/15421141 ] This change adds a new set of proto messages/fields the CDM's device files for recording device and engine information around OTA keybox provisioning (OKP). To make cleanup and thread protection possible, there is a single file which will contain all the information for the device as a whole and each CDM engine tied to an app/origin. Bug: 189232882 Test: Linux unit tests Change-Id: Iaf80cd6342f32657e04416750d9b278d935821a5 Client ID for OKP requests. [ Merge of http://go/wvgerrit/133744 and http://go/ag/15645331 ] Extended the CDM ClientIdentification class to support a subset of client info used for OKP requests. Bug: 189232882 Test: Android unit tests Change-Id: I6aafb4f2164efe69bc733ece0a912f0e91893b91
This commit is contained in:
242
libwvdrmengine/cdm/core/test/ota_keybox_provisioner_test.cpp
Normal file
242
libwvdrmengine/cdm/core/test/ota_keybox_provisioner_test.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
#include "ota_keybox_provisioner.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "crypto_session.h"
|
||||
|
||||
namespace wvcdm {
|
||||
using ::testing::DoAll;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
namespace {
|
||||
const std::string kEmptyString;
|
||||
const std::string kFakeOtaProvisioningRequest =
|
||||
"Totally real device ID, not fake" // Device ID : 32 bytes
|
||||
"Totally real model ID, also real" // Model ID : 32 bytes
|
||||
"Super secure key" // Encryped session key : 16 bytes
|
||||
"Undoubtedly authentic signature!"; // Signature : 32 bytes
|
||||
|
||||
const std::string kFakeOtaProvisioningResponse =
|
||||
"Definitely an IV" // IV : 16 bytes
|
||||
// Keybox : 128 bytes
|
||||
"I'm a keybox, look at my keys and box-like appearance []. You might "
|
||||
"be thinking 'you are not a real keybox', but you'd be wrong"
|
||||
"Scribble scribble dot slash dot "; // Signature : 32 bytes
|
||||
|
||||
const std::string kAnotherFakeOtaProvisioningResponse =
|
||||
"Looks like an IV" // IV : 16 bytes
|
||||
// Keybox : 128 bytes
|
||||
"I am also a keybox. It's so safe to assume I'm a real keybox that "
|
||||
"attempting to verify that will look very embarrassing for you"
|
||||
"A drawing of boat with dolphins "; // Signature : 32 bytes
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MockCryptoSession() : CryptoSession(&crypto_metrics_) {}
|
||||
~MockCryptoSession() {}
|
||||
|
||||
bool IsOpen() override { return is_open_; }
|
||||
void SetIsOpen(bool is_open) { is_open_ = is_open; }
|
||||
|
||||
MOCK_METHOD2(PrepareOtaProvisioningRequest,
|
||||
CdmResponseType(bool, std::string*));
|
||||
MOCK_METHOD2(LoadOtaProvisioning, CdmResponseType(bool, const std::string&));
|
||||
|
||||
void ExpectRequest(const std::string& request, CdmResponseType result) {
|
||||
if (result == NO_ERROR) {
|
||||
EXPECT_CALL(*this, PrepareOtaProvisioningRequest(false, NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(request), Return(NO_ERROR)));
|
||||
} else {
|
||||
EXPECT_CALL(*this, PrepareOtaProvisioningRequest(false, NotNull()))
|
||||
.WillOnce(Return(result));
|
||||
}
|
||||
}
|
||||
|
||||
void ExpectResponse(const std::string& response, CdmResponseType result) {
|
||||
EXPECT_CALL(*this, LoadOtaProvisioning(false, response))
|
||||
.WillOnce(Return(result));
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_open_ = false;
|
||||
|
||||
static metrics::CryptoMetrics crypto_metrics_;
|
||||
};
|
||||
|
||||
metrics::CryptoMetrics MockCryptoSession::crypto_metrics_;
|
||||
} // namespace
|
||||
|
||||
class OtaKeyboxProvisionerTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
crypto_session_.reset(new MockCryptoSession());
|
||||
provisioner_ = OtaKeyboxProvisioner::Create();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
crypto_session_.reset();
|
||||
provisioner_.reset();
|
||||
}
|
||||
|
||||
std::unique_ptr<MockCryptoSession> crypto_session_;
|
||||
std::unique_ptr<OtaKeyboxProvisioner> provisioner_;
|
||||
};
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, SingleRequestSingleResponse) {
|
||||
// Pre-request conditions.
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(0u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
|
||||
// Generate request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &request));
|
||||
EXPECT_EQ(request, kFakeOtaProvisioningRequest);
|
||||
|
||||
// Post-request, pre-response conditions.
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(1u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
|
||||
// Load response.
|
||||
const std::string response = kFakeOtaProvisioningResponse;
|
||||
crypto_session_->ExpectResponse(response, NO_ERROR);
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), response));
|
||||
|
||||
// Post-response conditions.
|
||||
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(1u, provisioner_->request_count());
|
||||
EXPECT_EQ(1u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, MultipleRequestsMultipleResponse) {
|
||||
// Generate first request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string first_request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &first_request));
|
||||
EXPECT_EQ(first_request, kFakeOtaProvisioningRequest);
|
||||
|
||||
// Generate second request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string second_request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &second_request));
|
||||
EXPECT_EQ(second_request, kFakeOtaProvisioningRequest);
|
||||
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(2u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
|
||||
// Load first response.
|
||||
const std::string first_response = kFakeOtaProvisioningResponse;
|
||||
crypto_session_->ExpectResponse(first_response, NO_ERROR);
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), first_response));
|
||||
|
||||
// Loading second response should be silently dropped.
|
||||
// No call to CryptoSession.
|
||||
const std::string second_response = kAnotherFakeOtaProvisioningResponse;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), second_response));
|
||||
|
||||
// Post-response conditions.
|
||||
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(2u, provisioner_->request_count());
|
||||
EXPECT_EQ(2u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, NullRequestParameters) {
|
||||
// Null CryptoSession
|
||||
std::string request;
|
||||
EXPECT_NE(NO_ERROR,
|
||||
provisioner_->GenerateProvisioningRequest(nullptr, &request));
|
||||
|
||||
// Null request, no call to CryptoSession.
|
||||
EXPECT_NE(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), nullptr));
|
||||
// Counter should not increase.
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(0u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, EmptyRequest) {
|
||||
// Generate request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &request));
|
||||
|
||||
// Attempt to load empty response. No call to CryptoSession.
|
||||
const std::string response = "";
|
||||
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), response));
|
||||
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(1u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, OtaProvisioningNotImplemented) {
|
||||
// Generate request.
|
||||
crypto_session_->ExpectRequest(kEmptyString, NOT_IMPLEMENTED_ERROR);
|
||||
std::string request;
|
||||
EXPECT_EQ(NOT_IMPLEMENTED_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &request));
|
||||
// Counter should not increase.
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(0u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, ResponseRejected) {
|
||||
// Generate request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &request));
|
||||
|
||||
// Attempt to load response. OEMCrypto rejects response.
|
||||
const std::string response = kFakeOtaProvisioningResponse;
|
||||
crypto_session_->ExpectResponse(response, UNKNOWN_ERROR);
|
||||
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), response));
|
||||
|
||||
// Should not be provisioned.
|
||||
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(1u, provisioner_->request_count());
|
||||
EXPECT_EQ(0u, provisioner_->response_count());
|
||||
}
|
||||
|
||||
TEST_F(OtaKeyboxProvisionerTest, GenerateRequestAfterProvisioning) {
|
||||
// Generate request.
|
||||
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||
std::string first_request;
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &first_request));
|
||||
|
||||
// Load response.
|
||||
const std::string response = kFakeOtaProvisioningResponse;
|
||||
crypto_session_->ExpectResponse(response, NO_ERROR);
|
||||
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||
crypto_session_.get(), response));
|
||||
|
||||
// Attempt to generate second request. Should fail.
|
||||
std::string second_request;
|
||||
EXPECT_NE(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||
crypto_session_.get(), &second_request));
|
||||
|
||||
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||
EXPECT_EQ(1u, provisioner_->request_count());
|
||||
EXPECT_EQ(1u, provisioner_->response_count());
|
||||
}
|
||||
} // namespace wvcdm
|
||||
Reference in New Issue
Block a user