Export provisioning sdk

Change-Id: I4d47d80444c9507f84896767dc676112ca11e901
This commit is contained in:
Kongqun Yang
2017-01-24 20:06:25 -08:00
parent d2903a4951
commit 8d17e4549a
110 changed files with 10187 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
################################################################################
# Copyright 2016 Google Inc.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
# Build file for provisioning 3.0 SDK internal library.
# Only accessible by public provisioning_sdk apis.
package_group(
name = "internal",
packages = [
"//provisioning_sdk/...",
],
)
package(
default_visibility = [":internal"],
)
cc_library(
name = "provisioning_engine_impl",
srcs = ["provisioning_engine_impl.cc"],
hdrs = ["provisioning_engine_impl.h"],
deps = [
":oem_device_cert",
"//base",
"//common:random_util",
"//common:rsa_key",
"//provisioning_sdk/internal/certificates:root_certificates",
"//provisioning_sdk/public:certificate_type",
"//provisioning_sdk/public:provisioning_status",
"//protos/public:device_certificate_proto",
"//protos/public:provisioned_device_info_proto",
"//protos/public:signed_device_certificate_proto",
],
)
cc_test(
name = "provisioning_engine_impl_test",
size = "small",
srcs = ["provisioning_engine_impl_test.cc"],
deps = [
":provisioning_engine_impl",
"//base",
"//external:gtest_main",
"//common:mock_rsa_key",
"//provisioning_sdk/public:certificate_type",
],
)
cc_library(
name = "provisioning_session_impl",
srcs = ["provisioning_session_impl.cc"],
hdrs = ["provisioning_session_impl.h"],
deps = [
":oem_device_cert",
":provisioning_engine_impl",
"//base",
"//common:aes_cbc_util",
"//common:random_util",
"//common:rsa_key",
"//common:sha_util",
"//provisioning_sdk/public:provisioning_status",
"//protos/public:certificate_provisioning_proto",
"//protos/public:client_identification_proto",
"//protos/public:device_certificate_proto",
"//protos/public:provisioned_device_info_proto",
],
)
cc_test(
name = "provisioning_session_impl_test",
size = "small",
srcs = ["provisioning_session_impl_test.cc"],
deps = [
":oem_device_cert",
":provisioning_engine_impl",
":provisioning_session_impl",
"//external:gtest_main",
"//common:aes_cbc_util",
"//common:mock_rsa_key",
"//common:sha_util",
],
)
cc_library(
name = "oem_device_cert",
srcs = ["oem_device_cert.cc"],
hdrs = ["oem_device_cert.h"],
deps = [
"//base",
"//external:openssl",
"//common:openssl_util",
"//common:rsa_key",
"//provisioning_sdk/internal/certificates:root_certificates",
"//provisioning_sdk/public:certificate_type",
],
)
cc_test(
name = "oem_device_cert_test",
size = "small",
srcs = ["oem_device_cert_test.cc"],
deps = [
":oem_device_cert",
"//external:gtest_main",
"//provisioning_sdk/internal/certificates:test_certificates",
],
)

View File

@@ -0,0 +1,117 @@
################################################################################
# Copyright 2016 Google Inc.
#
# This software is licensed under the terms defined in the Widevine Master
# License Agreement. For a copy of this agreement, please contact
# widevine-licensing@google.com.
################################################################################
# Build file for test certificates.
package(
default_visibility = ["//visibility:public"],
)
cc_library(
name = "root_certificates",
srcs = [
"root_certificates.cc",
":drm_ca_root_dev_cert",
":drm_ca_root_prod_cert",
":drm_ca_root_test_cert",
":oem_ca_root_dev_der",
":oem_ca_root_prod_der",
],
hdrs = ["root_certificates.h"],
)
genrule(
name = "drm_ca_root_test_cert",
srcs = ["drm_ca_root_test.cert"],
outs = ["drm_ca_root_test_cert.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "drm_ca_root_dev_cert",
srcs = ["drm_ca_root_dev.cert"],
outs = ["drm_ca_root_dev_cert.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "drm_ca_root_prod_cert",
srcs = ["drm_ca_root_prod.cert"],
outs = ["drm_ca_root_prod_cert.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "oem_ca_root_dev_der",
srcs = ["oem_ca_root_dev_cert.der"],
outs = ["oem_ca_root_dev_cert.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "oem_ca_root_prod_der",
srcs = ["oem_ca_root_prod_cert.der"],
outs = ["oem_ca_root_prod_cert.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
cc_library(
name = "test_certificates",
srcs = [
"test_certificates.cc",
":backwards_chain_der",
":expired_2000_chain_der",
":invalid_chain_der",
":single_cert_chain_der",
":sysid_2001_chain_der",
":sysid_2001_public_key_der",
],
hdrs = ["test_certificates.h"],
)
genrule(
name = "single_cert_chain_der",
srcs = ["single-cert-chain.der"],
outs = ["single_cert_chain.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "sysid_2001_chain_der",
srcs = ["sysid-2001-chain.der"],
outs = ["sysid_2001_chain.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "sysid_2001_public_key_der",
srcs = ["sysid-2001-public-key.der"],
outs = ["sysid_2001_public_key.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "expired_2000_chain_der",
srcs = ["expired-2000-chain.der"],
outs = ["expired_2000_chain.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "backwards_chain_der",
srcs = ["backwards-chain.der"],
outs = ["backwards_chain.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)
genrule(
name = "invalid_chain_der",
srcs = ["invalid_chain.der"],
outs = ["invalid_chain.h"],
cmd = "cd $$(dirname $<) && xxd -i $$(basename $<) $$OLDPWD/$@",
)

View File

@@ -0,0 +1,38 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/certificates/root_certificates.h"
#include "provisioning_sdk/internal/certificates/drm_ca_root_dev_cert.h"
#include "provisioning_sdk/internal/certificates/drm_ca_root_prod_cert.h"
#include "provisioning_sdk/internal/certificates/drm_ca_root_test_cert.h"
#include "provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.h"
#include "provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.h"
namespace widevine {
RootCertificates::RootCertificates()
: drm_root_test_certificate_(
drm_ca_root_test_cert,
drm_ca_root_test_cert + drm_ca_root_test_cert_len),
drm_root_dev_certificate_(
drm_ca_root_dev_cert,
drm_ca_root_dev_cert + drm_ca_root_dev_cert_len),
drm_root_prod_certificate_(
drm_ca_root_prod_cert,
drm_ca_root_prod_cert + drm_ca_root_prod_cert_len),
oem_root_dev_certificate_(
oem_ca_root_dev_cert_der,
oem_ca_root_dev_cert_der + oem_ca_root_dev_cert_der_len),
oem_root_prod_certificate_(
oem_ca_root_prod_cert_der,
oem_ca_root_prod_cert_der + oem_ca_root_prod_cert_der_len) {}
RootCertificates::~RootCertificates() {}
} // namespace widevine

View File

@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_
#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_
#include <string>
namespace widevine {
// This class contains DRM and OEM root certificates.
class RootCertificates {
public:
RootCertificates();
~RootCertificates();
const std::string& drm_root_test_certificate() const {
return drm_root_test_certificate_;
}
const std::string& drm_root_dev_certificate() const {
return drm_root_dev_certificate_;
}
const std::string& drm_root_prod_certificate() const {
return drm_root_prod_certificate_;
}
const std::string& oem_root_dev_certificate() const {
return oem_root_dev_certificate_;
}
const std::string& oem_root_prod_certificate() const {
return oem_root_prod_certificate_;
}
private:
RootCertificates(const RootCertificates&) = delete;
RootCertificates& operator=(const RootCertificates&) = delete;
std::string drm_root_test_certificate_;
std::string drm_root_dev_certificate_;
std::string drm_root_prod_certificate_;
std::string oem_root_dev_certificate_;
std::string oem_root_prod_certificate_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_ROOT_CERTIFICATES_H_

View File

@@ -0,0 +1,40 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/certificates/test_certificates.h"
#include "provisioning_sdk/internal/certificates/backwards_chain.h"
#include "provisioning_sdk/internal/certificates/expired_2000_chain.h"
#include "provisioning_sdk/internal/certificates/invalid_chain.h"
#include "provisioning_sdk/internal/certificates/single_cert_chain.h"
#include "provisioning_sdk/internal/certificates/sysid_2001_chain.h"
#include "provisioning_sdk/internal/certificates/sysid_2001_public_key.h"
namespace widevine {
TestCertificates::TestCertificates()
: single_certificate_chain_der_(
single_cert_chain_der,
single_cert_chain_der + single_cert_chain_der_len),
valid_certificate_chain_der_(
sysid_2001_chain_der,
sysid_2001_chain_der + sysid_2001_chain_der_len),
valid_certificate_public_key_der_(
sysid_2001_public_key_der,
sysid_2001_public_key_der + sysid_2001_public_key_der_len),
expired_certificate_chain_der_(
expired_2000_chain_der,
expired_2000_chain_der + expired_2000_chain_der_len),
backwards_certificate_chain_der_(
backwards_chain_der, backwards_chain_der + backwards_chain_der_len),
invalid_certificate_chain_der_(
invalid_chain_der, invalid_chain_der + invalid_chain_der_len) {}
TestCertificates::~TestCertificates() {}
} // namespace widevine

View File

@@ -0,0 +1,63 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_
#define PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_
#include <string>
namespace widevine {
// This class contains DER-encoded test certificates. The keys for these
// certificates came from common/rsa_test_keys.h.
class TestCertificates {
public:
TestCertificates();
~TestCertificates();
const std::string& single_certificate_chain_der() const {
return single_certificate_chain_der_;
}
const std::string& valid_certificate_chain_der() const {
return valid_certificate_chain_der_;
}
const std::string& valid_certificate_public_key_der() const {
return valid_certificate_public_key_der_;
}
const std::string& expired_certificate_chain_der() const {
return expired_certificate_chain_der_;
}
const std::string& backwards_certificate_chain_der() const {
return backwards_certificate_chain_der_;
}
const std::string& invalid_certificate_chain_der() const {
return invalid_certificate_chain_der_;
}
private:
TestCertificates(const TestCertificates&) = delete;
TestCertificates& operator=(const TestCertificates&) = delete;
std::string single_certificate_chain_der_;
// leaf + intermediate certificates.
std::string valid_certificate_chain_der_;
std::string valid_certificate_public_key_der_;
std::string expired_certificate_chain_der_;
// intermediate + leaf certificates.
std::string backwards_certificate_chain_der_;
std::string invalid_certificate_chain_der_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_INTERNAL_CERTIFICATES_TEST_CERTIFICATES_H_

View File

@@ -0,0 +1,210 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/oem_device_cert.h"
#include <string.h>
#include "glog/logging.h"
#include "openssl/asn1.h"
#include "openssl/x509.h"
#include "openssl/x509v3.h"
#include "provisioning_sdk/internal/certificates/root_certificates.h"
namespace widevine {
namespace {
const int kExtensionOidSize = 64;
const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1";
bool ExtractPublicKey(X509* x509, std::unique_ptr<RsaPublicKey>* public_key) {
DCHECK(x509);
ScopedPKEY pkey(X509_get_pubkey(x509));
if (!pkey) {
LOG(WARNING) << "X509_get_pubkey failed.";
return false;
}
public_key->reset(new RsaPublicKey(EVP_PKEY_get1_RSA(pkey.get())));
return true;
}
bool ExtractSystemId(X509* x509, uint32_t* system_id) {
DCHECK(x509);
const int num_of_extensions = X509_get_ext_count(x509);
for (int i = 0; i < num_of_extensions; ++i) {
X509_EXTENSION* extension = X509_get_ext(x509, i);
if (!extension) {
LOG(WARNING) << "X509_get_ext failed.";
return false;
}
ASN1_OBJECT* extension_object = X509_EXTENSION_get_object(extension);
if (!extension_object) {
LOG(WARNING) << "X509_EXTENSION_get_object failed.";
return false;
}
char extension_name[kExtensionOidSize + 1];
OBJ_obj2txt(extension_name, kExtensionOidSize, extension_object, 1);
if (strcmp(extension_name, kWidevineSystemIdExtensionOid)) continue;
ASN1_OCTET_STRING* octet_str = X509_EXTENSION_get_data(extension);
if (!octet_str) {
LOG(WARNING) << "X509_EXTENSION_get_data failed.";
return false;
}
const unsigned char* data = octet_str->data;
if (!data) {
LOG(WARNING) << "null data in octet string.";
return false;
}
ScopedAsn1Integer asn1_integer(
d2i_ASN1_INTEGER(nullptr, &data, octet_str->length));
if (!asn1_integer) {
LOG(WARNING) << "d2i_ASN1_INTEGER failed.";
return false;
}
int64_t system_id_long = ASN1_INTEGER_get(asn1_integer.get());
if (system_id_long == -1) {
LOG(WARNING) << "ASN1_INTEGER_get failed.";
return false;
}
*system_id = system_id_long;
return true;
}
LOG(WARNING) << "Widevine system ID extension not found.";
return false;
}
bool ExtractSerialNumber(X509* x509, std::string* oem_ca_serial_number) {
ASN1_INTEGER* serial = X509_get_serialNumber(x509);
if (!serial) {
LOG(WARNING) << "X509_get_serialNumber failed.";
return false;
}
uint8_t* buffer = ASN1_STRING_data(serial);
if (!buffer) {
LOG(WARNING) << "ASN1_STRING_data failed.";
return false;
}
int length = ASN1_STRING_length(serial);
if (length <= 0) {
LOG(WARNING) << "ASN1_STRING_length returns " << length;
}
oem_ca_serial_number->assign(reinterpret_cast<char*>(buffer), length);
return true;
}
} // namespace
OemDeviceCert::OemDeviceCert() {}
OemDeviceCert::~OemDeviceCert() {}
bool OemDeviceCert::Initialize(CertificateType certificate_type) {
RootCertificates root_certificates;
switch (certificate_type) {
case kCertTesting:
case kCertDevelopment:
return Initialize(root_certificates.oem_root_dev_certificate());
case kCertProduction:
return Initialize(root_certificates.oem_root_prod_certificate());
default:
LOG(WARNING) << "Invalid certificate type " << certificate_type;
return false;
}
}
bool OemDeviceCert::VerifyCertificateChain(
const std::string& certificate_chain,
std::unique_ptr<RsaPublicKey>* leaf_public_key, uint32_t* system_id,
std::string* oem_ca_serial_number) const {
if (certificate_chain.empty()) {
LOG(WARNING) << "Empty certificate.";
return false;
}
ScopedX509Stack x509_stack(sk_X509_new_null());
if (!x509_stack) {
LOG(WARNING) << "sk_X509_new_null returned NULL.";
return false;
}
CBS pkcs7;
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(certificate_chain.data()),
certificate_chain.size());
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
LOG(WARNING) << "PKCS7_get_certificates failed.";
return false;
}
if (sk_X509_num(x509_stack.get()) != 2) {
LOG(WARNING) << "Expecting one and only one intermediate certificate.";
return false;
}
X509* leaf_certificate = sk_X509_value(x509_stack.get(), 0);
X509* intermediate_certificate = sk_X509_value(x509_stack.get(), 1);
DCHECK(leaf_certificate);
DCHECK(intermediate_certificate);
// Verify the cert.
// TODO(user): Implement some form of caching mechanism so that we do not
// have to verify the same intermediate certs over and over.
ScopedX509StoreCtx context(X509_STORE_CTX_new());
if (!context) {
LOG(WARNING) << "X509_STORE_CTX_new returned NULL.";
return false;
}
if (!X509_STORE_CTX_init(context.get(), store_.get(), leaf_certificate,
x509_stack.get())) {
LOG(WARNING) << "X509_STORE_CTX_init failed.";
return false;
}
if (X509_verify_cert(context.get()) != 1) {
LOG(WARNING) << "Verification error: "
<< X509_verify_cert_error_string(
X509_STORE_CTX_get_error(context.get()));
return false;
}
if (sk_X509_num(context->chain) < 3) {
LOG(WARNING) << "The leaf certificate should not be signed by the root "
"certificate directly.";
return false;
}
// Extract public key from the leaf certificate and system_id extension and
// oem serial number from the intermediate certificate.
return ExtractPublicKey(leaf_certificate, leaf_public_key) &&
ExtractSystemId(intermediate_certificate, system_id) &&
ExtractSerialNumber(intermediate_certificate, oem_ca_serial_number);
}
bool OemDeviceCert::Initialize(const std::string& serialized_root_certificate) {
if (serialized_root_certificate.empty()) {
LOG(WARNING) << "Empty root certificate.";
return false;
}
store_.reset(X509_STORE_new());
if (!store_) {
LOG(WARNING) << "X509_STORE_new returned NULL";
return false;
}
const char* data = serialized_root_certificate.data();
ScopedX509 root_certificate(d2i_X509(nullptr,
reinterpret_cast<const uint8_t**>(&data),
serialized_root_certificate.size()));
if (!root_certificate) {
LOG(WARNING) << "d2i_X509 returned NULL.";
return false;
}
if (!X509_STORE_add_cert(store_.get(), root_certificate.get())) {
LOG(WARNING) << "X509_STORE_add_cert failed.";
return false;
}
return true;
}
} // namespace widevine

View File

@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#ifndef PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_
#define PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_
#include <stdint.h>
#include <memory>
#include <string>
#include "common/openssl_util.h"
#include "common/rsa_key.h"
#include "provisioning_sdk/public/certificate_type.h"
namespace widevine {
// Implements a class to handle OEM certificate: verifies the validity of the
// certificate and extracts leaf public key and system id.
class OemDeviceCert {
public:
OemDeviceCert();
virtual ~OemDeviceCert();
// Initialize with root certificate.
bool Initialize(CertificateType certificate_type);
// Verify the given certificate chain (in DER encoded pkcs7 format), which
// includes the leaf certificate (a device unique certificate containing the
// device public OEM key) and the intermediate certificate (OEM model
// intermediate CA certificate for a specific device make + model), and
// extract public key from the leaf certificate and system id extension and
// oem ca serial number from the intermediate certificate.
virtual bool VerifyCertificateChain(
const std::string& certificate_chain,
std::unique_ptr<RsaPublicKey>* leaf_public_key, uint32_t* system_id,
std::string* oem_ca_serial_number) const;
private:
OemDeviceCert(const OemDeviceCert&) = delete;
OemDeviceCert& operator=(const OemDeviceCert&) = delete;
// Internal implementation of Initialize function.
bool Initialize(const std::string& serialized_root_certificate);
ScopedX509Store store_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_INTERNAL_OEM_DEVICE_CERT_H_

View File

@@ -0,0 +1,95 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/oem_device_cert.h"
#include "gtest/gtest.h"
#include "provisioning_sdk/internal/certificates/test_certificates.h"
namespace widevine {
class OemDeviceCertTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(oem_device_cert_.Initialize(kCertTesting));
}
OemDeviceCert oem_device_cert_;
TestCertificates test_certificates_;
};
TEST_F(OemDeviceCertTest, EmptyCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
"", &leaf_public_key, &system_id, &oem_ca_serial_number));
}
TEST_F(OemDeviceCertTest, InvalidCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
"invalid certificte chain", &leaf_public_key, &system_id,
&oem_ca_serial_number));
}
TEST_F(OemDeviceCertTest, OnlyOneCertificateInCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
EXPECT_FALSE(oem_device_cert_.VerifyCertificateChain(
test_certificates_.single_certificate_chain_der(), &leaf_public_key,
&system_id, &oem_ca_serial_number));
}
TEST_F(OemDeviceCertTest, ValidCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
ASSERT_TRUE(oem_device_cert_.VerifyCertificateChain(
test_certificates_.valid_certificate_chain_der(), &leaf_public_key,
&system_id, &oem_ca_serial_number));
std::unique_ptr<RsaPublicKey> public_key(RsaPublicKey::Create(
test_certificates_.valid_certificate_public_key_der()));
ASSERT_TRUE(public_key);
EXPECT_TRUE(leaf_public_key->MatchesPublicKey(*public_key));
EXPECT_EQ(2001u, system_id);
EXPECT_EQ("\x1", oem_ca_serial_number);
}
TEST_F(OemDeviceCertTest, ExpiredCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
test_certificates_.expired_certificate_chain_der(), &leaf_public_key,
&system_id, &oem_ca_serial_number));
}
TEST_F(OemDeviceCertTest, OutOfOrderCertificateChain) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
test_certificates_.backwards_certificate_chain_der(), &leaf_public_key,
&system_id, &oem_ca_serial_number));
}
TEST_F(OemDeviceCertTest, CertificateChainNotSignedByRoot) {
std::unique_ptr<RsaPublicKey> leaf_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
ASSERT_FALSE(oem_device_cert_.VerifyCertificateChain(
test_certificates_.invalid_certificate_chain_der(), &leaf_public_key,
&system_id, &oem_ca_serial_number));
}
} // namespace widevine

View File

@@ -0,0 +1,464 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
#include <stddef.h>
#include <time.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "glog/logging.h"
#include "common/random_util.h"
#include "common/rsa_key.h"
#include "provisioning_sdk/internal/certificates/root_certificates.h"
#include "provisioning_sdk/public/provisioning_status.h"
#define LOG_WITH_PROTO(message, proto) \
LOG(WARNING) << (message) << " [proto: " << (proto).ShortDebugString() << "]"
namespace widevine {
namespace {
// Verify that |certificate| is signed by |public_key|. If |public_key| is null,
// |certificate| should be self signed.
bool VerifyAndExtractCertificate(const RsaPublicKey* public_key,
const std::string& certificate,
SignedDrmDeviceCertificate* signed_drm_cert,
DrmDeviceCertificate* drm_cert) {
DCHECK(signed_drm_cert);
DCHECK(drm_cert);
if (!signed_drm_cert->ParseFromString(certificate)) {
LOG(WARNING) << "Failed to parse SignedDrmDeviceCertificate.";
return false;
}
if (signed_drm_cert->drm_certificate().empty()) {
LOG_WITH_PROTO("Missing drm_certificate", *signed_drm_cert);
return false;
}
if (signed_drm_cert->signature().empty()) {
LOG_WITH_PROTO("Missing signature", *signed_drm_cert);
return false;
}
if (!drm_cert->ParseFromString(signed_drm_cert->drm_certificate())) {
LOG_WITH_PROTO("Failed to parse DrmDeviceCertificate", *signed_drm_cert);
return false;
}
if (drm_cert->public_key().empty()) {
LOG_WITH_PROTO("Missing public_key", *drm_cert);
return false;
}
std::unique_ptr<RsaPublicKey> local_public_key;
if (!public_key) {
local_public_key.reset(RsaPublicKey::Create(drm_cert->public_key()));
if (!local_public_key) {
LOG_WITH_PROTO("Invalid root public key", *drm_cert);
return false;
}
public_key = local_public_key.get();
}
if (!public_key->VerifySignature(signed_drm_cert->drm_certificate(),
signed_drm_cert->signature())) {
LOG_WITH_PROTO("Signature verification failed", *signed_drm_cert);
return false;
}
return true;
}
bool GenerateCertificate(DrmDeviceCertificate::CertificateType type,
uint32_t system_id,
const std::string& provider_id,
const std::string& serial_number,
const std::string& public_key,
const RsaPrivateKey& signing_key,
const SignedDrmDeviceCertificate& signer,
std::string* certificate) {
DCHECK(type == DrmDeviceCertificate::DRM_INTERMEDIATE ||
type == DrmDeviceCertificate::DRM_USER_DEVICE);
if (serial_number.empty()) {
LOG(WARNING) << "Require an non-empty serial number.";
return false;
}
DrmDeviceCertificate drm_cert;
drm_cert.set_type(type);
drm_cert.set_system_id(system_id);
if (!provider_id.empty()) drm_cert.set_provider_id(provider_id);
drm_cert.set_serial_number(serial_number);
drm_cert.set_creation_time_seconds(time(nullptr));
drm_cert.set_public_key(public_key);
SignedDrmDeviceCertificate signed_cert;
if (!drm_cert.SerializeToString(
signed_cert.mutable_drm_certificate())) {
LOG(WARNING) << "Error serializing DrmDeviceCertificate.";
return false;
}
if (!signing_key.GenerateSignature(signed_cert.drm_certificate(),
signed_cert.mutable_signature())) {
LOG(WARNING) << "Failed to generate signature for DrmDeviceCertificate.";
return false;
}
*signed_cert.mutable_signer() = signer;
if (!signed_cert.SerializeToString(certificate)) {
LOG(WARNING) << "Failed to serialize SignedDrmDeviceCertificate to string.";
return false;
}
return true;
}
// Compares oem serial number, which is a 16-byte big number.
bool IsSerialNumberEq(const std::string& a, const std::string& b) {
// An empty serial number indicates that it does not need to be matched.
if (a.empty() || b.empty()) return true;
int a_index = a.size() - 1;
int b_index = b.size() - 1;
// Matching a and b backwards.
for (; a_index >= 0 && b_index >= 0; --a_index, --b_index)
if (a[a_index] != b[b_index]) return false;
// The remaining characters should be 0.
for (; a_index >= 0; --a_index)
if (a[a_index] != 0) return false;
for (; b_index >= 0; --b_index)
if (b[b_index] != 0) return false;
return true;
}
} // namespace
ProvisioningEngineImpl::ProvisioningEngineImpl()
: rsa_key_factory_(new RsaKeyFactory) {}
ProvisioningEngineImpl::~ProvisioningEngineImpl() {}
ProvisioningStatus ProvisioningEngineImpl::Initialize(
CertificateType certificate_type, const std::string& drm_service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_phassphrase,
const std::string& provisioning_drm_certificate,
const std::string& provisioning_private_key,
const std::string& provisioning_private_key_phassphrase,
const std::string& secret_spoid_sauce) {
if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE;
SignedDrmDeviceCertificate signed_drm_cert;
DrmDeviceCertificate drm_cert;
if (!VerifyAndExtractCertificate(root_public_key_.get(),
drm_service_certificate, &signed_drm_cert,
&drm_cert)) {
return INVALID_SERVICE_DRM_CERTIFICATE;
}
if (drm_cert.type() != DrmDeviceCertificate::SERVICE) {
LOG(WARNING) << "Expecting SERVICE certificate.";
return INVALID_SERVICE_DRM_CERTIFICATE;
}
service_public_key_ =
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE;
service_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
service_private_key, service_private_key_phassphrase);
if (!service_private_key_) return INVALID_SERVICE_PRIVATE_KEY;
if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) {
LOG(WARNING) << "Services public key and private key do not match.";
return INVALID_SERVICE_PRIVATE_KEY;
}
if (!VerifyAndExtractCertificate(root_public_key_.get(),
provisioning_drm_certificate,
&signed_provisioning_cert_, &drm_cert)) {
return INVALID_PROVISIONER_DRM_CERTIFICATE;
}
if (drm_cert.type() != DrmDeviceCertificate::ROOT &&
drm_cert.type() != DrmDeviceCertificate::PROVISIONER) {
LOG(WARNING) << "Expecting ROOT or PROVISIONER certificate.";
return INVALID_PROVISIONER_DRM_CERTIFICATE;
}
provisioning_public_key_ =
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE;
provisioning_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey(
provisioning_private_key, provisioning_private_key_phassphrase);
if (!provisioning_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY;
if (!provisioning_public_key_->MatchesPrivateKey(
*provisioning_private_key_)) {
LOG(WARNING) << "Provisioning public key and private key do not match.";
return INVALID_PROVISIONER_PRIVATE_KEY;
}
if (secret_spoid_sauce.empty()) {
LOG(WARNING) << "SPOID secret sauce is empty!";
return INVALID_SPOID_SAUCE;
}
secret_spoid_sauce_ = secret_spoid_sauce;
if (!oem_device_cert_.Initialize(certificate_type)) return INTERNAL_ERROR;
return OK;
}
ProvisioningStatus ProvisioningEngineImpl::SetCertificateStatusList(
const std::string& certificate_status_list, uint32_t expiration_period_seconds) {
if (certificate_status_list.empty()) {
LOG(WARNING) << "Empty certificate_status_list.";
return INVALID_STATUS_LIST;
}
SignedCertificateStatusList signed_cert_status_list;
if (!signed_cert_status_list.ParseFromString(certificate_status_list)) {
LOG(WARNING) << "Error parsing SignedCertificateStatusList.";
return INVALID_STATUS_LIST;
}
if (!root_public_key_->VerifySignature(
signed_cert_status_list.certificate_status_list(),
signed_cert_status_list.signature())) {
LOG_WITH_PROTO("Signature verification failed", signed_cert_status_list);
return INVALID_STATUS_LIST;
}
DeviceCertificateStatusList cert_status_list;
if (!cert_status_list.ParseFromString(
signed_cert_status_list.certificate_status_list())) {
LOG_WITH_PROTO("Error parsing DeviceCertificateStatusList",
signed_cert_status_list);
return INVALID_STATUS_LIST;
}
WriterMutexLock writer_lock(&mutex_);
if (expiration_period_seconds == 0) {
certificate_expiration_seconds_utc_ = std::numeric_limits<uint32_t>::max();
} else {
certificate_expiration_seconds_utc_ =
cert_status_list.creation_time_seconds() + expiration_period_seconds;
}
certificate_status_map_.clear();
for (DeviceCertificateStatus& cert_status :
*cert_status_list.mutable_certificate_status()) {
const uint32_t system_id = cert_status.device_info().system_id();
certificate_status_map_[system_id].Swap(&cert_status);
}
// Remove items that are no longer valid.
for (auto it = intermediate_certs_info_.begin();
it != intermediate_certs_info_.end();) {
auto certificate_status_it = certificate_status_map_.find(it->first);
if (certificate_status_it == certificate_status_map_.end())
intermediate_certs_info_.erase(it++);
else
++it;
}
// Set / Update device info.
for (const auto& cert_status : certificate_status_map_) {
auto device_info = std::make_shared<ProvisionedDeviceInfo>();
*device_info = cert_status.second.device_info();
intermediate_certs_info_[cert_status.first].device_info = device_info;
// intermediate certificate and private key are not changed if exists.
}
return OK;
}
ProvisioningStatus ProvisioningEngineImpl::GenerateDrmIntermediateCertificate(
uint32_t system_id, const std::string& public_key, std::string* certificate) const {
auto intermediate_public_key =
rsa_key_factory_->CreateFromPkcs1PublicKey(public_key);
if (!intermediate_public_key) return INVALID_INTERMEDIATE_PUBLIC_KEY;
const size_t kCertificateSerialNumberSize = 16;
std::string serial_number;
if (!RandomBytes(kCertificateSerialNumberSize, &serial_number)) {
LOG(WARNING) << "Failed to generate serial_number.";
return INTERNAL_ERROR;
}
if (!GenerateCertificate(DrmDeviceCertificate::DRM_INTERMEDIATE,
system_id, std::string(), serial_number, public_key,
*provisioning_private_key_,
signed_provisioning_cert_, certificate)) {
return INTERNAL_ERROR;
}
return OK;
}
ProvisioningStatus ProvisioningEngineImpl::AddDrmIntermediateCertificate(
const std::string& intermediate_cert, const std::string& cert_private_key,
const std::string& cert_private_key_passphrase) {
SignedDrmDeviceCertificate intermediate_signed_cert;
DrmDeviceCertificate intermediate_drm_cert;
if (!VerifyAndExtractCertificate(provisioning_public_key_.get(),
intermediate_cert, &intermediate_signed_cert,
&intermediate_drm_cert)) {
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
}
if (intermediate_drm_cert.type() !=
DrmDeviceCertificate::DRM_INTERMEDIATE) {
LOG_WITH_PROTO("Invalid device certificate type", intermediate_drm_cert);
return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
}
if (!intermediate_drm_cert.has_system_id()) {
LOG_WITH_PROTO("Missing system_id", intermediate_drm_cert);
return UNKNOWN_SYSTEM_ID;
}
const std::string empty_oem_ca_serial_number;
ProvisioningStatus status = CheckDeviceStatus(
intermediate_drm_cert.system_id(), empty_oem_ca_serial_number);
if (status != OK) return status;
auto intermediate_public_key = rsa_key_factory_->CreateFromPkcs1PublicKey(
intermediate_drm_cert.public_key());
if (!intermediate_public_key) return INVALID_INTERMEDIATE_DRM_CERTIFICATE;
std::unique_ptr<RsaPrivateKey> intermediate_private_key =
rsa_key_factory_->CreateFromPkcs8PrivateKey(cert_private_key,
cert_private_key_passphrase);
if (!intermediate_private_key) return INVALID_INTERMEDIATE_PRIVATE_KEY;
if (!intermediate_public_key->MatchesPrivateKey(*intermediate_private_key)) {
LOG(WARNING) << "Intermediate public key and private key do not match.";
return INVALID_INTERMEDIATE_PRIVATE_KEY;
}
WriterMutexLock writer_lock(&mutex_);
auto& certificate_info =
intermediate_certs_info_[intermediate_drm_cert.system_id()];
certificate_info.signed_drm_certificate.Swap(&intermediate_signed_cert);
certificate_info.private_key = std::move(intermediate_private_key);
return OK;
}
ProvisioningStatus ProvisioningEngineImpl::GenerateDeviceDrmCertificate(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& public_key, const std::string& certificate_serial_number,
std::string* certificate) const {
// |oem_ca_serial_number| could be empty if it is called directly from
// ProvisioningEngine::GenerateDeviceDrmCertificate.
DCHECK(!certificate_serial_number.empty());
return GenerateProviderDeviceDrmCertificate(
system_id, oem_ca_serial_number, std::string(), public_key,
certificate_serial_number, certificate);
}
ProvisioningStatus ProvisioningEngineImpl::GenerateProviderDeviceDrmCertificate(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& provider_id, const std::string& public_key,
const std::string& certificate_serial_number, std::string* certificate) const {
// |oem_ca_serial_number| could be empty if it is called directly from
// ProvisioningEngine::GenerateDeviceDrmCertificate.
DCHECK(!certificate_serial_number.empty());
ProvisioningStatus status =
CheckDeviceStatus(system_id, oem_ca_serial_number);
if (status != OK) return status;
std::shared_ptr<RsaPrivateKey> intermediate_private_key;
const SignedDrmDeviceCertificate* intermediate_cert = nullptr;
{
ReaderMutexLock reader_lock(&mutex_);
auto info_it = intermediate_certs_info_.find(system_id);
if (info_it == intermediate_certs_info_.end() ||
!info_it->second.private_key) {
LOG(WARNING) << "Cannot find the intermediate certificate for system: "
<< system_id;
return MISSING_DRM_INTERMEDIATE_CERT;
}
intermediate_private_key = info_it->second.private_key;
intermediate_cert = &info_it->second.signed_drm_certificate;
}
if (!GenerateCertificate(DrmDeviceCertificate::DRM_USER_DEVICE,
system_id, provider_id, certificate_serial_number,
public_key, *intermediate_private_key,
*intermediate_cert, certificate)) {
return INTERNAL_ERROR;
}
return OK;
}
std::shared_ptr<ProvisionedDeviceInfo> ProvisioningEngineImpl::GetDeviceInfo(
uint32_t system_id) const {
ReaderMutexLock reader_lock(&mutex_);
auto info_it = intermediate_certs_info_.find(system_id);
if (info_it == intermediate_certs_info_.end()) {
LOG(WARNING) << "Cannot find the system id in device certificate list: "
<< system_id;
return std::shared_ptr<ProvisionedDeviceInfo>();
}
return info_it->second.device_info;
}
bool ProvisioningEngineImpl::LoadDrmRootPublicKey(
CertificateType certificate_type) {
const std::string* root_cert_string = nullptr;
RootCertificates root_certificates;
switch (certificate_type) {
case kCertTesting:
root_cert_string = &root_certificates.drm_root_test_certificate();
break;
case kCertDevelopment:
root_cert_string = &root_certificates.drm_root_dev_certificate();
break;
case kCertProduction:
root_cert_string = &root_certificates.drm_root_prod_certificate();
break;
default:
LOG(WARNING) << "Invalid certificate type " << certificate_type;
return false;
}
SignedDrmDeviceCertificate signed_root_cert;
DrmDeviceCertificate root_cert;
if (!VerifyAndExtractCertificate(nullptr /* self signed */, *root_cert_string,
&signed_root_cert, &root_cert)) {
LOG(WARNING) << "Failed to extract root certificate.";
return false;
}
if (root_cert.type() != DrmDeviceCertificate::ROOT) {
LOG(WARNING) << "Expecting ROOT certificate.";
return false;
}
root_public_key_ =
rsa_key_factory_->CreateFromPkcs1PublicKey(root_cert.public_key());
CHECK(root_public_key_);
return true;
}
ProvisioningStatus ProvisioningEngineImpl::CheckDeviceStatus(
uint32_t system_id, const std::string& oem_ca_serial_number) const {
ReaderMutexLock reader_lock(&mutex_);
if (certificate_expiration_seconds_utc_ < time(nullptr))
return STATUS_LIST_EXPIRED;
auto certificate_status_it = certificate_status_map_.find(system_id);
if (certificate_status_it == certificate_status_map_.end()) {
LOG(WARNING) << "Cannot find the system id in device certificate list: "
<< system_id;
return UNKNOWN_SYSTEM_ID;
}
if (!IsSerialNumberEq(certificate_status_it->second.oem_serial_number(),
oem_ca_serial_number)) {
LOG(WARNING) << "Provided serial number does not match with stored serial "
"number. It may come from a revoked device. System Id: "
<< system_id;
return DEVICE_REVOKED;
}
if (certificate_status_it->second.status() !=
DeviceCertificateStatus::VALID) {
LOG(WARNING) << "Device revoked " << system_id;
return DEVICE_REVOKED;
}
return OK;
}
} // namespace widevine

View File

@@ -0,0 +1,189 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// ProvisioningEngine internal implementation. This class is thread-safe.
#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_
#define PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "base/mutex.h"
#include "base/thread_annotations.h"
#include "common/rsa_key.h"
#include "provisioning_sdk/internal/oem_device_cert.h"
#include "provisioning_sdk/public/certificate_type.h"
#include "provisioning_sdk/public/provisioning_status.h"
#include "protos/public/device_certificate.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
#include "protos/public/signed_device_certificate.pb.h"
namespace widevine {
class ProvisioningSession;
class ProvisioningEngineImpl {
public:
ProvisioningEngineImpl();
virtual ~ProvisioningEngineImpl();
// Initializes the provisioning engine with required credentials.
// * |certificate_type| indicates which type of certificate chains will be
// used for device provisioning via this engine.
// * |drm_service_certificate| is a Google-generated certificate used to
// authenticate the service provider for purposes of user privacy.
// * |service_private_key| is the encrypted PKCS#8 private RSA key
// corresponding to the service certificate.
// * |service_private_key_passphrase| is the password required to decrypt
// |service_private_key|, if any.
// * |provisioning_drm_certificate| is a Google-generated certificate used to
// sign intermediate DRM certificates.
// * |provisioning_private_key| is the encrypted PKCS#8 private RSA key
// corresponding to the provisioning certificate.
// * |provisioning_private_key_passphrase| is the password required to
// decrypt |provisioning_private_key|, if any.
// * |secret_spoid_sauce| is a stable secret used as a factor in the
// derivation of Stable Per-Origin IDentifiers.
// * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus Initialize(
CertificateType certificate_type,
const std::string& drm_service_certificate,
const std::string& service_private_key,
const std::string& service_private_key_passphrase,
const std::string& provisioning_drm_certificate,
const std::string& provisioning_private_key,
const std::string& provisioning_private_key_passphrase,
const std::string& secret_spoid_sauce);
// Set the certificate status list for this engine.
// * |certificate_status_list| is a certificate status list generated by the
// Widevine Provisioning Service.
// * |expiration_period| is the number of seconds until the
// |certificate_status_list| expires after its creation time
// (creation_time_seconds). Zero means it will never expire.
// * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus SetCertificateStatusList(
const std::string& certificate_status_list,
uint32_t expiration_period_seconds);
// Generate an intermediate DRM certificate.
// * |system_id| is the Widevine system ID for the type of device.
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
// be embedded in the generated certificate.
// * |certificate| will contain the new intermediate certificate, upon
// successful return.
// * Returns OK on success, or an appropriate error status code otherwise.
// NOTE: The generated certificate and associated private key should be stored
// securely to be reused. They should also be propagated to all
// engines, including this one, by invoking
// |AddIntermediatedrmcertificate| on all active ProvisioningEngine(s).
ProvisioningStatus GenerateDrmIntermediateCertificate(
uint32_t system_id, const std::string& public_key, std::string* certificate) const;
// Add an intermediate DRM certificate to the provisioning engine. This is
// usually done once for each supported device type.
// * |intermediate_cert| is the intermediate DRM certificate to be added.
// * |cert_private_key| is a PKCS#8 private key corresponding to
// |intermediate_cert|.
// * |cert_private_key_passphrase| is the passphrase for cert_private_key,
// if any.
// * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus AddDrmIntermediateCertificate(
const std::string& intermediate_cert, const std::string& cert_private_key,
const std::string& cert_private_key_passphrase);
// Generate a new device DRM certificate to be provisioned.
// * |system_id| is the Widevine system ID for the type of device being
// provisioned.
// * |oem_ca_serial_number| is the oem serial number for the type of device
// being provisioned. ProvisioningEngine uses |system_id| and
// |oem_ca_serial_number| to determine if the device is valid, e.g. whether
// it has been revoked. |oem_ca_serial_number| can be empty if we do not
// care about serial number matches or not.
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
// be embedded in the generated certificate.
// * |certificate_serial_number| is a binary std::string to be used as the
// generated DRM certificate serial number.
// * |certificate| will contain, upon successful return the generated
// certificate.
// * Returns OK on success, or an appropriate error status code otherwise.
// Virtual for mocking.
virtual ProvisioningStatus GenerateDeviceDrmCertificate(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& public_key, const std::string& certificate_serial_number,
std::string* certificate) const;
// Internal version of the method above. Allows specifying |provider_id|.
virtual ProvisioningStatus GenerateProviderDeviceDrmCertificate(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& provider_id, const std::string& public_key,
const std::string& certificate_serial_number, std::string* certificate) const;
// Get the device info for the given |system_id|.
std::shared_ptr<ProvisionedDeviceInfo> GetDeviceInfo(
uint32_t system_id) const;
// Returns the service private key.
const RsaPrivateKey* service_private_key() const {
return service_private_key_.get();
}
const OemDeviceCert& oem_device_cert() const { return oem_device_cert_; }
const std::string& secret_spoid_sauce() const { return secret_spoid_sauce_; }
private:
friend class ProvisioningEngineImplTest;
ProvisioningEngineImpl(const ProvisioningEngineImpl&) = delete;
ProvisioningEngineImpl& operator=(const ProvisioningEngineImpl&) = delete;
// Load DRM root public key with type |certificate_type|.
bool LoadDrmRootPublicKey(CertificateType certificate_type);
// Check device status.
// If |oem_ca_serial_number| is empty, we do not care whether serial number
// matches or not.
// * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus CheckDeviceStatus(
uint32_t system_id, const std::string& oem_ca_serial_number) const;
// Inject rsa_key_factory for testing.
void set_rsa_key_factory(std::unique_ptr<RsaKeyFactory> rsa_key_factory) {
rsa_key_factory_ = std::move(rsa_key_factory);
}
std::unique_ptr<RsaKeyFactory> rsa_key_factory_;
std::unique_ptr<RsaPublicKey> root_public_key_;
std::unique_ptr<RsaPublicKey> service_public_key_;
std::unique_ptr<RsaPrivateKey> service_private_key_;
SignedDrmDeviceCertificate signed_provisioning_cert_;
std::unique_ptr<RsaPublicKey> provisioning_public_key_;
std::unique_ptr<RsaPrivateKey> provisioning_private_key_;
std::string secret_spoid_sauce_;
OemDeviceCert oem_device_cert_;
mutable Mutex mutex_;
// POSIX time, in seconds, when the list would be expired.
uint32_t certificate_expiration_seconds_utc_ GUARDED_BY(mutex_) = 0;
// Maps with system_id as the key.
std::map<uint32_t, DeviceCertificateStatus> certificate_status_map_
GUARDED_BY(mutex_);
struct IntermediateCertificateInfo {
SignedDrmDeviceCertificate signed_drm_certificate;
std::shared_ptr<ProvisionedDeviceInfo> device_info;
std::shared_ptr<RsaPrivateKey> private_key;
};
std::map<uint32_t, IntermediateCertificateInfo> intermediate_certs_info_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_ENGINE_IMPL_H_

View File

@@ -0,0 +1,783 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
#include <time.h>
#include <memory>
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "common/mock_rsa_key.h"
#include "provisioning_sdk/public/certificate_type.h"
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::StrEq;
namespace {
const int kSystemId = 100;
const int kExpirationPeriodSeconds = 3600;
const int kInfiniteExpirationPeriodSeconds = 0;
const char kEmptyOemSerialNumber[] = "";
const char kSecretSauce[] = "Twas bryllyg, and ye slythy toves";
const char kCertificateSerialNumber[] = "certificate_serial_number";
const char kOemSerialNumber0[] = "oem_serial_number_0";
const char kOemSerialNumber1[] = "oem_serial_number_1";
const char kDevicePublicKey[] = "device_public_key";
const char kSignature[] = "mock_signature";
const char kIntermediatePrivateKey[] = "intermediate_private_key";
const char kIntermediatePrivateKeyPassphrase[] =
"intermediate_private_key_passphrase";
const char kIntermediatePublicKey[] = "intermediate_public_key";
const char kServicePrivateKey[] = "service_private_key";
const char kServicePrivateKeyPassphrase[] = "service_private_key_phassphrase";
const char kServiceDrmCertificate[] = "service_drm_certificate";
const char kProvisioningDrmCertificate[] = "provisioning_drm_certificate";
const char kProvisioningPrivateKey[] = "provisioning_private_key";
const char kProvisioningPrivateKeyPassphrase[] =
"provisioning_private_key_phassphrase";
const char kProvisioningPublicKey[] = "provisioning_public_key";
const char kProvisioningSignature[] = "provisioning_signature";
// A simple std::string concatenation function. This assumes i within [0, 9].
std::string StrCat(const std::string& in, int i) {
DCHECK_LE(i, 9);
DCHECK_GE(i, 0);
std::string out = in + "0";
out[out.size() - 1] += i;
return out;
}
} // namespace
namespace widevine {
class ProvisioningEngineImplTest : public ::testing::Test {
protected:
ProvisioningEngineImplTest() {
mock_rsa_key_factory_ = new MockRsaKeyFactory;
engine_impl_.set_rsa_key_factory(
std::unique_ptr<RsaKeyFactory>(mock_rsa_key_factory_));
}
ProvisioningStatus CheckDeviceStatus(uint32_t system_id,
const std::string& oem_ca_serial_number) {
return engine_impl_.CheckDeviceStatus(system_id, oem_ca_serial_number);
}
ProvisioningEngineImpl engine_impl_;
MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr;
};
TEST_F(ProvisioningEngineImplTest, InvalidCertType) {
CertificateType invalid_certificate = static_cast<CertificateType>(100);
EXPECT_EQ(
INVALID_CERTIFICATE_TYPE,
engine_impl_.Initialize(
invalid_certificate, kServiceDrmCertificate, kServicePrivateKey,
kServicePrivateKeyPassphrase, kProvisioningDrmCertificate,
kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase,
kSecretSauce));
}
class ProvisioningEngineImplServiceTest : public ProvisioningEngineImplTest {
protected:
void SetUp() override {
mock_root_public_key_ = new MockRsaPublicKey();
EXPECT_CALL(*mock_rsa_key_factory_, CreateFromPkcs1PublicKey(_))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_root_public_key_))));
service_cert_.set_public_key("service_public_key");
service_cert_.set_type(DrmDeviceCertificate::SERVICE);
signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString());
signed_service_cert_.set_signature("service_signature");
}
ProvisioningStatus Initialize(const std::string& service_drm_certificate) {
return engine_impl_.Initialize(
kCertTesting, service_drm_certificate, kServicePrivateKey,
kServicePrivateKeyPassphrase, kProvisioningDrmCertificate,
kProvisioningPrivateKey, kProvisioningPrivateKeyPassphrase,
kSecretSauce);
}
DrmDeviceCertificate service_cert_;
SignedDrmDeviceCertificate signed_service_cert_;
MockRsaPublicKey* mock_root_public_key_;
};
TEST_F(ProvisioningEngineImplServiceTest, Empty) {
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE, Initialize(""));
}
TEST_F(ProvisioningEngineImplServiceTest, MissingDeviceCert) {
signed_service_cert_.clear_drm_certificate();
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, MissingSignature) {
signed_service_cert_.clear_signature();
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, SignatureVerificationFailure) {
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(StrEq(service_cert_.SerializeAsString()),
"service_signature"))
.WillOnce(Return(false));
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, InvalidDeviceCertType) {
service_cert_.set_type(DrmDeviceCertificate::PROVISIONER);
signed_service_cert_.set_drm_certificate(service_cert_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(StrEq(service_cert_.SerializeAsString()),
"service_signature"))
.WillOnce(Return(true));
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, InvaidPublicKey) {
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey("service_public_key"))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(INVALID_SERVICE_DRM_CERTIFICATE,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, InvalidPrivateKey) {
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey("service_public_key"))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kServicePrivateKey,
kServicePrivateKeyPassphrase))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY,
Initialize(signed_service_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplServiceTest, MismatchPublicKeyPrivateKey) {
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
.WillOnce(Return(true));
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey("service_public_key"))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kServicePrivateKey,
kServicePrivateKeyPassphrase))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPrivateKey>(mock_rsa_private_key))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
.WillOnce(Return(false));
EXPECT_EQ(INVALID_SERVICE_PRIVATE_KEY,
Initialize(signed_service_cert_.SerializeAsString()));
}
class ProvisioningEngineImplProvTest
: public ProvisioningEngineImplServiceTest {
protected:
void SetUp() override {
ProvisioningEngineImplServiceTest::SetUp();
// Service certificate expectations.
EXPECT_CALL(*mock_root_public_key_, VerifySignature(_, "service_signature"))
.WillOnce(Return(true));
mock_service_public_key_ = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey("service_public_key"))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_service_public_key_))));
mock_service_private_key_ = new MockRsaPrivateKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kServicePrivateKey,
kServicePrivateKeyPassphrase))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(mock_service_private_key_))));
EXPECT_CALL(*mock_service_public_key_, MatchesPrivateKey(_))
.WillOnce(Return(true));
prov_cert_.set_public_key(kProvisioningPublicKey);
prov_cert_.set_type(DrmDeviceCertificate::PROVISIONER);
signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString());
signed_prov_cert_.set_signature(kProvisioningSignature);
}
ProvisioningStatus Initialize(const std::string& provisioning_drm_certificate) {
return engine_impl_.Initialize(
kCertTesting, signed_service_cert_.SerializeAsString(),
kServicePrivateKey, kServicePrivateKeyPassphrase,
provisioning_drm_certificate, kProvisioningPrivateKey,
kProvisioningPrivateKeyPassphrase, "spoid_secret_sauce");
}
DrmDeviceCertificate prov_cert_;
SignedDrmDeviceCertificate signed_prov_cert_;
MockRsaPublicKey* mock_service_public_key_ = nullptr;
MockRsaPrivateKey* mock_service_private_key_ = nullptr;
};
TEST_F(ProvisioningEngineImplProvTest, Empty) {
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE, Initialize(""));
}
TEST_F(ProvisioningEngineImplProvTest, MissingDeviceCert) {
signed_prov_cert_.clear_drm_certificate();
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, MissingSignature) {
signed_prov_cert_.clear_signature();
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, SignatureVerificationFailure) {
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(StrEq(prov_cert_.SerializeAsString()),
kProvisioningSignature))
.WillOnce(Return(false));
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, InvalidDeviceCertType) {
prov_cert_.set_type(DrmDeviceCertificate::SERVICE);
signed_prov_cert_.set_drm_certificate(prov_cert_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(StrEq(prov_cert_.SerializeAsString()),
kProvisioningSignature))
.WillOnce(Return(true));
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, InvaidPublicKey) {
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, kProvisioningSignature))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(INVALID_PROVISIONER_DRM_CERTIFICATE,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, InvalidPrivateKey) {
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, kProvisioningSignature))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
kProvisioningPrivateKeyPassphrase))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY,
Initialize(signed_prov_cert_.SerializeAsString()));
}
TEST_F(ProvisioningEngineImplProvTest, MismatchPublicKeyPrivateKey) {
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, kProvisioningSignature))
.WillOnce(Return(true));
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
MockRsaPrivateKey* mock_rsa_private_key = new MockRsaPrivateKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
kProvisioningPrivateKeyPassphrase))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPrivateKey>(mock_rsa_private_key))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
.WillOnce(Return(false));
EXPECT_EQ(INVALID_PROVISIONER_PRIVATE_KEY,
Initialize(signed_prov_cert_.SerializeAsString()));
}
class ProvisioningEngineImplGeneralTest
: public ProvisioningEngineImplProvTest {
protected:
void SetUp() override {
ProvisioningEngineImplProvTest::SetUp();
// Provisioning certificate expectations.
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, kProvisioningSignature))
.WillOnce(Return(true));
mock_prov_public_key_ = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kProvisioningPublicKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_prov_public_key_))));
mock_prov_private_key_ = new MockRsaPrivateKey;
EXPECT_CALL(
*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kProvisioningPrivateKey,
kProvisioningPrivateKeyPassphrase))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(mock_prov_private_key_))));
EXPECT_CALL(*mock_prov_public_key_, MatchesPrivateKey(_))
.WillOnce(Return(true));
ASSERT_EQ(OK, ProvisioningEngineImplProvTest::Initialize(
signed_prov_cert_.SerializeAsString()));
// Setup certificate status list.
cert_status_list_.set_creation_time_seconds(time(nullptr));
for (int i = 0; i < 2; ++i) {
DeviceCertificateStatus* cert_status =
cert_status_list_.add_certificate_status();
cert_status->set_oem_serial_number(StrCat("oem_serial_number_", i));
ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info();
device_info->set_system_id(kSystemId + i);
device_info->set_model(StrCat("model_", i));
}
cert_status_list_.mutable_certificate_status(0)->set_status(
DeviceCertificateStatus::VALID);
cert_status_list_.mutable_certificate_status(1)->set_status(
DeviceCertificateStatus::REVOKED);
SignedCertificateStatusList signed_cert_status_list;
signed_cert_status_list.set_certificate_status_list(
cert_status_list_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(
StrEq(signed_cert_status_list.certificate_status_list()),
"cert_status_list_signature"))
.WillOnce(Return(true));
signed_cert_status_list.set_signature("cert_status_list_signature");
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
signed_cert_status_list.SerializeAsString(),
kExpirationPeriodSeconds));
// Setup a DrmDeviceCertificate to be used later.
intermediate_cert_.set_type(DrmDeviceCertificate::DRM_INTERMEDIATE);
intermediate_cert_.set_system_id(kSystemId);
intermediate_cert_.set_public_key(kIntermediatePublicKey);
signed_intermediate_cert_.set_drm_certificate(
intermediate_cert_.SerializeAsString());
signed_intermediate_cert_.set_signature(kSignature);
}
MockRsaPublicKey* mock_prov_public_key_ = nullptr;
MockRsaPrivateKey* mock_prov_private_key_ = nullptr;
DeviceCertificateStatusList cert_status_list_;
DrmDeviceCertificate intermediate_cert_;
SignedDrmDeviceCertificate signed_intermediate_cert_;
};
TEST_F(ProvisioningEngineImplGeneralTest, InvalidCertificateStatusList) {
EXPECT_EQ(INVALID_STATUS_LIST, engine_impl_.SetCertificateStatusList(
"", kExpirationPeriodSeconds));
EXPECT_EQ(INVALID_STATUS_LIST,
engine_impl_.SetCertificateStatusList(
"invalid_certificate_status_list", kExpirationPeriodSeconds));
}
TEST_F(ProvisioningEngineImplGeneralTest,
CertificateStatusListIncorrectSignature) {
SignedCertificateStatusList signed_cert_status_list;
signed_cert_status_list.set_certificate_status_list(
cert_status_list_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, "cert_status_list_signature"))
.WillOnce(Return(false));
signed_cert_status_list.set_signature("cert_status_list_signature");
ASSERT_EQ(INVALID_STATUS_LIST,
engine_impl_.SetCertificateStatusList(
signed_cert_status_list.SerializeAsString(),
kExpirationPeriodSeconds));
}
TEST_F(ProvisioningEngineImplGeneralTest, GetDeviceInfoAndCheckDeviceStatus) {
EXPECT_EQ(OK, CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber));
auto device_info = engine_impl_.GetDeviceInfo(kSystemId);
ASSERT_NE(nullptr, device_info);
EXPECT_EQ("model_0", device_info->model());
EXPECT_EQ(DEVICE_REVOKED,
CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber));
// We can still query device info for revoked device.
device_info = engine_impl_.GetDeviceInfo(kSystemId + 1);
ASSERT_NE(nullptr, device_info);
EXPECT_EQ("model_1", device_info->model());
EXPECT_EQ(UNKNOWN_SYSTEM_ID,
CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber));
EXPECT_EQ(nullptr, engine_impl_.GetDeviceInfo(kSystemId + 2));
}
TEST_F(ProvisioningEngineImplGeneralTest, UpdateCertificateStatusList) {
cert_status_list_.mutable_certificate_status(0)->set_status(
DeviceCertificateStatus::REVOKED);
DeviceCertificateStatus* cert_status =
cert_status_list_.add_certificate_status();
ProvisionedDeviceInfo* device_info = cert_status->mutable_device_info();
device_info->set_system_id(kSystemId + 2);
device_info->set_model("model_2");
SignedCertificateStatusList signed_cert_status_list;
signed_cert_status_list.set_certificate_status_list(
cert_status_list_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, "cert_status_list_signature"))
.WillOnce(Return(true));
signed_cert_status_list.set_signature("cert_status_list_signature");
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
signed_cert_status_list.SerializeAsString(),
kInfiniteExpirationPeriodSeconds));
// Now device with system id = kSystemId is revoked.
EXPECT_EQ(DEVICE_REVOKED,
CheckDeviceStatus(kSystemId, kEmptyOemSerialNumber));
EXPECT_EQ("model_0", engine_impl_.GetDeviceInfo(kSystemId)->model());
EXPECT_EQ(DEVICE_REVOKED,
CheckDeviceStatus(kSystemId + 1, kEmptyOemSerialNumber));
EXPECT_EQ("model_1", engine_impl_.GetDeviceInfo(kSystemId + 1)->model());
EXPECT_EQ(OK, CheckDeviceStatus(kSystemId + 2, kEmptyOemSerialNumber));
EXPECT_EQ("model_2", engine_impl_.GetDeviceInfo(kSystemId + 2)->model());
}
TEST_F(ProvisioningEngineImplGeneralTest,
GenerateDrmIntermediateCertificateInvalidPublicKey) {
std::string drm_certificate;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(Return(ByMove(nullptr)));
std::string certificate;
ASSERT_EQ(INVALID_INTERMEDIATE_PUBLIC_KEY,
engine_impl_.GenerateDrmIntermediateCertificate(
kSystemId, kIntermediatePublicKey, &certificate));
}
TEST_F(ProvisioningEngineImplGeneralTest, GenerateDrmIntermediateCertificate) {
std::string drm_certificate;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_prov_private_key_, GenerateSignature(_, _))
.WillOnce(DoAll(SaveArg<0>(&drm_certificate),
SetArgPointee<1>(kSignature), Return(true)));
std::string certificate;
ASSERT_EQ(OK, engine_impl_.GenerateDrmIntermediateCertificate(
kSystemId, kIntermediatePublicKey, &certificate));
SignedDrmDeviceCertificate signed_drm_cert_proto;
ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate));
EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate());
EXPECT_EQ(kSignature, signed_drm_cert_proto.signature());
EXPECT_EQ(signed_prov_cert_.SerializeAsString(),
signed_drm_cert_proto.signer().SerializeAsString());
DrmDeviceCertificate drm_cert_proto;
ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate));
EXPECT_EQ(DrmDeviceCertificate::DRM_INTERMEDIATE, drm_cert_proto.type());
EXPECT_NE("", drm_cert_proto.serial_number());
EXPECT_EQ(kSystemId, drm_cert_proto.system_id());
EXPECT_EQ(kIntermediatePublicKey, drm_cert_proto.public_key());
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateInvalidCert) {
EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE,
engine_impl_.AddDrmIntermediateCertificate(
"", kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase));
EXPECT_EQ(INVALID_INTERMEDIATE_DRM_CERTIFICATE,
engine_impl_.AddDrmIntermediateCertificate(
"invalid_intermediate_cert", kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateIncorrectCertType) {
intermediate_cert_.set_type(DrmDeviceCertificate::DRM_USER_DEVICE);
signed_intermediate_cert_.set_drm_certificate(
intermediate_cert_.SerializeAsString());
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_EQ(
INVALID_INTERMEDIATE_DRM_CERTIFICATE,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateMissingSystemId) {
intermediate_cert_.clear_system_id();
signed_intermediate_cert_.set_drm_certificate(
intermediate_cert_.SerializeAsString());
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_EQ(
UNKNOWN_SYSTEM_ID,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateRevokedCert) {
intermediate_cert_.set_system_id(kSystemId + 1);
signed_intermediate_cert_.set_drm_certificate(
intermediate_cert_.SerializeAsString());
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_EQ(DEVICE_REVOKED, engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateInvalidPublicKey) {
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(
INVALID_INTERMEDIATE_DRM_CERTIFICATE,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateInvalidPrivateKey) {
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(
INVALID_INTERMEDIATE_PRIVATE_KEY,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateMismatchPublicPrivateKey) {
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
.WillOnce(Return(false));
EXPECT_EQ(
INVALID_INTERMEDIATE_PRIVATE_KEY,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
AddDrmIntermediateCertificateSuccess) {
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
.WillOnce(Return(true));
EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest, ExpiredCertificateStatusList) {
cert_status_list_.set_creation_time_seconds(time(nullptr) -
kExpirationPeriodSeconds - 1);
SignedCertificateStatusList signed_cert_status_list;
signed_cert_status_list.set_certificate_status_list(
cert_status_list_.SerializeAsString());
EXPECT_CALL(*mock_root_public_key_,
VerifySignature(_, "cert_status_list_signature"))
.WillOnce(Return(true));
signed_cert_status_list.set_signature("cert_status_list_signature");
ASSERT_EQ(OK, engine_impl_.SetCertificateStatusList(
signed_cert_status_list.SerializeAsString(),
kExpirationPeriodSeconds));
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
EXPECT_EQ(
STATUS_LIST_EXPIRED,
engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey, kIntermediatePrivateKeyPassphrase));
}
TEST_F(ProvisioningEngineImplGeneralTest,
GenerateDeviceDrmCertificateRevokedDevice) {
std::string certificate;
EXPECT_EQ(DEVICE_REVOKED,
engine_impl_.GenerateDeviceDrmCertificate(
kSystemId + 1, kOemSerialNumber1, kDevicePublicKey,
kCertificateSerialNumber, &certificate));
}
TEST_F(ProvisioningEngineImplGeneralTest,
GenerateDeviceDrmCertificateWithMismatchingOemSerialNumber) {
std::string certificate;
// If oem serial number does not match, consider as revoked.
EXPECT_EQ(DEVICE_REVOKED,
engine_impl_.GenerateDeviceDrmCertificate(
kSystemId, kOemSerialNumber1, kDevicePublicKey,
kCertificateSerialNumber, &certificate));
}
TEST_F(ProvisioningEngineImplGeneralTest,
GenerateDeviceDrmCertificateWithoutIntermediateCert) {
std::string certificate;
EXPECT_EQ(MISSING_DRM_INTERMEDIATE_CERT,
engine_impl_.GenerateDeviceDrmCertificate(
kSystemId, kOemSerialNumber0, kDevicePublicKey,
kCertificateSerialNumber, &certificate));
}
TEST_F(ProvisioningEngineImplGeneralTest,
GenerateDeviceDrmCertificate) {
// Add Intermediate certificate.
EXPECT_CALL(
*mock_prov_public_key_,
VerifySignature(StrEq(signed_intermediate_cert_.drm_certificate()),
StrEq(signed_intermediate_cert_.signature())))
.WillOnce(Return(true));
MockRsaPublicKey* mock_intermediate_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kIntermediatePublicKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPublicKey>(mock_intermediate_public_key))));
MockRsaPrivateKey* mock_intermediate_private_key = new MockRsaPrivateKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs8PrivateKey(kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase))
.WillOnce(Return(ByMove(
std::unique_ptr<RsaPrivateKey>(mock_intermediate_private_key))));
EXPECT_CALL(*mock_intermediate_public_key, MatchesPrivateKey(_))
.WillOnce(Return(true));
EXPECT_EQ(OK, engine_impl_.AddDrmIntermediateCertificate(
signed_intermediate_cert_.SerializeAsString(),
kIntermediatePrivateKey,
kIntermediatePrivateKeyPassphrase));
// Intermediate private key expectation.
std::string drm_certificate;
EXPECT_CALL(*mock_intermediate_private_key, GenerateSignature(_, _))
.WillOnce(DoAll(SaveArg<0>(&drm_certificate),
SetArgPointee<1>(kSignature), Return(true)));
std::string certificate;
EXPECT_EQ(OK, engine_impl_.GenerateDeviceDrmCertificate(
kSystemId, kOemSerialNumber0, kDevicePublicKey,
kCertificateSerialNumber, &certificate));
SignedDrmDeviceCertificate signed_drm_cert_proto;
ASSERT_TRUE(signed_drm_cert_proto.ParseFromString(certificate));
EXPECT_EQ(drm_certificate, signed_drm_cert_proto.drm_certificate());
EXPECT_EQ(kSignature, signed_drm_cert_proto.signature());
EXPECT_THAT(signed_intermediate_cert_.SerializeAsString(),
signed_drm_cert_proto.signer().SerializeAsString());
DrmDeviceCertificate drm_cert_proto;
ASSERT_TRUE(drm_cert_proto.ParseFromString(drm_certificate));
EXPECT_EQ(DrmDeviceCertificate::DRM_USER_DEVICE, drm_cert_proto.type());
EXPECT_EQ(kCertificateSerialNumber, drm_cert_proto.serial_number());
EXPECT_EQ(kSystemId, drm_cert_proto.system_id());
EXPECT_EQ(kDevicePublicKey, drm_cert_proto.public_key());
}
} // namespace widevine

View File

@@ -0,0 +1,256 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/provisioning_session_impl.h"
#include <stddef.h>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "common/aes_cbc_util.h"
#include "common/random_util.h"
#include "common/rsa_key.h"
#include "common/sha_util.h"
#include "provisioning_sdk/public/provisioning_status.h"
DEFINE_int32(provisioning_log_every_n, 1,
"parameter for LOG_EVERY_N to help abate log spamming.");
#define LOG_EVERY_N_WITH_PROTO(message, proto) \
LOG_EVERY_N(WARNING, FLAGS_provisioning_log_every_n) \
<< (message) << " [proto: " << (proto).ShortDebugString() << "]"
namespace widevine {
ProvisioningSessionImpl::ProvisioningSessionImpl(
const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert,
const RsaPrivateKey& service_private_key)
: engine_(engine),
oem_device_cert_(oem_device_cert),
service_private_key_(service_private_key),
rsa_key_factory_(new RsaKeyFactory) {}
ProvisioningSessionImpl::~ProvisioningSessionImpl() {}
ProvisioningStatus ProvisioningSessionImpl::Initialize(
const std::string& device_public_key, const std::string& device_private_key) {
auto rsa_public_key =
rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key);
if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY;
auto rsa_private_key =
rsa_key_factory_->CreateFromPkcs1PrivateKey(device_private_key);
if (!rsa_private_key) return INVALID_DEVICE_PRIVATE_KEY;
if (!rsa_public_key->MatchesPrivateKey(*rsa_private_key)) {
LOG(WARNING) << "Device public key and private key do not match.";
return INVALID_DEVICE_PRIVATE_KEY;
}
device_public_key_ = device_public_key;
device_private_key_ = device_private_key;
return OK;
}
ProvisioningStatus ProvisioningSessionImpl::ProcessMessage(
const std::string& message, std::string* response) {
SignedProvisioningMessage signed_request;
ProvisioningRequest request;
if (!ValidateAndDeserializeRequest(message, &signed_request, &request))
return INVALID_REQUEST_MESSAGE;
ClientIdentification client_id;
if (request.has_encrypted_client_id()) {
if (!DecryptClientIdentification(request.encrypted_client_id(), &client_id))
return INVALID_REQUEST_MESSAGE;
} else {
DCHECK(request.has_client_id());
client_id.Swap(request.mutable_client_id());
}
if (client_id.type() != ClientIdentification::OEM_DEVICE_CERTIFICATE) {
LOG_EVERY_N_WITH_PROTO("Invalid client_id type", client_id);
return INVALID_REQUEST_MESSAGE;
}
if (client_id.token().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing client_id.token", client_id);
return INVALID_REQUEST_MESSAGE;
}
std::unique_ptr<RsaPublicKey> cert_public_key;
uint32_t system_id;
std::string oem_ca_serial_number;
if (!oem_device_cert_.VerifyCertificateChain(client_id.token(),
&cert_public_key, &system_id,
&oem_ca_serial_number)) {
LOG_EVERY_N_WITH_PROTO("Invalid token", client_id);
return INVALID_REQUEST_MESSAGE;
}
if (!cert_public_key->VerifySignature(signed_request.message(),
signed_request.signature())) {
LOG_EVERY_N_WITH_PROTO("Signature verification failed", client_id);
return INVALID_REQUEST_MESSAGE;
}
// Save device_info for query later.
device_info_ = engine_.GetDeviceInfo(system_id);
std::string certificate_serial_number;
if (request.has_spoid()) {
certificate_serial_number = request.spoid();
} else {
// Generate stable serial number.
const std::string stable_data(client_id.token() + request.stable_id() +
request.provider_id() +
engine_.secret_spoid_sauce());
const std::string hash = Sha256_Hash(stable_data);
const size_t kCertificateSerialNumberSize = 16;
certificate_serial_number = hash.substr(0, kCertificateSerialNumberSize);
}
ProvisioningResponse provisioning_response;
ProvisioningStatus status = GenerateProvisioningResponse(
system_id, oem_ca_serial_number, request.provider_id(),
certificate_serial_number, *cert_public_key, &provisioning_response);
if (status != OK) return status;
provisioning_response.set_nonce(request.nonce());
// Sign the response.
SignedProvisioningMessage signed_message;
if (!provisioning_response.SerializeToString(
signed_message.mutable_message())) {
LOG(WARNING) << "Error serializing ProvisioningResponse.";
return INTERNAL_ERROR;
}
if (!service_private_key_.GenerateSignature(
signed_message.message(), signed_message.mutable_signature())) {
LOG(WARNING) << "Failed to sign ProvisioningResponse.";
return INTERNAL_ERROR;
}
if (!signed_message.SerializeToString(response)) {
LOG(WARNING) << "Error serializing SignedProvisioningMessage.";
return INTERNAL_ERROR;
}
return OK;
}
bool ProvisioningSessionImpl::ValidateAndDeserializeRequest(
const std::string& message, SignedProvisioningMessage* signed_request,
ProvisioningRequest* request) const {
if (!signed_request->ParseFromString(message)) {
LOG_EVERY_N(WARNING, FLAGS_provisioning_log_every_n)
<< "Failed to parse SignedProvisioningMessage.";
return false;
}
VLOG(1) << "signed_request: " << signed_request->ShortDebugString();
if (signed_request->message().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing message", *signed_request);
return false;
}
if (signed_request->signature().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing signature", *signed_request);
return false;
}
if (!request->ParseFromString(signed_request->message())) {
LOG_EVERY_N_WITH_PROTO("Failed to parse ProvisioningRequest",
*signed_request);
return false;
}
if (request->has_encrypted_client_id()) {
const EncryptedClientIdentification& encrypted_client_id =
request->encrypted_client_id();
if (encrypted_client_id.encrypted_client_id().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id",
encrypted_client_id);
return false;
}
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id_iv",
encrypted_client_id);
return false;
}
if (encrypted_client_id.encrypted_privacy_key().empty()) {
LOG_EVERY_N_WITH_PROTO("Missing encrypted_privacy_key",
encrypted_client_id);
return false;
}
} else if (!request->has_client_id()) {
LOG_EVERY_N_WITH_PROTO("Missing clear_or_encrypted_client_id", *request);
return false;
}
const size_t kMinimumRequiredNonceLength = 4;
if (request->nonce().size() < kMinimumRequiredNonceLength) {
LOG_EVERY_N_WITH_PROTO("Missing or invalid nonce", *request);
return false;
}
return true;
}
bool ProvisioningSessionImpl::DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id) {
std::string privacy_key;
if (!service_private_key_.Decrypt(encrypted_client_id.encrypted_privacy_key(),
&privacy_key)) {
LOG_EVERY_N_WITH_PROTO("Failed to decrypt encrypted_privacy_key",
encrypted_client_id);
return false;
}
std::string serialized_client_id(crypto_util::DecryptAesCbc(
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
encrypted_client_id.encrypted_client_id()));
if (serialized_client_id.empty()) {
LOG_EVERY_N_WITH_PROTO("Failed to decrypt client_id", encrypted_client_id);
return false;
}
if (!client_id->ParseFromString(serialized_client_id)) {
LOG_EVERY_N_WITH_PROTO("Failed to parse client_id", encrypted_client_id);
return false;
}
return true;
}
ProvisioningStatus ProvisioningSessionImpl::GenerateProvisioningResponse(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& provider_id, const std::string& certificate_serial_number,
const RsaPublicKey& cert_public_key, ProvisioningResponse* response) {
ProvisioningStatus status = engine_.GenerateProviderDeviceDrmCertificate(
system_id, oem_ca_serial_number, provider_id, device_public_key_,
certificate_serial_number, response->mutable_device_certificate());
if (status != OK) return status;
const size_t kAesKeySize = 16;
const size_t kIvSize = 16;
// Encrypt private key.
std::string message_key;
if (!RandomBytes(kAesKeySize, &message_key)) {
LOG(WARNING) << "Failed to generate message_key.";
return INTERNAL_ERROR;
}
std::string iv;
if (!RandomBytes(kIvSize, &iv)) {
LOG(WARNING) << "Failed to generate iv.";
return INTERNAL_ERROR;
}
response->set_device_rsa_key_iv(iv);
response->set_device_rsa_key(
crypto_util::EncryptAesCbc(message_key, iv, device_private_key_));
if (response->device_rsa_key().empty()) {
LOG(WARNING) << "Failed to encrypt device_rsa_key";
return INTERNAL_ERROR;
}
if (!cert_public_key.Encrypt(message_key, response->mutable_wrapping_key())) {
LOG(WARNING) << "Failed to encrypt wrapping_key";
return INTERNAL_ERROR;
}
return OK;
}
} // namespace widevine

View File

@@ -0,0 +1,88 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
// ProvisioningSession internal implementation.
#ifndef PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_
#define PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "common/rsa_key.h"
#include "provisioning_sdk/internal/oem_device_cert.h"
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
#include "provisioning_sdk/public/provisioning_status.h"
#include "protos/public/certificate_provisioning.pb.h"
#include "protos/public/client_identification.pb.h"
#include "protos/public/device_certificate.pb.h"
#include "protos/public/provisioned_device_info.pb.h"
namespace widevine {
class ProvisioningSessionImpl {
public:
ProvisioningSessionImpl(const ProvisioningEngineImpl& engine,
const OemDeviceCert& oem_device_cert,
const RsaPrivateKey& service_private_key);
~ProvisioningSessionImpl();
// Initialize provisioning session with given public key and private key.
ProvisioningStatus Initialize(const std::string& device_public_key,
const std::string& device_private_key);
// Process a message from the client device.
// * |message| is the message received from the client device.
// * |response| will contain, upon successful return, a message to be sent
// back to the client device as a response to |message|.
// Returns OK if successful, or an appropriate error status code otherwise.
ProvisioningStatus ProcessMessage(const std::string& message, std::string* response);
// * Returns a ProvisioneddeviceInfo message containing information about the
// type of device being provisioned. May return nullptr.
const ProvisionedDeviceInfo* GetDeviceInfo() const {
return device_info_.get();
}
private:
friend class ProvisioningSessionImplTest;
ProvisioningSessionImpl(const ProvisioningSessionImpl&) = delete;
ProvisioningSessionImpl& operator=(const ProvisioningSessionImpl&) = delete;
bool ValidateAndDeserializeRequest(const std::string& message,
SignedProvisioningMessage* signed_request,
ProvisioningRequest* request) const;
bool DecryptClientIdentification(
const EncryptedClientIdentification& encrypted_client_id,
ClientIdentification* client_id);
ProvisioningStatus GenerateProvisioningResponse(
uint32_t system_id, const std::string& oem_ca_serial_number,
const std::string& provider_id, const std::string& certificate_serial_number,
const RsaPublicKey& cert_public_key, ProvisioningResponse* response);
// Inject rsa_key_factory for testing.
void set_rsa_key_factory(std::unique_ptr<RsaKeyFactory> rsa_key_factory) {
rsa_key_factory_ = std::move(rsa_key_factory);
}
const ProvisioningEngineImpl& engine_;
const OemDeviceCert& oem_device_cert_;
const RsaPrivateKey& service_private_key_;
std::unique_ptr<RsaKeyFactory> rsa_key_factory_;
std::string device_public_key_;
std::string device_private_key_;
std::shared_ptr<ProvisionedDeviceInfo> device_info_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_INTERNAL_PROVISIONING_SESSION_IMPL_H_

View File

@@ -0,0 +1,426 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google Inc.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/provisioning_session_impl.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "common/aes_cbc_util.h"
#include "common/mock_rsa_key.h"
#include "common/sha_util.h"
#include "provisioning_sdk/internal/oem_device_cert.h"
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
namespace {
const char kEncryptedClientIdIv[] = "sixteen_bytes_iv";
const char kPrivacyKey[] = "privacy_key_16B_";
const char kProviderId[] = "testing_provider";
const char kClientToken[] = "client_id_token";
const char kDevicePublicKey[] = "device_public_key";
const char kEncryptedPrivacyKey[] = "encrypted_privacy_key";
const char kDevicePrivateKey[] = "device_private_key";
const char kWrappingKey[] = "wrapping_key";
const char kDeviceCertificate[] = "device_certificate";
const char kNonce[] = "testing_nonce";
const char kSignature[] = "generated_signature";
// Derives Stable Per-Origin IDentifiers.
std::string DeriveSpoid(const std::string& client_token,
const std::string& provider_id,
const std::string& secret_sauce) {
return widevine::Sha256_Hash(client_token + provider_id + secret_sauce)
.substr(0, 16);
}
} // namespace
namespace widevine {
class MockProvisioningEngineImpl : public ProvisioningEngineImpl {
public:
MOCK_CONST_METHOD6(GenerateProviderDeviceDrmCertificate,
ProvisioningStatus(uint32_t system_id,
const std::string& oem_ca_serial_number,
const std::string& provider_id,
const std::string& public_key,
const std::string& certificate_serial_number,
std::string* certificate));
};
class MockOemDeviceCert : public OemDeviceCert {
public:
// gmock does not support SetArgPointee on std::unique_ptr, so we have to
// workaround it with a trick.
MOCK_CONST_METHOD4(DoVerifyCertificateChain,
bool(const std::string& certificate_chain,
RsaPublicKey** leaf_public_key, uint32_t* system_id,
std::string* oem_ca_serial_number));
bool VerifyCertificateChain(const std::string& certificate_chain,
std::unique_ptr<RsaPublicKey>* leaf_public_key,
uint32_t* system_id,
std::string* oem_ca_serial_number) const override {
RsaPublicKey* raw_leaf_public_key = nullptr;
if (!DoVerifyCertificateChain(certificate_chain, &raw_leaf_public_key,
system_id, oem_ca_serial_number)) {
return false;
}
*leaf_public_key = std::unique_ptr<RsaPublicKey>(raw_leaf_public_key);
return true;
}
};
class ProvisioningSessionImplTest : public ::testing::Test {
protected:
ProvisioningSessionImplTest()
: session_impl_(mock_engine_impl_, mock_oem_device_cert_,
mock_service_private_key_) {
mock_rsa_key_factory_ = new MockRsaKeyFactory;
session_impl_.set_rsa_key_factory(
std::unique_ptr<RsaKeyFactory>(mock_rsa_key_factory_));
}
ProvisioningSessionImpl session_impl_;
MockRsaKeyFactory* mock_rsa_key_factory_ = nullptr;
MockProvisioningEngineImpl mock_engine_impl_;
MockOemDeviceCert mock_oem_device_cert_;
MockRsaPrivateKey mock_service_private_key_;
};
TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPublicKey) {
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kDevicePublicKey))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(
INVALID_DEVICE_PUBLIC_KEY,
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
}
TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPrivateKey) {
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kDevicePublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(
INVALID_DEVICE_PRIVATE_KEY,
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
}
TEST_F(ProvisioningSessionImplTest, InitializeWithMismatchPublicPrivateKey) {
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kDevicePublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
.WillOnce(Return(false));
EXPECT_EQ(
INVALID_DEVICE_PRIVATE_KEY,
session_impl_.Initialize(kDevicePublicKey, kDevicePrivateKey));
}
class ProvisioningSessionImplProcessTest : public ProvisioningSessionImplTest {
public:
void SetUp() override {
MockRsaPublicKey* mock_rsa_public_key = new MockRsaPublicKey;
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PublicKey(kDevicePublicKey))
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
.WillOnce(Return(true));
ASSERT_EQ(OK, session_impl_.Initialize(kDevicePublicKey,
kDevicePrivateKey));
// Setup Provisioning Message.
client_id_.set_type(ClientIdentification::OEM_DEVICE_CERTIFICATE);
client_id_.set_token(kClientToken);
EncryptedClientIdentification* encrypted_client_id =
prov_request_.mutable_encrypted_client_id();
encrypted_client_id->set_encrypted_client_id(crypto_util::EncryptAesCbc(
kPrivacyKey, kEncryptedClientIdIv, client_id_.SerializeAsString()));
encrypted_client_id->set_encrypted_client_id_iv(kEncryptedClientIdIv);
encrypted_client_id->set_encrypted_privacy_key(kEncryptedPrivacyKey);
prov_request_.set_provider_id(kProviderId);
prov_request_.set_nonce(kNonce);
signed_prov_message_.set_message(prov_request_.SerializeAsString());
signed_prov_message_.set_signature("testing_signature");
}
ClientIdentification client_id_;
ProvisioningRequest prov_request_;
SignedProvisioningMessage signed_prov_message_;
};
TEST_F(ProvisioningSessionImplProcessTest, InvalidMessage) {
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage("invalid_message", &response));
}
TEST_F(ProvisioningSessionImplProcessTest, EmptyMessage) {
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage("", &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingMessage) {
signed_prov_message_.clear_message();
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingSignature) {
signed_prov_message_.clear_signature();
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingClientId) {
prov_request_.clear_encrypted_client_id();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientId) {
prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedClientIdIv) {
prov_request_.mutable_encrypted_client_id()->clear_encrypted_client_id_iv();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, MissingEncryptedPrivacyKey) {
prov_request_.mutable_encrypted_client_id()->clear_encrypted_privacy_key();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, InvalidNonce) {
// Nonce should be at least 4 buytes.
const char kNonceWithLessThanFourBytes[] = "xx";
prov_request_.set_nonce(kNonceWithLessThanFourBytes);
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, PrivacyKeyDecryptionFailed) {
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(Return(false));
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, InvalidEncryptedClientId) {
prov_request_.mutable_encrypted_client_id()->set_encrypted_client_id(
"invalid_encrypted_client_id");
signed_prov_message_.set_message(prov_request_.SerializeAsString());
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, VerifyCertificateChainFailed) {
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
EXPECT_CALL(mock_oem_device_cert_,
DoVerifyCertificateChain(kClientToken, _, _, _))
.WillOnce(Return(false));
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest,
ClearClientIdVerifyCertificateChainFailed) {
*prov_request_.mutable_client_id() = client_id_;
prov_request_.clear_encrypted_client_id();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
EXPECT_CALL(mock_oem_device_cert_,
DoVerifyCertificateChain(kClientToken, _, _, _))
.WillOnce(Return(false));
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdInvalidClientIdType) {
client_id_.set_type(ClientIdentification::KEYBOX);
*prov_request_.mutable_client_id() = client_id_;
prov_request_.clear_encrypted_client_id();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, ClearClientIdMissingToken) {
client_id_.clear_token();
*prov_request_.mutable_client_id() = client_id_;
prov_request_.clear_encrypted_client_id();
signed_prov_message_.set_message(prov_request_.SerializeAsString());
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, VerifySignatureFailed) {
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
EXPECT_CALL(mock_oem_device_cert_,
DoVerifyCertificateChain(kClientToken, _, _, _))
.WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key), Return(true)));
EXPECT_CALL(*mock_cert_public_key,
VerifySignature(signed_prov_message_.message(),
signed_prov_message_.signature()))
.WillOnce(Return(false));
std::string response;
EXPECT_EQ(INVALID_REQUEST_MESSAGE,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, GenerateDeviceCertificateFailed) {
const uint32_t kSystemId = 1234;
const char kExpectedOemSerialNumber[] = "test_oem_serial_number";
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
EXPECT_CALL(mock_oem_device_cert_,
DoVerifyCertificateChain(kClientToken, _, _, _))
.WillOnce(DoAll(
SetArgPointee<1>(mock_cert_public_key), SetArgPointee<2>(kSystemId),
SetArgPointee<3>(kExpectedOemSerialNumber), Return(true)));
EXPECT_CALL(*mock_cert_public_key,
VerifySignature(signed_prov_message_.message(),
signed_prov_message_.signature()))
.WillOnce(Return(true));
EXPECT_CALL(
mock_engine_impl_,
GenerateProviderDeviceDrmCertificate(
kSystemId, kExpectedOemSerialNumber, kProviderId, kDevicePublicKey,
DeriveSpoid(kClientToken, kProviderId, ""), _))
.WillOnce(Return(INTERNAL_ERROR));
std::string response;
EXPECT_EQ(INTERNAL_ERROR,
session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
}
TEST_F(ProvisioningSessionImplProcessTest, Success) {
const uint32_t kSystemId = 1234;
EXPECT_CALL(mock_service_private_key_, Decrypt(kEncryptedPrivacyKey, _))
.WillOnce(DoAll(SetArgPointee<1>(kPrivacyKey), Return(true)));
MockRsaPublicKey* mock_cert_public_key = new MockRsaPublicKey;
EXPECT_CALL(mock_oem_device_cert_,
DoVerifyCertificateChain(kClientToken, _, _, _))
.WillOnce(DoAll(SetArgPointee<1>(mock_cert_public_key),
SetArgPointee<2>(kSystemId), Return(true)));
EXPECT_CALL(*mock_cert_public_key,
VerifySignature(signed_prov_message_.message(),
signed_prov_message_.signature()))
.WillOnce(Return(true));
EXPECT_CALL(mock_engine_impl_,
GenerateProviderDeviceDrmCertificate(kSystemId, _, _,
kDevicePublicKey, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kDeviceCertificate), Return(OK)));
std::string message_key;
EXPECT_CALL(*mock_cert_public_key, Encrypt(_, _))
.WillOnce(DoAll(SaveArg<0>(&message_key),
SetArgPointee<1>(kWrappingKey), Return(true)));
std::string message;
EXPECT_CALL(mock_service_private_key_, GenerateSignature(_, _))
.WillOnce(DoAll(SaveArg<0>(&message),
SetArgPointee<1>(kSignature), Return(true)));
std::string response;
ASSERT_EQ(OK, session_impl_.ProcessMessage(
signed_prov_message_.SerializeAsString(), &response));
// Verify the response.
SignedProvisioningMessage signed_prov_message;
ASSERT_TRUE(signed_prov_message.ParseFromString(response));
EXPECT_EQ(message, signed_prov_message.message());
EXPECT_EQ(kSignature, signed_prov_message.signature());
ProvisioningResponse prov_response;
ASSERT_TRUE(prov_response.ParseFromString(message));
EXPECT_EQ(
kDevicePrivateKey,
crypto_util::DecryptAesCbc(message_key, prov_response.device_rsa_key_iv(),
prov_response.device_rsa_key()));
EXPECT_EQ(kDeviceCertificate, prov_response.device_certificate());
EXPECT_EQ(kNonce, prov_response.nonce());
EXPECT_EQ(kWrappingKey, prov_response.wrapping_key());
}
} // namespace widevine