Export provisioning sdk
Change-Id: I4d47d80444c9507f84896767dc676112ca11e901
This commit is contained in:
112
provisioning_sdk/internal/BUILD
Normal file
112
provisioning_sdk/internal/BUILD
Normal 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",
|
||||
],
|
||||
)
|
||||
117
provisioning_sdk/internal/certificates/BUILD
Normal file
117
provisioning_sdk/internal/certificates/BUILD
Normal 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/$@",
|
||||
)
|
||||
BIN
provisioning_sdk/internal/certificates/backwards-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/backwards-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_dev.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_dev.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_prod.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_prod.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/drm_ca_root_test.cert
Normal file
BIN
provisioning_sdk/internal/certificates/drm_ca_root_test.cert
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/expired-2000-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/expired-2000-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/invalid_chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/invalid_chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der
Normal file
BIN
provisioning_sdk/internal/certificates/oem_ca_root_dev_cert.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der
Normal file
BIN
provisioning_sdk/internal/certificates/oem_ca_root_prod_cert.der
Normal file
Binary file not shown.
38
provisioning_sdk/internal/certificates/root_certificates.cc
Normal file
38
provisioning_sdk/internal/certificates/root_certificates.cc
Normal 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
|
||||
56
provisioning_sdk/internal/certificates/root_certificates.h
Normal file
56
provisioning_sdk/internal/certificates/root_certificates.h
Normal 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_
|
||||
|
||||
BIN
provisioning_sdk/internal/certificates/single-cert-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/single-cert-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/sysid-2001-chain.der
Normal file
BIN
provisioning_sdk/internal/certificates/sysid-2001-chain.der
Normal file
Binary file not shown.
BIN
provisioning_sdk/internal/certificates/sysid-2001-public-key.der
Normal file
BIN
provisioning_sdk/internal/certificates/sysid-2001-public-key.der
Normal file
Binary file not shown.
40
provisioning_sdk/internal/certificates/test_certificates.cc
Normal file
40
provisioning_sdk/internal/certificates/test_certificates.cc
Normal 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
|
||||
63
provisioning_sdk/internal/certificates/test_certificates.h
Normal file
63
provisioning_sdk/internal/certificates/test_certificates.h
Normal 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_
|
||||
210
provisioning_sdk/internal/oem_device_cert.cc
Normal file
210
provisioning_sdk/internal/oem_device_cert.cc
Normal 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
|
||||
56
provisioning_sdk/internal/oem_device_cert.h
Normal file
56
provisioning_sdk/internal/oem_device_cert.h
Normal 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_
|
||||
95
provisioning_sdk/internal/oem_device_cert_test.cc
Normal file
95
provisioning_sdk/internal/oem_device_cert_test.cc
Normal 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
|
||||
464
provisioning_sdk/internal/provisioning_engine_impl.cc
Normal file
464
provisioning_sdk/internal/provisioning_engine_impl.cc
Normal 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
|
||||
189
provisioning_sdk/internal/provisioning_engine_impl.h
Normal file
189
provisioning_sdk/internal/provisioning_engine_impl.h
Normal 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_
|
||||
783
provisioning_sdk/internal/provisioning_engine_impl_test.cc
Normal file
783
provisioning_sdk/internal/provisioning_engine_impl_test.cc
Normal 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
|
||||
256
provisioning_sdk/internal/provisioning_session_impl.cc
Normal file
256
provisioning_sdk/internal/provisioning_session_impl.cc
Normal 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
|
||||
88
provisioning_sdk/internal/provisioning_session_impl.h
Normal file
88
provisioning_sdk/internal/provisioning_session_impl.h
Normal 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_
|
||||
426
provisioning_sdk/internal/provisioning_session_impl_test.cc
Normal file
426
provisioning_sdk/internal/provisioning_session_impl_test.cc
Normal 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
|
||||
Reference in New Issue
Block a user