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:
Rahul Frias
2021-09-15 02:56:19 -07:00
committed by Alex Dale
parent bac33dbc6e
commit 3acc64a478
13 changed files with 586 additions and 30 deletions

View 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