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

View File

@@ -0,0 +1,84 @@
################################################################################
# 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.
package(
default_visibility = ["//visibility:public"],
)
# Defines the common copts for cc_library targets. The main option here is
# -fvisibility=default, which exports symbols from the public APIs in the shared
# library.
# Note that the shared library should be built with -fvisibility=hidden.
PUBLIC_COPTS = ["-fvisibility=default"]
cc_binary(
name = "libprovisioning_sdk.so",
linkshared = 1,
deps = [":provisioning_engine"],
)
cc_library(
name = "libprovisioning_sdk",
srcs = [":libprovisioning_sdk.so"],
hdrs = glob(["*.h"]),
)
cc_library(
name = "provisioning_engine",
srcs = ["provisioning_engine.cc"],
hdrs = ["provisioning_engine.h"],
copts = PUBLIC_COPTS,
deps = [
":certificate_type",
":provisioning_session",
":provisioning_status",
"//base",
"//common:rsa_key",
"//provisioning_sdk/internal:provisioning_engine_impl",
"//provisioning_sdk/internal:provisioning_session_impl",
],
)
cc_test(
name = "provisioning_engine_test",
size = "small",
srcs = ["provisioning_engine_test.cc"],
deps = [
":provisioning_engine",
"//external:gtest_main",
],
)
cc_library(
name = "provisioning_session",
srcs = ["provisioning_session.cc"],
hdrs = ["provisioning_session.h"],
copts = PUBLIC_COPTS,
deps = [
":provisioning_status",
"//base",
"//provisioning_sdk/internal:provisioning_session_impl",
"//protos/public:device_certificate_proto",
],
)
cc_library(
name = "provisioning_status",
srcs = ["provisioning_status.cc"],
hdrs = ["provisioning_status.h"],
copts = PUBLIC_COPTS,
deps = ["//base"],
)
cc_library(
name = "certificate_type",
hdrs = ["certificate_type.h"],
copts = PUBLIC_COPTS,
)

View File

@@ -0,0 +1,22 @@
////////////////////////////////////////////////////////////////////////////////
// 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_PUBLIC_CERTIFICATE_TYPE_H_
#define PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_
namespace widevine {
enum CertificateType {
kCertTesting = 0,
kCertDevelopment,
kCertProduction,
};
} // namespace widevine
#endif // PROVISIONING_SDK_PUBLIC_CERTIFICATE_TYPE_H_

View File

@@ -0,0 +1,118 @@
////////////////////////////////////////////////////////////////////////////////
// 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/public/provisioning_engine.h"
#include <utility>
#include "glog/logging.h"
#include "common/rsa_key.h"
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
#include "provisioning_sdk/internal/provisioning_session_impl.h"
#include "provisioning_sdk/public/provisioning_session.h"
namespace widevine {
ProvisioningEngine::ProvisioningEngine() {}
ProvisioningEngine::~ProvisioningEngine() {}
ProvisioningStatus ProvisioningEngine::Initialize(
CertificateType certificate_type, const std::string& service_drm_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) {
if (impl_) {
LOG(WARNING) << "ProvisioningEngine is already initialized.";
return INTERNAL_ERROR;
}
std::unique_ptr<ProvisioningEngineImpl> impl(new ProvisioningEngineImpl);
ProvisioningStatus status = impl->Initialize(
certificate_type, service_drm_certificate, service_private_key,
service_private_key_passphrase, provisioning_drm_certificate,
provisioning_private_key, provisioning_private_key_passphrase,
secret_spoid_sauce);
if (status != OK) return status;
impl_ = std::move(impl);
return OK;
}
ProvisioningStatus ProvisioningEngine::SetCertificateStatusList(
const std::string& certificate_status_list, uint32_t expiration_period_seconds) {
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
return impl_->SetCertificateStatusList(certificate_status_list,
expiration_period_seconds);
}
ProvisioningStatus ProvisioningEngine::GenerateDrmIntermediateCertificate(
uint32_t system_id, const std::string& public_key, std::string* certificate) const {
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
if (!certificate) {
LOG(WARNING) << "|certificate| should not be a nullptr.";
return INTERNAL_ERROR;
}
return impl_->GenerateDrmIntermediateCertificate(system_id, public_key,
certificate);
}
ProvisioningStatus ProvisioningEngine::AddDrmIntermediateCertificate(
const std::string& intermediate_cert, const std::string& cert_private_key,
const std::string& cert_private_key_passphrase) {
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
return impl_->AddDrmIntermediateCertificate(
intermediate_cert, cert_private_key, cert_private_key_passphrase);
}
ProvisioningStatus ProvisioningEngine::NewProvisioningSession(
const std::string& device_public_key, const std::string& device_private_key,
std::unique_ptr<ProvisioningSession>* new_session) const {
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
if (!new_session) {
LOG(WARNING) << "|new_session| should not be a nullptr.";
return INTERNAL_ERROR;
}
std::unique_ptr<ProvisioningSessionImpl> session_impl(
new ProvisioningSessionImpl(*impl_, impl_->oem_device_cert(),
*impl_->service_private_key()));
ProvisioningStatus status =
session_impl->Initialize(device_public_key, device_private_key);
if (status != OK) return status;
new_session->reset(new ProvisioningSession(std::move(session_impl)));
return OK;
}
ProvisioningStatus ProvisioningEngine::GenerateDeviceDrmCertificate(
uint32_t system_id, const std::string& public_key, const std::string& serial_number,
std::string* certificate) const {
if (!impl_) return PROVISIONING_ENGINE_UNINITIALIZED;
if (!certificate) {
LOG(WARNING) << "|certificate| should not be a nullptr.";
return INTERNAL_ERROR;
}
// Validate public key. This validation is done here instead of inside
// |impl_->GenerateDeviceDrmCertificate| is because the latter is an internal
// function which expect the input to be already validated.
std::unique_ptr<RsaPublicKey> rsa_public_key(
RsaPublicKey::Create(public_key));
if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY;
if (serial_number.empty()) return INVALID_SERIAL_NUMBER;
const std::string empty_oem_ca_serial_number;
return impl_->GenerateDeviceDrmCertificate(
system_id, empty_oem_ca_serial_number, public_key, serial_number,
certificate);
}
} // namespace widevine

View File

@@ -0,0 +1,153 @@
////////////////////////////////////////////////////////////////////////////////
// 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_PUBLIC_PROVISIONING_ENGINE_H_
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_
#include <cstdint>
#include <memory>
#include <string>
#include "provisioning_sdk/public/certificate_type.h"
#include "provisioning_sdk/public/provisioning_status.h"
namespace widevine {
class ProvisioningEngineImpl;
class ProvisioningSession;
// Class which is used to implement a Widevine DRM device provisioning engine.
// There should be only one instance of ProvisioningEngine. The engine should
// be "Initialized" before being used. ProvisioningEngine::Initialize is the
// only method that is not thread-safe. After initializing the engine, it can
// be safely used in different threads.
class ProvisioningEngine {
public:
ProvisioningEngine();
~ProvisioningEngine();
// Initializes the provisioning engine with required credentials.
// * |certificate_type| indicates which type of certificate chains will be
// used for device provisioning via this engine.
// * |service_drm_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& service_drm_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);
// Create a session to handle a provisioning exchange between a client device
// and the provisioning server.
// * |device_public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which
// will used to create the DRM certificate to be provisioned onto the
// device.
// * |device_private_key| is a DER-encoded PKCS#8 PrivateKeyInfo message
// which contains the private key matching |device_public_key|.
// * |new_session| will point, on successful return, to the newly created
// ProvisioningSession.
// * Returns OK if successful, or an appropriate error status code otherwise.
// The key pairs can be re-used if the created session failed to process the
// message.
// NOTE: All ProvisioningSession objects must be deleted before the
// ProvisioningEngine which created them.
ProvisioningStatus NewProvisioningSession(
const std::string& device_public_key,
const std::string& device_private_key,
std::unique_ptr<ProvisioningSession>* new_session) const;
// Generate a new device DRM certificate to be provisioned by means other than
// the Widevine provisioning protocol.
// NOTE: This API should only be used to provision devices which were
// manufactured without Widevine DRM support. It is meant to be used as
// an exception, and not the norm. Most devices should be provisioned
// by means of a ProvisioningSession.
// * |system_id| is the Widevine system ID for the type of device being
// provisioned.
// * |public_key| is a DER-encoded PKCS#1.5 RSAPublicKey message which will
// be embedded in the generated 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.
ProvisioningStatus GenerateDeviceDrmCertificate(
uint32_t system_id,
const std::string& public_key,
const std::string& serial_number,
std::string* certificate) const;
private:
#ifndef SWIGPYTHON
ProvisioningEngine(const ProvisioningEngine&) = delete;
ProvisioningEngine& operator=(const ProvisioningEngine&) = delete;
#endif
std::unique_ptr<ProvisioningEngineImpl> impl_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_ENGINE_H_

View File

@@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////////////////////////
// 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/public/provisioning_engine.h"
#include "gtest/gtest.h"
namespace {
const int kSystemId = 100;
const int kExpirationPeriodSeconds = 3600;
} // namespace
namespace widevine {
class ProvisioningEngineTest : public ::testing::Test {
protected:
ProvisioningEngine engine_;
};
TEST_F(ProvisioningEngineTest, SetCertificateStatusListWithoutInit) {
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
engine_.SetCertificateStatusList("certificate_status_list",
kExpirationPeriodSeconds));
}
TEST_F(ProvisioningEngineTest, GenerateDrmIntermediateCertificateWithoutInit) {
std::string certificate;
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
engine_.GenerateDrmIntermediateCertificate(
kSystemId, "intermediate_public_key", &certificate));
}
TEST_F(ProvisioningEngineTest, AddDrmIntermediateCertificateWithoutInit) {
std::string certificate;
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
engine_.AddDrmIntermediateCertificate(
"intermediate_certificate", "intermediate_private_key",
"intermediate_private_key_passphrase"));
}
TEST_F(ProvisioningEngineTest, GenerateDeviceDrmCertificateWithoutInit) {
std::string certificate;
EXPECT_EQ(PROVISIONING_ENGINE_UNINITIALIZED,
engine_.GenerateDeviceDrmCertificate(kSystemId, "device_public_key",
"device_serial_number",
&certificate));
}
// ProvisioningEngine is basically a wrapper of ProvisioningEngineImpl, so there
// is no need to test the functionalities as ProvisioningEngineImpl is already
// tested.
} // namespace widevine

View File

@@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// 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/public/provisioning_session.h"
#include <utility>
#include "glog/logging.h"
#include "provisioning_sdk/internal/provisioning_session_impl.h"
#include "protos/public/device_certificate.pb.h"
namespace widevine {
ProvisioningSession::~ProvisioningSession() {}
ProvisioningStatus ProvisioningSession::ProcessMessage(const std::string& message,
std::string* response,
bool* done) {
if (!response) {
LOG(WARNING) << "|response| should not be a nullptr.";
return INTERNAL_ERROR;
}
if (!done) {
LOG(WARNING) << "|done| should not be a nullptr.";
return INTERNAL_ERROR;
}
ProvisioningStatus status = impl_->ProcessMessage(message, response);
*done = true;
return status;
}
const ProvisionedDeviceInfo* ProvisioningSession::GetDeviceInfo() const {
return impl_->GetDeviceInfo();
}
ProvisioningSession::ProvisioningSession(
std::unique_ptr<ProvisioningSessionImpl> impl)
: impl_(std::move(impl)) {
DCHECK(impl_);
}
} // 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_PUBLIC_PROVISIONING_SESSION_H_
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_
#include <memory>
#include <string>
#include "provisioning_sdk/public/provisioning_status.h"
namespace widevine {
class ProvisionedDeviceInfo;
class ProvisioningSessionImpl;
// Class which is used to implement the provisioning session state machine.
class ProvisioningSession {
public:
~ProvisioningSession();
// 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|.
// * |done| will indicate, upon successful return, whether the provisioning
// exchange is complete, and the ProvisioningSession can be deleted.
// Returns OK if successful, or an appropriate error status code otherwise.
ProvisioningStatus ProcessMessage(const std::string& message,
std::string* response,
bool* done);
// * Returns a ProvisioneddeviceInfo message containing information about the
// type of device being provisioned. May return nullptr.
const ProvisionedDeviceInfo* GetDeviceInfo() const;
private:
#ifndef SWIGPYTHON
friend class ProvisioningEngine;
ProvisioningSession(const ProvisioningSession&) = delete;
ProvisioningSession& operator=(const ProvisioningSession&) = delete;
#endif
explicit ProvisioningSession(std::unique_ptr<ProvisioningSessionImpl> impl);
std::unique_ptr<ProvisioningSessionImpl> impl_;
};
} // namespace widevine
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_SESSION_H_

View File

@@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
// 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/public/provisioning_status.h"
#include "base/macros.h"
namespace widevine {
static const char* kProvisioningStatusMessage[] = {
"OK",
"Invalid certificate type",
"Provisioning engine uninitialized",
"Invalid service drm certificate",
"Invalid service private key",
"Invalid provisioning drm certificate",
"Invalid provisioning private key",
"Invalid intermediate drm certificate",
"Invalid intermediate public key",
"Invalid intermediate private key",
"Invalid status list",
"Status list expired",
"Unknown system id",
"Invalid device public key",
"Invalid device private key",
"Invalid request message",
"Invalid MAC",
"Missing DRM intermediate certificate",
"DRM device certificate not set",
"Device revoked",
"Invalid serial number",
"Internal error",
"Invalid SPOID secret sauce"
};
const char* GetProvisioningStatusMessage(ProvisioningStatus status) {
static_assert(
arraysize(kProvisioningStatusMessage) == NUM_PROVISIONING_STATUS,
"mismatching provisioning status message and provisioning status.");
return kProvisioningStatusMessage[status];
}
} // namespace widevine

View File

@@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////////
// 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_PUBLIC_PROVISIONING_STATUS_H_
#define PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_
namespace widevine {
enum ProvisioningStatus {
OK = 0,
INVALID_CERTIFICATE_TYPE = 1,
PROVISIONING_ENGINE_UNINITIALIZED = 2,
INVALID_SERVICE_DRM_CERTIFICATE = 3,
// Invalid service private key or private key passphrase.
INVALID_SERVICE_PRIVATE_KEY = 4,
INVALID_PROVISIONER_DRM_CERTIFICATE = 5,
// Invalid provisioner private key or private key passphrase.
INVALID_PROVISIONER_PRIVATE_KEY = 6,
INVALID_INTERMEDIATE_DRM_CERTIFICATE = 7,
INVALID_INTERMEDIATE_PUBLIC_KEY = 8,
// Invalid intermediate private key or private key passphrase.
INVALID_INTERMEDIATE_PRIVATE_KEY = 9,
INVALID_STATUS_LIST = 10,
STATUS_LIST_EXPIRED = 11,
UNKNOWN_SYSTEM_ID = 12,
INVALID_DEVICE_PUBLIC_KEY = 13,
INVALID_DEVICE_PRIVATE_KEY = 14,
INVALID_REQUEST_MESSAGE = 15,
INVALID_MAC = 16,
MISSING_DRM_INTERMEDIATE_CERT = 17,
DRM_DEVICE_CERTIFICATE_NOT_SET = 18,
DEVICE_REVOKED = 19,
INVALID_SERIAL_NUMBER = 20,
INTERNAL_ERROR = 21,
INVALID_SPOID_SAUCE = 22,
NUM_PROVISIONING_STATUS,
};
// Returns the message std::string for the given ProvisioningStatus.
const char* GetProvisioningStatusMessage(ProvisioningStatus status);
} // namespace widevine
#endif // PROVISIONING_SDK_PUBLIC_PROVISIONING_STATUS_H_

View File

@@ -0,0 +1,73 @@
################################################################################
# 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.
################################################################################
package(default_visibility = ["//visibility:public"])
py_library(
name = "test_data_utility",
srcs = ["test_data_utility.py"],
data = ["//example:example_data"],
deps = [
"//protos/public:certificate_provisioning_py_pb2",
],
)
py_library(
name = "crypto_utility",
srcs = ["crypto_utility.py"],
)
py_test(
name = "init_engine_test",
size = "small",
srcs = ["init_engine_test.py"],
deps = [
":test_data_utility",
],
)
py_test(
name = "set_certificate_status_list_test",
size = "small",
srcs = ["set_certificate_status_list_test.py"],
deps = [
":test_data_utility",
],
)
py_test(
name = "drm_intermediate_certificate_test",
size = "small",
srcs = ["drm_intermediate_certificate_test.py"],
deps = [
":test_data_utility",
],
)
py_test(
name = "engine_generate_certificate_test",
size = "small",
srcs = ["engine_generate_certificate_test.py"],
deps = [
":crypto_utility",
":test_data_utility",
"//protos/public:signed_device_certificate_py_pb2",
],
)
py_test(
name = "new_session_test",
size = "small",
srcs = ["new_session_test.py"],
deps = [
":crypto_utility",
":test_data_utility",
"//protos/public:certificate_provisioning_py_pb2",
"//protos/public:signed_device_certificate_py_pb2",
],
)

View File

@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////////
// 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 "std_string.i"
%include "typemaps.i"
%define %ignoreall %ignore ""; %enddef
%define %unignore %rename("%s") %enddef
%define %unignoreall %rename("%s") ""; %enddef
%define COPY_TYPEMAPS(oldtype, newtype)
typedef oldtype newtype;
%apply oldtype * OUTPUT { newtype * OUTPUT };
%apply oldtype & OUTPUT { newtype & OUTPUT };
%apply oldtype * INPUT { newtype * INPUT };
%apply oldtype & INPUT { newtype & INPUT };
%apply oldtype * INOUT { newtype * INOUT };
%apply oldtype & INOUT { newtype & INOUT };
%enddef
COPY_TYPEMAPS(int, int32);
COPY_TYPEMAPS(unsigned int, uint32);

View File

@@ -0,0 +1,28 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// Swig file to generate a Python library for:
// provisioning_sdk/public/certificate_type.h
%module pywrapcertificate_type
%include "base.i"
%{
#include "provisioning_sdk/public/certificate_type.h"
%}
%ignoreall
%unignore widevine;
%unignore widevine::CertificateType;
%unignore widevine::kCertTesting;
%unignore widevine::kCertDevelopment;
%include "provisioning_sdk/public/certificate_type.h"
%unignoreall

View File

@@ -0,0 +1,25 @@
################################################################################
# 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.
################################################################################
"""Utility functions for cryptography."""
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def VerifySignature(public_key, signature, data):
hash_algorithm = hashes.SHA1()
salt_len = 20
key = serialization.load_der_public_key(
public_key, backend=backends.default_backend())
key.verify(signature, data,
padding.PSS(padding.MGF1(hash_algorithm), salt_len),
hash_algorithm)

View File

@@ -0,0 +1,54 @@
################################################################################
# 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.
################################################################################
import unittest
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_utility
class AddDrmIntermediateTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
test_data_utility.InitProvisionEngineWithTestData(
self._engine, verify_success=True)
def testGenerateDrmIntermediateCertificateWithValidExpirationPeriod(self):
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
def testSetCertificateStatusListInvalid(self):
set_cert_status_list = self._engine.SetCertificateStatusList(
'INVALID_STATUS_LIST', 0)
self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST,
set_cert_status_list)
def testAddDrmIntermediateCertificateWithoutCertificateStatusList(self):
# Users should not be able to add DRM certificate without having
# certificate status list.
status = test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001)
self.assertEqual(pywrapprovisioning_status.STATUS_LIST_EXPIRED, status)
def testAddDrmIntermediateCertificateSystemIdInvalid(self):
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
# system_id 9999 is not in the sample certificate status list
add_ca_status = test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 9999)
self.assertEqual(pywrapprovisioning_status.UNKNOWN_SYSTEM_ID, add_ca_status)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,64 @@
################################################################################
# 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.
################################################################################
import unittest
import crypto_utility
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_utility
from protos.public import signed_device_certificate_pb2
class EngineGenerateCertificateTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
test_data_utility.InitProvisionEngineWithTestData(
self._engine, verify_success=True)
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
def testSuccess(self):
status, signed_cert_string = self._engine.GenerateDeviceDrmCertificate(
2001, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.OK, status)
signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate()
signed_cert.ParseFromString(signed_cert_string)
crypto_utility.VerifySignature(test_data_utility.CA_PUBLIC_KEY,
signed_cert.signature,
signed_cert.drm_certificate)
def testEmptySerialNumber(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2001, test_data_utility.DEVICE_PUBLIC_KEY, '')
self.assertEqual(pywrapprovisioning_status.INVALID_SERIAL_NUMBER, status)
def testEmptyPublicKey(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2001, '', 'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
status)
def testInvalidPublicKey(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2001, 'PUBLIC_KEY_MUST_BE_IN_DER_ENCODED_PKCS1_FORMAT',
'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
status)
def testMissingIntermediateCertificate(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2002, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.DEVICE_REVOKED, status)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,170 @@
################################################################################
# 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.
################################################################################
import unittest
import pywrapcertificate_type
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_utility
class InitEngineTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
def testInitEngineSucceed(self):
test_data_utility.InitProvisionEngineWithTestData(
self._engine, verify_success=True)
def testSetCertificateStatusListWithoutInit(self):
status = self._engine.SetCertificateStatusList('CERTIFICATE_STATUS_LIST',
3600)
self.assertEqual(
pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status)
def testGenerateDrmIntermediateCertificateWithoutInit(self):
status, _ = self._engine.GenerateDrmIntermediateCertificate(
100, 'INTERMEDIATE_PUBLIC_KEY')
self.assertEqual(
pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status)
def testAddDrmIntermediateCertificateWithoutInit(self):
status = self._engine.AddDrmIntermediateCertificate(
'INTERMEDIATE_CERTIFICATE', 'INTERMEDIATE_PRIVATE_KEY',
'INTERMEDIATE_PRIVATE_KEY_PASSPHRASE')
self.assertEqual(
pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status)
def testGenerateDeviceDrmCertificateWithoutInit(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
100, 'DEVICE_PUBLIC_KEY', 'DEVICE_SERIAL_NUMBER')
self.assertEqual(
pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status)
def testNewProvisioningSessionWithoutInit(self):
status, session = self._engine.NewProvisioningSession('DEVICE_PUBLIC_KEY',
'DEVICE_PRIVATE_KEY')
self.assertEqual(
pywrapprovisioning_status.PROVISIONING_ENGINE_UNINITIALIZED, status)
self.assertIsNone(session)
def testInitEngineInvalidServiceDrmCert(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting, 'INVALID_CERT',
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_DRM_CERTIFICATE,
status)
def testInitEngineInvalidServicePrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT, 'INVALID_KEY',
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineWrongServicePrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineInvalidServicePrivateKeyPassphrase(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY, 'INVALID_PASSPHRASE',
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineInvalidDrmCert(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS, 'INVALID_CERT',
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(
pywrapprovisioning_status.INVALID_PROVISIONER_DRM_CERTIFICATE, status)
def testInitEngineInvalidDrmPrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT, 'INVALID_KEY',
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineWrongDrmPrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineInvalidDrmPrivateKeyPassphrase(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
'INVALID_PASSPHRASE',
test_data_utility.PROVISIONER_SPOID_SECRET)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineInvalidSpoidSecret(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
'')
self.assertEqual(pywrapprovisioning_status.INVALID_SPOID_SAUCE, status)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,103 @@
################################################################################
# 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.
################################################################################
import unittest
import crypto_utility
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_utility
from protos.public import certificate_provisioning_pb2
from protos.public import signed_device_certificate_pb2
class NewSessionTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
test_data_utility.InitProvisionEngineWithTestData(
self._engine, verify_success=True)
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
def testNewSessionSuccess(self):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(_, new_session) = test_data_utility.NewProvisioningSessionWithTestData(
self._engine, verify_success=True)
(status, raw_response,
_) = new_session.ProcessMessage(test_data_utility.MESSAGE)
test_data_utility.AssertSuccess(status, 'Failed to create session.')
signed_request = test_data_utility.ConvertToSignedProvisioningMessage(
test_data_utility.MESSAGE)
unsigned_request = certificate_provisioning_pb2.ProvisioningRequest()
unsigned_request.ParseFromString(signed_request.message)
signed_response = test_data_utility.ConvertToSignedProvisioningMessage(
raw_response)
self._VerifyMessageSignature(test_data_utility.SERVICE_PUBLIC_KEY,
signed_response)
unsigned_response = certificate_provisioning_pb2.ProvisioningResponse()
unsigned_response.ParseFromString(signed_response.message)
self._VerifyProvisioningResponse(unsigned_request, unsigned_response)
def testProcessInvalidMessage(self):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(_, new_session
) = test_data_utility.NewProvisioningSessionWithTestData(self._engine)
(status, _, _) = new_session.ProcessMessage('INVALID_MESSAGE')
self.assertEqual(pywrapprovisioning_status.INVALID_REQUEST_MESSAGE, status)
def testNewSessionWithoutIntermediateCert(self):
(_, new_session) = test_data_utility.NewProvisioningSessionWithTestData(
self._engine, verify_success=True)
(status, _, _) = new_session.ProcessMessage(test_data_utility.MESSAGE)
self.assertEqual(pywrapprovisioning_status.MISSING_DRM_INTERMEDIATE_CERT,
status)
def testNewSessionInvalidDevicePublicKey(self):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(session_status, _) = self._engine.NewProvisioningSession(
'INVALID_PUBLIC_KEY', test_data_utility.DEVICE_PRIVATE_KEY)
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
session_status)
def testNewSessionInvalidDevicePrivateKey(self):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(session_status, _) = self._engine.NewProvisioningSession(
test_data_utility.DEVICE_PUBLIC_KEY, 'INVALID_PRIVATE_KEY')
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PRIVATE_KEY,
session_status)
def _VerifyMessageSignature(self, public_key, signed_response):
crypto_utility.VerifySignature(public_key, signed_response.signature,
signed_response.message)
def _VerifyCertSignature(self, public_key, signed_cert):
crypto_utility.VerifySignature(public_key, signed_cert.signature,
signed_cert.drm_certificate)
def _VerifyProvisioningResponse(self, request, response):
self.assertEqual(request.nonce, response.nonce)
signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate()
signed_cert.ParseFromString(response.device_certificate)
self._VerifyCertSignature(test_data_utility.CA_PUBLIC_KEY, signed_cert)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// Swig file to generate a Python library for:
// provisioning_sdk/public/provisioning_engine.h
%module pywrapprovisioning_engine
%include "base.i"
%include "unique_ptr.i"
%import(module="pywrapprovisioning_session") "provisioning_sdk/public/python/provisioning_session.i"
UNIQUE_PTR_ARGOUT(widevine::ProvisioningSession, new_session);
%apply int { CertificateType, ProvisioningStatus };
%apply int32 { int32_t };
%apply uint32 { uint32_t };
%apply std::string* OUTPUT { std::string* certificate };
%{
#include "provisioning_sdk/public/provisioning_engine.h"
#include "provisioning_sdk/public/provisioning_session.h"
using namespace widevine;
%}
%ignoreall
%unignore widevine;
%unignore widevine::ProvisioningSession;
%unignore widevine::ProvisioningEngine;
%unignore widevine::ProvisioningEngine::ProvisioningEngine;
%unignore widevine::ProvisioningEngine::~ProvisioningEngine;
%unignore widevine::ProvisioningEngine::SetCertificateStatusList;
%unignore widevine::ProvisioningEngine::Initialize;
%unignore widevine::ProvisioningEngine::GenerateDrmIntermediateCertificate;
%unignore widevine::ProvisioningEngine::AddDrmIntermediateCertificate;
%unignore widevine::ProvisioningEngine::NewProvisioningSession;
%unignore widevine::ProvisioningEngine::GenerateDeviceDrmCertificate;
%include "provisioning_sdk/public/provisioning_engine.h"
%unignoreall

View File

@@ -0,0 +1,37 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// Swig file to generate a Python library for:
// provisioning_sdk/public/provisioning_session.h
%module pywrapprovisioning_session
%include "base.i"
%apply bool* OUTPUT { bool* done };
%apply int { ProvisioningStatus };
%apply std::string* OUTPUT { std::string* response };
%{
#include "provisioning_sdk/public/provisioning_session.h"
using namespace widevine;
%}
%ignoreall
%unignore widevine;
%unignore widevine::ProvisioningSession;
%unignore widevine::ProvisioningSession::~ProvisioningSession;
%unignore widevine::ProvisioningSession::ProcessMessage;
%include "provisioning_sdk/public/provisioning_session.h"
%unignoreall

View File

@@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
// Swig file to generate a Python library for:
// provisioning_sdk/public/provisioning_status.h
%module pywrapprovisioning_status
%include "base.i"
%{
#include "provisioning_sdk/public/provisioning_status.h"
%}
%ignoreall
%unignore widevine;
%unignore widevine::ProvisioningStatus;
%unignore widevine::OK;
%unignore widevine::PROVISIONING_ENGINE_UNINITIALIZED;
%unignore widevine::INVALID_SERVICE_DRM_CERTIFICATE;
%unignore widevine::INVALID_SERVICE_PRIVATE_KEY;
%unignore widevine::INVALID_PROVISIONER_DRM_CERTIFICATE;
%unignore widevine::INVALID_PROVISIONER_PRIVATE_KEY;
%unignore widevine::INVALID_STATUS_LIST;
%unignore widevine::STATUS_LIST_EXPIRED;
%unignore widevine::UNKNOWN_SYSTEM_ID;
%unignore widevine::INVALID_DEVICE_PUBLIC_KEY;
%unignore widevine::INVALID_DEVICE_PRIVATE_KEY;
%unignore widevine::INVALID_REQUEST_MESSAGE;
%unignore widevine::MISSING_DRM_INTERMEDIATE_CERT;
%unignore widevine::DEVICE_REVOKED;
%unignore widevine::INVALID_SERIAL_NUMBER;
%unignore widevine::INVALID_SPOID_SAUCE;
%unignore widevine::GetProvisioningStatusMessage;
%include "provisioning_sdk/public/provisioning_status.h"
%unignoreall

View File

@@ -0,0 +1,35 @@
################################################################################
# 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.
################################################################################
import unittest
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_utility
class SetCertificateStatusListTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
test_data_utility.InitProvisionEngineWithTestData(
self._engine, verify_success=True)
def testSetCertificateStatusListSuccess(self):
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
def testSetCertificateStatusListInvalid(self):
set_cert_status_list = self._engine.SetCertificateStatusList(
'INVALID_STATUS_LIST', 0)
self.assertEqual(pywrapprovisioning_status.INVALID_STATUS_LIST,
set_cert_status_list)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,62 @@
################################################################################
# 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.
################################################################################
"""setup script to build Python wrappers using swig configurations."""
import os
from distutils import core
OUT_DIRNAME = 'test_genfiles'
def GetSdkRootDir():
"""Obtains folder containing |OUT_DIRNAME| that is considered as root dir."""
current_dir = os.path.realpath(os.path.dirname(__file__))
while not os.path.isdir(os.path.join(current_dir, OUT_DIRNAME)):
current_dir = os.path.dirname(current_dir)
return current_dir
SDK_ROOT_DIR = GetSdkRootDir()
SWIG_CONFIG_FILE = os.path.join(SDK_ROOT_DIR, OUT_DIRNAME, '%s.i')
SWIG_CONFIG_MODULE_PATH = OUT_DIRNAME + '.%s'
SDK_LIBRARY_DIR = os.path.join(SDK_ROOT_DIR, 'bazel-bin', 'provisioning_sdk',
'public')
def ProvisioningSwigExtension(extension_name):
return core.Extension(
name=SWIG_CONFIG_MODULE_PATH % ('_pywrap' + extension_name),
sources=[SWIG_CONFIG_FILE % extension_name],
include_dirs=[SDK_ROOT_DIR],
swig_opts=['-c++'],
library_dirs=[SDK_ROOT_DIR, SDK_LIBRARY_DIR],
runtime_library_dirs=[SDK_ROOT_DIR, SDK_LIBRARY_DIR],
libraries=['provisioning_sdk'],
extra_compile_args=['-std=c++11'])
if __name__ == '__main__':
os.chdir(SDK_ROOT_DIR)
core.setup(
name='provisioning_sdk',
ext_modules=[
ProvisioningSwigExtension('certificate_type'),
ProvisioningSwigExtension('provisioning_status'),
ProvisioningSwigExtension('provisioning_session'),
ProvisioningSwigExtension('provisioning_engine')
],
py_modules=[
SWIG_CONFIG_MODULE_PATH % 'pywrapcertificate_type',
SWIG_CONFIG_MODULE_PATH % 'pywarpprovisioning_status',
SWIG_CONFIG_MODULE_PATH % 'pywrapprovisioning_session',
SWIG_CONFIG_MODULE_PATH % 'pywrapprovisioning_engine'
])

View File

@@ -0,0 +1,151 @@
################################################################################
# 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.
################################################################################
"""Utility class for Provisioning SDK testing."""
import os
import pywrapcertificate_type
import pywrapprovisioning_status
from protos.public import certificate_provisioning_pb2
TEST_DATA_FOLDER = os.path.join('example', 'example_data')
def GetTestData(filename):
current_dir = os.path.realpath(os.path.dirname(__file__))
while not os.path.isdir(os.path.join(current_dir, TEST_DATA_FOLDER)):
current_dir = os.path.dirname(current_dir)
filename = os.path.join(current_dir, TEST_DATA_FOLDER, filename)
with open(filename, 'rb') as data_file:
data = data_file.read()
return data
SERVICE_DRM_CERT = GetTestData('service.cert')
SERVICE_PUBLIC_KEY = GetTestData('service.public')
SERVICE_PRIVATE_KEY = GetTestData('service.encrypted.private')
SERVICE_PRIVATE_KEY_PASS = GetTestData('service.passphrase')
PROVISIONER_DRM_CERT = GetTestData('provisioner.cert')
PROVISIONER_PRIVATE_KEY = GetTestData('provisioner.encrypted.private')
PROVISIONER_PRIVATE_KEY_PASS = GetTestData('provisioner.passphrase')
PROVISIONER_SPOID_SECRET = GetTestData('provisioner.spoid_secret')
CA_PUBLIC_KEY = GetTestData('intermediate.public')
DEVICE_PUBLIC_KEY = GetTestData('user.public')
DEVICE_PRIVATE_KEY = GetTestData('user.private')
MESSAGE = GetTestData('message')
def InitProvisionEngineWithTestData(engine, verify_success=False):
"""Initialize the provisioning engine with sample credentials.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
verify_success: whether to verify that resulting status code equals OK
Returns:
OK on success, or an appropriate error status code otherwise.
"""
status = engine.Initialize(pywrapcertificate_type.kCertTesting,
SERVICE_DRM_CERT, SERVICE_PRIVATE_KEY,
SERVICE_PRIVATE_KEY_PASS, PROVISIONER_DRM_CERT,
PROVISIONER_PRIVATE_KEY,
PROVISIONER_PRIVATE_KEY_PASS,
PROVISIONER_SPOID_SECRET)
if verify_success:
AssertSuccess(status, 'Failed to initialize.')
return status
def SetCertificateStatusListWithTestData(engine,
expiration_period_seconds,
verify_success=False):
"""Set the certificate status list with sample certificate status list.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
expiration_period_seconds: number of seconds until certificate_status_list
expires after its creation time
verify_success: whether to verify that resulting status code equals OK
Returns:
OK on success, or an appropriate error status code otherwise.
"""
certificate_status_list = GetTestData('certificate_list')
status = engine.SetCertificateStatusList(certificate_status_list,
expiration_period_seconds)
if verify_success:
AssertSuccess(status, 'Failed to set certificate status list.')
return status
def AddDrmIntermediateCertificateWithTestData(engine,
system_id,
verify_success=False):
"""Generate an intermediate DRM cert and add it to provisioning engine.
The intermediate DRM certificate is generated with sample public key and
is added to the provisioning engine with sample certificate private key and
passphrase.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
system_id: Widevine system ID for the type of device
verify_success: whether to verify that resulting status code equals OK
Returns:
OK on success, or an appropriate error status code otherwise.
"""
ca_private_key = GetTestData('intermediate.encrypted.private')
ca_private_key_passphrase = GetTestData('intermediate.passphrase')
gen_status, ca_certificate = engine.GenerateDrmIntermediateCertificate(
system_id, CA_PUBLIC_KEY)
AssertSuccess(gen_status, 'Failed to generate intermediate certificate.')
add_ca_status = engine.AddDrmIntermediateCertificate(
ca_certificate, ca_private_key, ca_private_key_passphrase)
if verify_success:
AssertSuccess(add_ca_status, 'Failed to add intermediate certificate.')
return add_ca_status
def NewProvisioningSessionWithTestData(engine, verify_success=False):
"""Create a provisioning session with sample device public and private keys.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
verify_success: whether to verify that resulting status code equals OK
Returns:
status: OK on success, or an appropriate error status code otherwise.
new_session: A new provisioning_session.
"""
status, new_session = engine.NewProvisioningSession(DEVICE_PUBLIC_KEY,
DEVICE_PRIVATE_KEY)
if verify_success:
AssertSuccess(status, 'Failed to create session.')
return (status, new_session)
def AssertSuccess(status, message=None):
"""Assert status equals OK."""
assert pywrapprovisioning_status.OK == status, message
def ConvertToSignedProvisioningMessage(serialized_message):
signed_message = certificate_provisioning_pb2.SignedProvisioningMessage()
signed_message.ParseFromString(serialized_message)
return signed_message

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////////
namespace std {
template <class T> class unique_ptr {};
}
%define _UNIQUE_PTR_TEMPLATE(type)
template <> class std::unique_ptr <type> {};
%enddef
%define UNIQUE_PTR(type)
_UNIQUE_PTR_TEMPLATE(type);
%typemap(out) std::unique_ptr<type> %{
$result = SWIG_NewPointerObj(
SWIG_as_voidptr($1.release()), $descriptor(type*), SWIG_POINTER_OWN);
%}
%enddef
%define UNIQUE_PTR_WITH_ERROR(type, err_str)
_UNIQUE_PTR_TEMPLATE(type);
%typemap(out) std::unique_ptr<type> %{
if ($1) {
$result = SWIG_NewPointerObj(
SWIG_as_voidptr($1.release()), $descriptor(type*), SWIG_POINTER_OWN);
} else {
SWIG_exception(SWIG_ValueError, err_str);
}
%}
%enddef
%define UNIQUE_PTR_ARGOUT(type, arg_name)
_UNIQUE_PTR_TEMPLATE(type)
%typemap(in, numinputs=0) std::unique_ptr<type>* arg_name
(std::unique_ptr<type> temp) %{
$1 = &temp;
%}
%typemap(argout) std::unique_ptr<type>* arg_name %{
%append_output(SWIG_NewPointerObj(SWIG_as_voidptr($1->release()),
$descriptor(type*), SWIG_POINTER_OWN));
%}
%enddef