NewProvisioningSession expects pkcs8 private key and SHA race fix

-------------
Fix SHA hashing to remove race condition. This change
fixes the implementation by passing in the digest buffer.

-------------
The input to ProvisioningEngine::NewProvisioningSession should be
pkcs8 private key instead of pkcs1 private key

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=151273394

Change-Id: Ibcdff7757b2ac2878ee8b1b88365083964bfa10a
This commit is contained in:
Kongqun Yang
2017-03-26 15:26:46 -07:00
parent 187d13a5c3
commit 84f66d2320
33 changed files with 620 additions and 310 deletions

View File

@@ -3,7 +3,7 @@ workspace(name = "provisioning_sdk")
git_repository(
name = "protobuf_repo",
remote = "https://github.com/google/protobuf.git",
tag = "v3.0.0",
tag = "v3.2.0",
)
git_repository(

View File

@@ -132,6 +132,7 @@ cc_library(
srcs = ["sha_util.cc"],
hdrs = ["sha_util.h"],
deps = [
"//base",
"//external:openssl",
],
)

View File

@@ -10,6 +10,7 @@
#include <vector>
#include <cstdint>
#include "glog/logging.h"
#include "openssl/aes.h"

View File

@@ -5,7 +5,9 @@
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
//
// RAII wrapper classes for cleaning up various OpenSSL dynamically allocated
// structures.
#ifndef COMMON_OPENSSL_UTIL_H__
#define COMMON_OPENSSL_UTIL_H__
@@ -42,6 +44,7 @@ template <typename StackType>
using ScopedOpenSSLStackOnly =
std::unique_ptr<StackType, OpenSSLStackOnlyDeleter<StackType>>;
using ScopedBIGNUM = ScopedOpenSSLType<BIGNUM, BN_free>;
using ScopedBIO = ScopedOpenSSLType<BIO, BIO_vfree>;
using ScopedPKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
using ScopedRSA = ScopedOpenSSLType<RSA, RSA_free>;
@@ -54,10 +57,14 @@ using ScopedX509NameEntry =
using ScopedX509Store = ScopedOpenSSLType<X509_STORE, X509_STORE_free>;
using ScopedX509StoreCtx =
ScopedOpenSSLType<X509_STORE_CTX, X509_STORE_CTX_free>;
using ScopedX509Req = ScopedOpenSSLType<X509_REQ, X509_REQ_free>;
using ScopedAsn1UtcTime = ScopedOpenSSLType<ASN1_UTCTIME, ASN1_UTCTIME_free>;
using ScopedAsn1Utc8String =
ScopedOpenSSLType<ASN1_UTF8STRING, ASN1_UTF8STRING_free>;
using ScopedAsn1Integer = ScopedOpenSSLType<ASN1_INTEGER, ASN1_INTEGER_free>;
using ScopedAsn1Object = ScopedOpenSSLType<ASN1_OBJECT, ASN1_OBJECT_free>;
using ScopedAsn1OctetString =
ScopedOpenSSLType<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>;
// XxxStack deallocates the stack and its members while XxxStackOnly deallocates
// the stack only.

View File

@@ -19,4 +19,9 @@ bool RandomBytes(size_t num_bytes, std::string* output) {
return RAND_bytes(reinterpret_cast<uint8_t*>(&(*output)[0]), num_bytes);
}
std::string Random16Bytes() {
std::string output;
CHECK(RandomBytes(16u, &output));
return output;
}
} // namespace widevine

View File

@@ -14,9 +14,12 @@
namespace widevine {
// Generates a random string.
// Return true on success, false otherwise.
// Returns true on success, false otherwise.
bool RandomBytes(size_t num_bytes, std::string* output);
// Returns a 16-byte std::string suitable for use as an AES key
std::string Random16Bytes();
} // namespace widevine
#endif // COMMON_RANDOM_UTIL_H_

View File

@@ -51,6 +51,9 @@ namespace widevine {
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
: key_(CHECK_NOTNULL(RSAPrivateKey_dup(rsa_key.key_))) {}
RsaPrivateKey::~RsaPrivateKey() { RSA_free(key_); }
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
@@ -135,16 +138,12 @@ bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message,
}
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(message.data()),
message.size(),
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
digest);
unsigned int sig_len = RSA_size(key_);
signature->resize(sig_len);
return RSA_sign(NID_sha256,
digest,
sizeof(digest),
reinterpret_cast<unsigned char*>(&(*signature)[0]),
&sig_len,
return RSA_sign(NID_sha256, digest, sizeof(digest),
reinterpret_cast<unsigned char*>(&(*signature)[0]), &sig_len,
key_) == 1;
}
@@ -156,8 +155,13 @@ bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
return RsaKeyMatch(key(), public_key.key());
}
uint32_t RsaPrivateKey::KeySize() const { return RSA_size(key_); }
RsaPublicKey::RsaPublicKey(RSA* key) : key_(CHECK_NOTNULL(key)) {}
RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
: key_(CHECK_NOTNULL(RSAPublicKey_dup(rsa_key.key_))) {}
RsaPublicKey::~RsaPublicKey() { RSA_free(key_); }
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
@@ -222,15 +226,20 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
std::string message_digest = Sha1_Hash(message);
// Verify PSS padding.
return RSA_verify_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
EVP_sha1(), EVP_sha1(),
reinterpret_cast<unsigned char*>(&padded_digest[0]),
kPssSaltLength) != 0;
if (RSA_verify_PKCS1_PSS_mgf1(
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
EVP_sha1(), EVP_sha1(),
reinterpret_cast<unsigned char*>(&padded_digest[0]),
kPssSaltLength) == 0) {
LOG(ERROR) << "RSA Verify PSS padding failure: "
<< ERR_error_string(ERR_get_error(), nullptr);
return false;
}
return true;
}
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
const std::string& message, const std::string& signature) const {
bool RsaPublicKey::VerifySignatureSha256Pkcs7(const std::string& message,
const std::string& signature) const {
if (message.empty()) {
LOG(ERROR) << "Empty signature verification message";
return false;
@@ -245,15 +254,11 @@ bool RsaPublicKey::VerifySignatureSha256Pkcs7(
return false;
}
unsigned char digest[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(message.data()),
message.size(),
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
digest);
return RSA_verify(NID_sha256,
digest,
sizeof(digest),
return RSA_verify(NID_sha256, digest, sizeof(digest),
reinterpret_cast<const unsigned char*>(signature.data()),
signature.size(),
key_) == 1;
signature.size(), key_) == 1;
}
bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const {
@@ -264,6 +269,8 @@ bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
return RsaKeyMatch(key(), public_key.key());
}
uint32_t RsaPublicKey::KeySize() const { return RSA_size(key_); }
RsaKeyFactory::RsaKeyFactory() {}
RsaKeyFactory::~RsaKeyFactory() {}

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <cstdint>
#include "base/macros.h"
#include "openssl/rsa.h"
@@ -27,6 +28,7 @@ class RsaPublicKey;
class RsaPrivateKey {
public:
explicit RsaPrivateKey(RSA* key);
RsaPrivateKey(const RsaPrivateKey&);
virtual ~RsaPrivateKey();
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
@@ -55,17 +57,26 @@ class RsaPrivateKey {
// |public_key|.
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
const RSA* key() const { return key_; }
private:
RSA* key_;
DISALLOW_COPY_AND_ASSIGN(RsaPrivateKey);
// SWIG appears to think this declaration is a syntax error. Excluding it for
// python SWIG wrapping.
#ifndef SWIG
// Disallow assignment operator.
RsaPrivateKey& operator=(const RsaPrivateKey&) = delete;
#endif // SWIG
};
class RsaPublicKey {
public:
explicit RsaPublicKey(RSA* key);
RsaPublicKey(const RsaPublicKey&);
virtual ~RsaPublicKey();
// Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey.
@@ -97,12 +108,20 @@ class RsaPublicKey {
// Return true if the underlying key matches with |public_key|.
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const;
// Returns the RSA key size (modulus) in bytes.
virtual uint32_t KeySize() const;
const RSA* key() const { return key_; }
private:
RSA* key_;
DISALLOW_COPY_AND_ASSIGN(RsaPublicKey);
// SWIG appears to think this declaration is a syntax error. Excluding it for
// python SWIG wrapping.
#ifndef SWIG
// Disallow assignment operator.
RsaPublicKey& operator=(const RsaPublicKey&) = delete;
#endif // SWIG
};
class RsaKeyFactory {
@@ -114,7 +133,8 @@ class RsaKeyFactory {
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
const std::string& private_key);
// Create an RsaPrivateKey object using an encrypted PKCS#8 RSAPrivateKey.
// Create a PKCS#1 RsaPrivateKey object using an PKCS#8 PrivateKeyInfo or
// EncryptedPrivateKeyInfo (if |private_key_passprhase| is not empty).
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase);

View File

@@ -38,6 +38,24 @@ class RsaKeyTest : public ::testing::Test {
RsaKeyFactory factory_;
};
TEST_F(RsaKeyTest, CopyConstructor) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits()));
std::unique_ptr<RsaPrivateKey> private_key_copy(
new RsaPrivateKey(*private_key));
std::unique_ptr<RsaPublicKey> public_key_copy(
new RsaPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPublicKey(*public_key));
EXPECT_TRUE(public_key_copy->MatchesPrivateKey(*private_key));
EXPECT_TRUE(private_key_copy->MatchesPublicKey(*public_key));
EXPECT_TRUE(private_key_copy->MatchesPrivateKey(*private_key));
}
void RsaKeyTest::TestEncryption(std::unique_ptr<RsaPrivateKey> private_key,
std::unique_ptr<RsaPublicKey> public_key) {
ASSERT_TRUE(private_key);
@@ -203,6 +221,16 @@ TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) {
factory_.CreateFromPkcs1PublicKey(public_key));
}
TEST_F(RsaKeyTest, KeySize) {
std::unique_ptr<RsaPrivateKey> private_key(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));
std::unique_ptr<RsaPublicKey> public_key(
RsaPublicKey::Create(test_keys_.public_test_key_2_2048_bits()));
EXPECT_EQ(256, private_key->KeySize());
EXPECT_EQ(256, public_key->KeySize());
}
TEST_F(RsaKeyTest, RsaKeyMatch) {
std::unique_ptr<RsaPrivateKey> private_key2(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));

View File

@@ -8,22 +8,25 @@
#include "common/sha_util.h"
#include <cstdint>
#include "openssl/sha.h"
namespace widevine {
std::string Sha1_Hash(const std::string& message) {
return std::string(reinterpret_cast<char*>(
SHA1(reinterpret_cast<const uint8_t*>(message.data()),
message.size(), nullptr)),
SHA_DIGEST_LENGTH);
std::string digest;
digest.resize(SHA_DIGEST_LENGTH);
SHA1(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
reinterpret_cast<uint8_t*>(&digest[0]));
return digest;
}
std::string Sha256_Hash(const std::string& message) {
return std::string(reinterpret_cast<char*>(
SHA256(reinterpret_cast<const uint8_t*>(message.data()),
message.size(), nullptr)),
SHA256_DIGEST_LENGTH);
std::string digest;
digest.resize(SHA256_DIGEST_LENGTH);
SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(),
reinterpret_cast<uint8_t*>(&digest[0]));
return digest;
}
} // namespace widevine

Binary file not shown.

View File

@@ -15,10 +15,19 @@ py_binary(
srcs = ["oem_certificate.py"],
)
py_library(
name = "oem_certificate_generator_helper",
srcs = ["oem_certificate_generator_helper.py"],
deps = [
":oem_certificate",
],
)
py_test(
name = "oem_certificate_test",
srcs = ["oem_certificate_test.py"],
deps = [
":oem_certificate",
":oem_certificate_generator_helper",
],
)

View File

@@ -196,8 +196,8 @@ def _random_serial_number():
return utils.int_from_bytes(os.urandom(16), byteorder='big')
def _build_certificate(subject_name, issuer_name, system_id, not_valid_before,
valid_duration, public_key, signing_key, ca):
def build_certificate(subject_name, issuer_name, system_id, not_valid_before,
valid_duration, public_key, signing_key, ca):
"""Utility function to build certificate."""
builder = x509.CertificateBuilder()
builder = builder.subject_name(subject_name).issuer_name(issuer_name)
@@ -237,10 +237,10 @@ def generate_intermediate_certificate(args):
raise ValueError('Root certificate does not match with root private key')
csr = x509.load_pem_x509_csr(args.csr_file.read(), backends.default_backend())
certificate = _build_certificate(csr.subject, root_cert.subject,
args.system_id, args.not_valid_before,
args.valid_duration,
csr.public_key(), root_private_key, True)
certificate = build_certificate(csr.subject, root_cert.subject,
args.system_id, args.not_valid_before,
args.valid_duration,
csr.public_key(), root_private_key, True)
args.output_certificate_file.write(
certificate.public_bytes(serialization.Encoding.DER))
@@ -282,11 +282,11 @@ def generate_leaf_certificate(args):
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=_get_encryption_algorithm(args.passphrase)))
certificate = _build_certificate(subject_name, intermediate_cert.subject,
system_id, args.not_valid_before,
args.valid_duration,
leaf_private_key.public_key(),
intermediate_private_key, False)
certificate = build_certificate(subject_name, intermediate_cert.subject,
system_id, args.not_valid_before,
args.valid_duration,
leaf_private_key.public_key(),
intermediate_private_key, False)
args.output_certificate_file.write(
X509CertificateChain([certificate, intermediate_cert]).der_bytes())

View File

@@ -0,0 +1,154 @@
################################################################################
# Copyright 2017 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.
################################################################################
"""Common utility functions for OEM certificate generation."""
import datetime
import StringIO
from cryptography import x509
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509 import oid
import oem_certificate
_COUNTRY_NAME = 'US'
_STATE_OR_PROVINCE_NAME = 'WA'
_LOCALITY_NAME = 'Kirkland'
_ORGANIZATION_NAME = 'CompanyXYZ'
_ORGANIZATIONAL_UNIT_NAME = 'ContentProtection'
_NOT_VALID_BEFORE = datetime.datetime(2001, 8, 9)
_VALID_DURATION = 100
_LEAF_CERT_VALID_DURATION = 8000
_SYSTEM_ID = 2001
_ROOT_PRIVATE_KEY_PASSPHRASE = 'root_passphrase'
class ArgParseObject(object):
"""A convenient object to allow adding arbitrary attribute to it."""
def create_root_certificate_and_key():
"""Creates a root certificate and key."""
key = rsa.generate_private_key(
public_exponent=65537,
key_size=3072,
backend=backends.default_backend())
subject_name = x509.Name(
[x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')])
certificate = oem_certificate.build_certificate(
subject_name, subject_name, None,
datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True)
return (key, certificate)
def setup_csr_rgs(country_name=_COUNTRY_NAME,
state_or_province_name=_STATE_OR_PROVINCE_NAME,
locality_name=_LOCALITY_NAME,
organization_name=_ORGANIZATION_NAME,
organizational_unit_name=_ORGANIZATIONAL_UNIT_NAME,
key_size=4096,
output_csr_file=None,
output_private_key_file=None,
passphrase=None):
"""Sets up arguments to OEM Certificate generator for generating csr."""
args = ArgParseObject()
args.key_size = key_size
args.country_name = country_name
args.state_or_province_name = state_or_province_name
args.locality_name = locality_name
args.organization_name = organization_name
args.organizational_unit_name = organizational_unit_name
if output_csr_file:
args.output_csr_file = output_csr_file
else:
args.output_csr_file = StringIO.StringIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
return args
def setup_intermediate_cert_args(
csr_bytes, root_key, root_certificate, not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_VALID_DURATION, system_id=_SYSTEM_ID,
root_private_key_passphrase=_ROOT_PRIVATE_KEY_PASSPHRASE,
output_certificate_file=None):
"""Sets up args to OEM Cert generator for generating intermediate cert."""
args = ArgParseObject()
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.system_id = system_id
args.csr_file = StringIO.StringIO(csr_bytes)
args.root_private_key_passphrase = root_private_key_passphrase
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = StringIO.StringIO()
serialized_private_key = root_key.private_bytes(
serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(
args.root_private_key_passphrase))
serialized_certificate = root_certificate.public_bytes(
serialization.Encoding.DER)
args.root_certificate_file = StringIO.StringIO(serialized_certificate)
args.root_private_key_file = StringIO.StringIO(serialized_private_key)
return args
def setup_leaf_cert_args(intermediate_key_bytes,
intermediate_certificate_bytes,
key_size=1024,
passphrase=None,
not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_LEAF_CERT_VALID_DURATION,
output_certificate_file=None,
output_private_key_file=None):
"""Sets up args to OEM Certificate generator for generating leaf cert."""
args = ArgParseObject()
args.key_size = key_size
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.intermediate_private_key_passphrase = None
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = StringIO.StringIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
args.intermediate_private_key_file = StringIO.StringIO(
intermediate_key_bytes)
args.intermediate_certificate_file = StringIO.StringIO(
intermediate_certificate_bytes)
return args
def create_intermediate_certificate_and_key_bytes(key_size=4096,
passphrase=None):
"""Creates an intermediate certificate and key."""
csr_args = setup_csr_rgs(key_size=key_size, passphrase=passphrase)
oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue()
root_key, root_certificate = create_root_certificate_and_key()
args = setup_intermediate_cert_args(csr_bytes, root_key, root_certificate)
oem_certificate.generate_intermediate_certificate(args)
return (csr_args.output_private_key_file.getvalue(),
args.output_certificate_file.getvalue())

View File

@@ -18,10 +18,10 @@ from cryptography import x509
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509 import oid
import oem_certificate
import oem_certificate_generator_helper as oem_cert_gen_helper
class ArgParseObject(object):
@@ -31,19 +31,6 @@ class ArgParseObject(object):
class OemCertificateTest(unittest.TestCase):
def _setup_csr_args(self, key_size=4096, passphrase=None):
args = ArgParseObject()
args.key_size = key_size
args.country_name = 'US'
args.state_or_province_name = 'WA'
args.locality_name = 'Kirkland'
args.organization_name = 'CompanyXYZ'
args.organizational_unit_name = 'ContentProtection'
args.output_csr_file = StringIO.StringIO()
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
return args
def test_widevine_system_id(self):
system_id = 1234567890123
self.assertEqual(
@@ -51,7 +38,7 @@ class OemCertificateTest(unittest.TestCase):
system_id)
def test_generate_csr(self):
args = self._setup_csr_args()
args = oem_cert_gen_helper.setup_csr_rgs()
oem_certificate.generate_csr(args)
# Verify CSR.
csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(),
@@ -84,7 +71,8 @@ class OemCertificateTest(unittest.TestCase):
private_key.public_key().public_numbers())
def test_generate_csr_with_keysize4096_and_passphrase(self):
args = self._setup_csr_args(key_size=4096, passphrase='passphrase_4096')
args = oem_cert_gen_helper.setup_csr_rgs(
key_size=4096, passphrase='passphrase_4096')
oem_certificate.generate_csr(args)
private_key = serialization.load_der_private_key(
args.output_private_key_file.getvalue(),
@@ -98,49 +86,16 @@ class OemCertificateTest(unittest.TestCase):
self.assertEqual(csr.public_key().public_numbers(),
private_key.public_key().public_numbers())
def _create_root_certificate_and_key(self):
key = rsa.generate_private_key(
public_exponent=65537,
key_size=3072,
backend=backends.default_backend())
subject_name = x509.Name(
[x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')])
certificate = oem_certificate._build_certificate(
subject_name, subject_name, None,
datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True)
return (key, certificate)
def _setup_intermediate_cert_args(self, csr_bytes, root_key,
root_certificate):
args = ArgParseObject()
args.not_valid_before = datetime.datetime(2001, 8, 9)
args.valid_duration = 100
args.system_id = 1234554321
args.csr_file = StringIO.StringIO(csr_bytes)
args.root_private_key_passphrase = 'root_passphrase'
args.output_certificate_file = StringIO.StringIO()
serialized_private_key = root_key.private_bytes(
serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(
args.root_private_key_passphrase))
serialized_certificate = root_certificate.public_bytes(
serialization.Encoding.DER)
args.root_certificate_file = StringIO.StringIO(serialized_certificate)
args.root_private_key_file = StringIO.StringIO(serialized_private_key)
return args
def test_generate_intermediate_certificate(self):
csr_args = self._setup_csr_args()
csr_args = oem_cert_gen_helper.setup_csr_rgs()
oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue()
csr = x509.load_pem_x509_csr(csr_bytes, backends.default_backend())
root_key, root_certificate = self._create_root_certificate_and_key()
args = self._setup_intermediate_cert_args(csr_bytes, root_key,
root_certificate)
root_key, root_certificate = (
oem_cert_gen_helper.create_root_certificate_and_key())
args = oem_cert_gen_helper.setup_intermediate_cert_args(
csr_bytes, root_key, root_certificate)
oem_certificate.generate_intermediate_certificate(args)
cert = x509.load_der_x509_certificate(
@@ -162,53 +117,20 @@ class OemCertificateTest(unittest.TestCase):
cert.signature_hash_algorithm)
def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self):
root_key1, _ = self._create_root_certificate_and_key()
_, root_certificate2 = self._create_root_certificate_and_key()
args = self._setup_intermediate_cert_args('some csr data', root_key1,
root_certificate2)
root_key1, _ = (
oem_cert_gen_helper.create_root_certificate_and_key())
_, root_certificate2 = oem_cert_gen_helper.create_root_certificate_and_key()
args = oem_cert_gen_helper.setup_intermediate_cert_args(
'some csr data', root_key1, root_certificate2)
with self.assertRaises(ValueError) as context:
oem_certificate.generate_intermediate_certificate(args)
self.assertTrue('certificate does not match' in str(context.exception))
def _setup_leaf_cert_args(self,
intermediate_key_bytes,
intermediate_certificate_bytes,
key_size=1024,
passphrase=None):
args = ArgParseObject()
args.key_size = key_size
args.not_valid_before = datetime.datetime(2001, 8, 9)
args.valid_duration = 8000
args.intermediate_private_key_passphrase = None
args.output_certificate_file = StringIO.StringIO()
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
args.intermediate_private_key_file = StringIO.StringIO(
intermediate_key_bytes)
args.intermediate_certificate_file = StringIO.StringIO(
intermediate_certificate_bytes)
return args
def _create_intermediate_certificate_and_key_bytes(self,
key_size=4096,
passphrase=None):
csr_args = self._setup_csr_args(key_size, passphrase)
oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue()
root_key, root_certificate = self._create_root_certificate_and_key()
args = self._setup_intermediate_cert_args(csr_bytes, root_key,
root_certificate)
oem_certificate.generate_intermediate_certificate(args)
return (csr_args.output_private_key_file.getvalue(),
args.output_certificate_file.getvalue())
def test_generate_leaf_certificate(self):
intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args(intermediate_key_bytes,
intermediate_certificate_bytes)
oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_key_bytes, intermediate_certificate_bytes)
oem_certificate.generate_leaf_certificate(args)
certificate_chain = oem_certificate.X509CertificateChain.load_der(
@@ -233,7 +155,7 @@ class OemCertificateTest(unittest.TestCase):
oem_certificate.WidevineSystemId.oid).value.value
self.assertEqual(
oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(),
1234554321)
2001)
leaf_key = serialization.load_der_private_key(
args.output_private_key_file.getvalue(),
@@ -247,8 +169,8 @@ class OemCertificateTest(unittest.TestCase):
def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self):
intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args(
oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_key_bytes,
intermediate_certificate_bytes,
key_size=4096,
@@ -261,7 +183,7 @@ class OemCertificateTest(unittest.TestCase):
self.assertEqual(4096, args.key_size)
def test_get_csr_info(self):
args = self._setup_csr_args()
args = oem_cert_gen_helper.setup_csr_rgs()
oem_certificate.generate_csr(args)
args.file = StringIO.StringIO(args.output_csr_file.getvalue())
output = StringIO.StringIO()
@@ -278,7 +200,7 @@ class OemCertificateTest(unittest.TestCase):
def test_get_certificate_info(self):
_, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes())
oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = ArgParseObject()
args.file = StringIO.StringIO(intermediate_certificate_bytes)
output = StringIO.StringIO()
@@ -293,23 +215,23 @@ class OemCertificateTest(unittest.TestCase):
Issuer Name:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
Key Size: 4096
Widevine System Id: 1234554321
Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00
Not valid after: 2001-11-17 00:00:00"""
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))
def test_get_certificate_chain_info(self):
intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args(intermediate_key_bytes,
intermediate_certificate_bytes)
oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_key_bytes, intermediate_certificate_bytes)
oem_certificate.generate_leaf_certificate(args)
args.file = StringIO.StringIO(args.output_certificate_file.getvalue())
output = StringIO.StringIO()
oem_certificate.get_info(args, output)
expected_info = """\
Certificate Subject Name:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'1234554321-leaf')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'2001-leaf')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value=u'WA')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')>
@@ -322,7 +244,7 @@ class OemCertificateTest(unittest.TestCase):
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value=u'CompanyXYZ')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
Key Size: 1024
Widevine System Id: 1234554321
Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00
Not valid after: 2023-07-05 00:00:00
@@ -335,7 +257,7 @@ class OemCertificateTest(unittest.TestCase):
Issuer Name:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
Key Size: 4096
Widevine System Id: 1234554321
Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00
Not valid after: 2001-11-17 00:00:00"""
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))

View File

@@ -85,8 +85,9 @@ message ProvisioningResponse {
// The message authentication key.
message SignedProvisioningMessage {
enum ProtocolVersion {
VERSION_2 = 2; // Keybox factory-provisioned devices.
VERSION_3 = 3; // OEM certificate factory-provisioned devices.
PROVISIONING_20 = 2; // Keybox factory-provisioned devices.
PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices.
INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol.
}
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
@@ -94,5 +95,5 @@ message SignedProvisioningMessage {
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
optional bytes signature = 2;
// Version number of provisioning protocol.
optional ProtocolVersion protocol_version = 3 [default = VERSION_2];
optional ProtocolVersion protocol_version = 3 [default = PROVISIONING_20];
}

View File

@@ -77,10 +77,8 @@ bool VerifyAndExtractCertificate(const RsaPublicKey* public_key,
}
bool GenerateCertificate(DrmDeviceCertificate::CertificateType type,
uint32_t system_id,
const std::string& provider_id,
const std::string& serial_number,
const std::string& public_key,
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) {
@@ -145,10 +143,10 @@ 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& service_private_key_passphrase,
const std::string& provisioning_drm_certificate,
const std::string& provisioning_private_key,
const std::string& provisioning_private_key_phassphrase,
const std::string& provisioning_private_key_passphrase,
const std::string& secret_spoid_sauce) {
if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE;
@@ -167,7 +165,7 @@ ProvisioningStatus ProvisioningEngineImpl::Initialize(
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);
service_private_key, service_private_key_passphrase);
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.";
@@ -189,7 +187,7 @@ ProvisioningStatus ProvisioningEngineImpl::Initialize(
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);
provisioning_private_key, provisioning_private_key_passphrase);
if (!provisioning_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY;
if (!provisioning_public_key_->MatchesPrivateKey(
*provisioning_private_key_)) {

View File

@@ -72,8 +72,7 @@ class ProvisioningEngineImpl {
// (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);
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.
@@ -129,8 +128,7 @@ class ProvisioningEngineImpl {
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;
std::shared_ptr<ProvisionedDeviceInfo> GetDeviceInfo(uint32_t system_id) const;
// Returns the service private key.
const RsaPrivateKey* service_private_key() const {

View File

@@ -18,11 +18,11 @@
#include "common/sha_util.h"
#include "provisioning_sdk/public/provisioning_status.h"
DEFINE_int32(provisioning_log_every_n, 1,
DEFINE_int32(prov_sdk_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) \
LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) \
<< (message) << " [proto: " << (proto).ShortDebugString() << "]"
namespace widevine {
@@ -42,8 +42,10 @@ ProvisioningStatus ProvisioningSessionImpl::Initialize(
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);
// Use empty std::string to indicate the private key is not encrypted.
const std::string kClearPkcs8PrivateKeyPassphrase;
auto rsa_private_key = rsa_key_factory_->CreateFromPkcs8PrivateKey(
device_private_key, kClearPkcs8PrivateKeyPassphrase);
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.";
@@ -141,7 +143,7 @@ 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)
LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n)
<< "Failed to parse SignedProvisioningMessage.";
return false;
}

View File

@@ -19,6 +19,7 @@
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::IsEmpty;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
@@ -113,7 +114,7 @@ TEST_F(ProvisioningSessionImplTest, InitializeWithInvalidPrivateKey) {
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(new MockRsaPublicKey))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty()))
.WillOnce(Return(ByMove(nullptr)));
EXPECT_EQ(
INVALID_DEVICE_PRIVATE_KEY,
@@ -127,7 +128,7 @@ TEST_F(ProvisioningSessionImplTest, InitializeWithMismatchPublicPrivateKey) {
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty()))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))
@@ -146,7 +147,7 @@ class ProvisioningSessionImplProcessTest : public ProvisioningSessionImplTest {
.WillOnce(
Return(ByMove(std::unique_ptr<RsaPublicKey>(mock_rsa_public_key))));
EXPECT_CALL(*mock_rsa_key_factory_,
CreateFromPkcs1PrivateKey(kDevicePrivateKey))
CreateFromPkcs8PrivateKey(kDevicePrivateKey, IsEmpty()))
.WillOnce(Return(
ByMove(std::unique_ptr<RsaPrivateKey>(new MockRsaPrivateKey))));
EXPECT_CALL(*mock_rsa_public_key, MatchesPrivateKey(_))

View File

@@ -33,6 +33,7 @@ cc_library(
name = "libprovisioning_sdk",
srcs = [":libprovisioning_sdk.so"],
hdrs = glob(["*.h"]),
deps = ["//base"],
)
cc_library(

View File

@@ -29,7 +29,7 @@ class ProvisioningSession;
class ProvisioningEngine {
public:
ProvisioningEngine();
~ProvisioningEngine();
virtual ~ProvisioningEngine();
// Initializes the provisioning engine with required credentials.
// * |certificate_type| indicates which type of certificate chains will be
@@ -66,7 +66,7 @@ class ProvisioningEngine {
// |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(
virtual ProvisioningStatus SetCertificateStatusList(
const std::string& certificate_status_list,
uint32_t expiration_period_seconds);
@@ -94,7 +94,7 @@ class ProvisioningEngine {
// * |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(
virtual ProvisioningStatus AddDrmIntermediateCertificate(
const std::string& intermediate_cert,
const std::string& cert_private_key,
const std::string& cert_private_key_passphrase);
@@ -113,7 +113,7 @@ class ProvisioningEngine {
// message.
// NOTE: All ProvisioningSession objects must be deleted before the
// ProvisioningEngine which created them.
ProvisioningStatus NewProvisioningSession(
virtual ProvisioningStatus NewProvisioningSession(
const std::string& device_public_key,
const std::string& device_private_key,
std::unique_ptr<ProvisioningSession>* new_session) const;

View File

@@ -16,6 +16,8 @@
namespace widevine {
ProvisioningSession::ProvisioningSession() {}
ProvisioningSession::~ProvisioningSession() {}
ProvisioningStatus ProvisioningSession::ProcessMessage(const std::string& message,

View File

@@ -22,7 +22,7 @@ class ProvisioningSessionImpl;
// Class which is used to implement the provisioning session state machine.
class ProvisioningSession {
public:
~ProvisioningSession();
virtual ~ProvisioningSession();
// Process a message from the client device.
// * |message| is the message received from the client device.
@@ -31,13 +31,16 @@ class ProvisioningSession {
// * |done| will indicate, upon successful return, whether the provisioning
// exchange is complete, and the ProvisioningSession can be deleted.
// Returns OK if successful, or an appropriate error status code otherwise.
ProvisioningStatus ProcessMessage(const std::string& message,
std::string* response,
bool* done);
virtual ProvisioningStatus ProcessMessage(const std::string& message,
std::string* response,
bool* done);
// * Returns a ProvisioneddeviceInfo message containing information about the
// type of device being provisioned. May return nullptr.
const ProvisionedDeviceInfo* GetDeviceInfo() const;
virtual const ProvisionedDeviceInfo* GetDeviceInfo() const;
protected:
ProvisioningSession(); // To enable mocking.
private:
#ifndef SWIGPYTHON

View File

@@ -18,8 +18,13 @@ filegroup(
py_library(
name = "test_data_utility",
srcs = ["test_data_utility.py"],
data = ["//example:example_data"],
srcs = [
"test_data_provider.py",
"test_data_utility.py",
],
data = [
"//example:example_data",
],
deps = [
"//protos/public:certificate_provisioning_py_pb2",
],

View File

@@ -23,5 +23,5 @@ typedef oldtype newtype;
%apply oldtype & INOUT { newtype & INOUT };
%enddef
COPY_TYPEMAPS(int, int32);
COPY_TYPEMAPS(unsigned int, uint32);
COPY_TYPEMAPS(int, int32_t);
COPY_TYPEMAPS(unsigned int, uint32_t);

View File

@@ -8,6 +8,8 @@
"""Utility functions for cryptography."""
import logging
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
@@ -18,6 +20,7 @@ def VerifySignature(public_key, signature, data):
hash_algorithm = hashes.SHA1()
salt_len = 20
logging.info('Verying signature.')
key = serialization.load_der_public_key(
public_key, backend=backends.default_backend())
key.verify(signature, data,

View File

@@ -9,8 +9,10 @@
import unittest
import crypto_utility
import pywrapcertificate_type
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_provider
import test_data_utility
from protos.public import signed_device_certificate_pb2
@@ -25,21 +27,23 @@ class EngineGenerateCertificateTest(unittest.TestCase):
self._engine, 0, verify_success=True)
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
self._data_provider = test_data_provider.TestDataProvider(
pywrapcertificate_type.kCertTesting)
def testSuccess(self):
status, signed_cert_string = self._engine.GenerateDeviceDrmCertificate(
2001, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
2001, self._data_provider.device_public_key, 'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.OK, status)
signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate()
signed_cert.ParseFromString(signed_cert_string)
crypto_utility.VerifySignature(test_data_utility.CA_PUBLIC_KEY,
crypto_utility.VerifySignature(self._data_provider.ca_public_key,
signed_cert.signature,
signed_cert.drm_certificate)
def testEmptySerialNumber(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2001, test_data_utility.DEVICE_PUBLIC_KEY, '')
2001, self._data_provider.device_public_key, '')
self.assertEqual(pywrapprovisioning_status.INVALID_SERIAL_NUMBER, status)
def testEmptyPublicKey(self):
@@ -57,7 +61,7 @@ class EngineGenerateCertificateTest(unittest.TestCase):
def testMissingIntermediateCertificate(self):
status, _ = self._engine.GenerateDeviceDrmCertificate(
2002, test_data_utility.DEVICE_PUBLIC_KEY, 'DEVICE_SERIAL_NUMBER')
2002, self._data_provider.device_public_key, 'DEVICE_SERIAL_NUMBER')
self.assertEqual(pywrapprovisioning_status.DEVICE_REVOKED, status)
if __name__ == '__main__':

View File

@@ -11,6 +11,7 @@ import unittest
import pywrapcertificate_type
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_provider
import test_data_utility
@@ -18,6 +19,8 @@ class InitEngineTest(unittest.TestCase):
def setUp(self):
self._engine = pywrapprovisioning_engine.ProvisioningEngine()
self._data_provider = test_data_provider.TestDataProvider(
pywrapcertificate_type.kCertTesting)
def testInitEngineSucceed(self):
test_data_utility.InitProvisionEngineWithTestData(
@@ -58,112 +61,111 @@ class InitEngineTest(unittest.TestCase):
def testInitEngineInvalidServiceDrmCert(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting, 'INVALID_CERT',
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_DRM_CERTIFICATE,
status)
def testInitEngineInvalidServicePrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT, 'INVALID_KEY',
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert, 'INVALID_KEY',
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineWrongServicePrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineInvalidServicePrivateKeyPassphrase(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY, 'INVALID_PASSPHRASE',
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert,
self._data_provider.service_private_key, 'INVALID_PASSPHRASE',
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_SERVICE_PRIVATE_KEY,
status)
def testInitEngineInvalidDrmCert(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS, 'INVALID_CERT',
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert,
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase, 'INVALID_CERT',
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(
pywrapprovisioning_status.INVALID_PROVISIONER_DRM_CERTIFICATE, status)
def testInitEngineInvalidDrmPrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT, 'INVALID_KEY',
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert,
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert, 'INVALID_KEY',
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineWrongDrmPrivateKey(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.service_drm_cert,
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.service_private_key,
self._data_provider.provisioner_private_key_passphrase,
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineInvalidDrmPrivateKeyPassphrase(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
self._data_provider.service_drm_cert,
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key_passphrase,
'INVALID_PASSPHRASE',
test_data_utility.PROVISIONER_SPOID_SECRET)
self._data_provider.provisioner_spoid_secret)
self.assertEqual(pywrapprovisioning_status.INVALID_PROVISIONER_PRIVATE_KEY,
status)
def testInitEngineInvalidSpoidSecret(self):
status = self._engine.Initialize(
pywrapcertificate_type.kCertTesting,
test_data_utility.SERVICE_DRM_CERT,
test_data_utility.SERVICE_PRIVATE_KEY,
test_data_utility.SERVICE_PRIVATE_KEY_PASS,
test_data_utility.PROVISIONER_DRM_CERT,
test_data_utility.PROVISIONER_PRIVATE_KEY,
test_data_utility.PROVISIONER_PRIVATE_KEY_PASS,
'')
self._data_provider.service_drm_cert,
self._data_provider.service_private_key,
self._data_provider.service_private_key_passphrase,
self._data_provider.provisioner_drm_cert,
self._data_provider.provisioner_private_key,
self._data_provider.provisioner_private_key_passphrase, '')
self.assertEqual(pywrapprovisioning_status.INVALID_SPOID_SAUCE, status)
if __name__ == '__main__':

View File

@@ -9,8 +9,10 @@
import unittest
import crypto_utility
import pywrapcertificate_type
import pywrapprovisioning_engine
import pywrapprovisioning_status
import test_data_provider
import test_data_utility
from protos.public import certificate_provisioning_pb2
from protos.public import signed_device_certificate_pb2
@@ -24,6 +26,8 @@ class NewSessionTest(unittest.TestCase):
self._engine, verify_success=True)
test_data_utility.SetCertificateStatusListWithTestData(
self._engine, 0, verify_success=True)
self._data_provider = test_data_provider.TestDataProvider(
pywrapcertificate_type.kCertTesting)
def testNewSessionSuccess(self):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
@@ -32,11 +36,11 @@ class NewSessionTest(unittest.TestCase):
(_, new_session) = test_data_utility.NewProvisioningSessionWithTestData(
self._engine, verify_success=True)
(status, raw_response,
_) = new_session.ProcessMessage(test_data_utility.MESSAGE)
_) = new_session.ProcessMessage(self._data_provider.message)
test_data_utility.AssertSuccess(status, 'Failed to create session.')
signed_request = test_data_utility.ConvertToSignedProvisioningMessage(
test_data_utility.MESSAGE)
self._data_provider.message)
unsigned_request = certificate_provisioning_pb2.ProvisioningRequest()
unsigned_request.ParseFromString(signed_request.message)
@@ -44,7 +48,7 @@ class NewSessionTest(unittest.TestCase):
signed_response = test_data_utility.ConvertToSignedProvisioningMessage(
raw_response)
self._VerifyMessageSignature(test_data_utility.SERVICE_PUBLIC_KEY,
self._VerifyMessageSignature(self._data_provider.service_public_key,
signed_response)
unsigned_response = certificate_provisioning_pb2.ProvisioningResponse()
@@ -63,7 +67,8 @@ class NewSessionTest(unittest.TestCase):
def testNewSessionWithoutIntermediateCert(self):
(_, new_session) = test_data_utility.NewProvisioningSessionWithTestData(
self._engine, verify_success=True)
(status, _, _) = new_session.ProcessMessage(test_data_utility.MESSAGE)
(status, _, _) = new_session.ProcessMessage(
self._data_provider.message)
self.assertEqual(pywrapprovisioning_status.MISSING_DRM_INTERMEDIATE_CERT,
status)
@@ -71,7 +76,7 @@ class NewSessionTest(unittest.TestCase):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(session_status, _) = self._engine.NewProvisioningSession(
'INVALID_PUBLIC_KEY', test_data_utility.DEVICE_PRIVATE_KEY)
'INVALID_PUBLIC_KEY', self._data_provider.device_private_key)
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PUBLIC_KEY,
session_status)
@@ -79,7 +84,7 @@ class NewSessionTest(unittest.TestCase):
test_data_utility.AddDrmIntermediateCertificateWithTestData(
self._engine, 2001, verify_success=True)
(session_status, _) = self._engine.NewProvisioningSession(
test_data_utility.DEVICE_PUBLIC_KEY, 'INVALID_PRIVATE_KEY')
self._data_provider.device_public_key, 'INVALID_PRIVATE_KEY')
self.assertEqual(pywrapprovisioning_status.INVALID_DEVICE_PRIVATE_KEY,
session_status)
@@ -97,7 +102,8 @@ class NewSessionTest(unittest.TestCase):
signed_cert = signed_device_certificate_pb2.SignedDrmDeviceCertificate()
signed_cert.ParseFromString(response.device_certificate)
self._VerifyCertSignature(test_data_utility.CA_PUBLIC_KEY, signed_cert)
self._VerifyCertSignature(self._data_provider.ca_public_key,
signed_cert)
if __name__ == '__main__':
unittest.main()

View File

@@ -18,9 +18,6 @@
UNIQUE_PTR_ARGOUT(widevine::ProvisioningSession, new_session);
%apply int { CertificateType, ProvisioningStatus };
%apply int32 { int32_t };
%apply uint32 { uint32_t };
%apply std::string* OUTPUT { std::string* certificate };
%{

View File

@@ -0,0 +1,101 @@
################################################################################
# Copyright 2017 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.
################################################################################
"""Class that provides test data for Provisioning SDK testing."""
import os
import pywrapcertificate_type
_TEST_CERT_DATA_FOLDER = os.path.join('example', 'example_data')
_DEV_CERT_DATA_FOLDER = os.path.join('example', 'dev_cert_example_data')
class TestDataProvider(object):
"""For for Test Data."""
def __init__(self, cert_type):
"""Initializes the TestData for Provisioning SDK tests."""
assert (cert_type in (
pywrapcertificate_type.kCertDevelopment,
pywrapcertificate_type.kCertTesting))
self._cert_type = cert_type
def _GetTestData(self, filename):
"""Helps read test data files such as certs and keys for SDK testing."""
current_dir = os.path.realpath(os.path.dirname(__file__))
if self._cert_type == pywrapcertificate_type.kCertDevelopment:
subfolder_path = _DEV_CERT_DATA_FOLDER
elif self._cert_type == pywrapcertificate_type.kCertTesting:
subfolder_path = _TEST_CERT_DATA_FOLDER
while not os.path.isdir(os.path.join(current_dir, subfolder_path)):
current_dir = os.path.dirname(current_dir)
filename = os.path.join(current_dir, subfolder_path, filename)
with open(filename, 'rb') as data_file:
data = data_file.read()
return data
@property
def service_drm_cert(self):
return self._GetTestData('service.cert')
@property
def service_public_key(self):
return self._GetTestData('service.public')
@property
def service_private_key(self):
return self._GetTestData('service.encrypted.private')
@property
def service_private_key_passphrase(self):
return self._GetTestData('service.passphrase')
@property
def provisioner_drm_cert(self):
return self._GetTestData('provisioner.cert')
@property
def provisioner_private_key(self):
return self._GetTestData('provisioner.encrypted.private')
@property
def provisioner_private_key_passphrase(self):
return self._GetTestData('provisioner.passphrase')
@property
def provisioner_spoid_secret(self):
return self._GetTestData('provisioner.spoid_secret')
@property
def ca_public_key(self):
return self._GetTestData('intermediate.public')
@property
def ca_private_key(self):
return self._GetTestData('intermediate.encrypted.private')
@property
def ca_private_key_passphrase(self):
return self._GetTestData('intermediate.passphrase')
@property
def device_public_key(self):
return self._GetTestData('user.public')
@property
def device_private_key(self):
return self._GetTestData('user.private')
@property
def message(self):
return self._GetTestData('message')
@property
def certificate_list(self):
return self._GetTestData('certificate_list')

View File

@@ -8,63 +8,48 @@
"""Utility class for Provisioning SDK testing."""
import os
import logging
import pywrapcertificate_type
import pywrapprovisioning_status
import test_data_provider
from protos.public import certificate_provisioning_pb2
TEST_DATA_FOLDER = os.path.join('example', 'example_data')
logging.basicConfig(level=logging.DEBUG)
def GetTestData(filename):
current_dir = os.path.realpath(os.path.dirname(__file__))
while not os.path.isdir(os.path.join(current_dir, TEST_DATA_FOLDER)):
current_dir = os.path.dirname(current_dir)
filename = os.path.join(current_dir, TEST_DATA_FOLDER, filename)
with open(filename, 'rb') as data_file:
data = data_file.read()
return data
SERVICE_DRM_CERT = GetTestData('service.cert')
SERVICE_PUBLIC_KEY = GetTestData('service.public')
SERVICE_PRIVATE_KEY = GetTestData('service.encrypted.private')
SERVICE_PRIVATE_KEY_PASS = GetTestData('service.passphrase')
PROVISIONER_DRM_CERT = GetTestData('provisioner.cert')
PROVISIONER_PRIVATE_KEY = GetTestData('provisioner.encrypted.private')
PROVISIONER_PRIVATE_KEY_PASS = GetTestData('provisioner.passphrase')
PROVISIONER_SPOID_SECRET = GetTestData('provisioner.spoid_secret')
CA_PUBLIC_KEY = GetTestData('intermediate.public')
DEVICE_PUBLIC_KEY = GetTestData('user.public')
DEVICE_PRIVATE_KEY = GetTestData('user.private')
MESSAGE = GetTestData('message')
def InitProvisionEngineWithTestData(engine, verify_success=False):
def InitProvisionEngineWithTestData(
engine, verify_success=False,
cert_type=pywrapcertificate_type.kCertTesting):
"""Initialize the provisioning engine with sample credentials.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
verify_success: whether to verify that resulting status code equals OK
cert_type: The type of certificate to use for initializing SDK -
{kCertTesting/kCertDevelopment}
Returns:
OK on success, or an appropriate error status code otherwise.
"""
status = engine.Initialize(pywrapcertificate_type.kCertTesting,
SERVICE_DRM_CERT, SERVICE_PRIVATE_KEY,
SERVICE_PRIVATE_KEY_PASS, PROVISIONER_DRM_CERT,
PROVISIONER_PRIVATE_KEY,
PROVISIONER_PRIVATE_KEY_PASS,
PROVISIONER_SPOID_SECRET)
logging.info('Initializing provisioning engine with test data.')
data_provider = test_data_provider.TestDataProvider(cert_type)
status = engine.Initialize(cert_type,
data_provider.service_drm_cert,
data_provider.service_private_key,
data_provider.service_private_key_passphrase,
data_provider.provisioner_drm_cert,
data_provider.provisioner_private_key,
data_provider.provisioner_private_key_passphrase,
data_provider.provisioner_spoid_secret)
if verify_success:
AssertSuccess(status, 'Failed to initialize.')
return status
def SetCertificateStatusListWithTestData(engine,
expiration_period_seconds,
verify_success=False):
def SetCertificateStatusListWithTestData(
engine, expiration_period_seconds, verify_success=False,
cert_type=pywrapcertificate_type.kCertTesting):
"""Set the certificate status list with sample certificate status list.
Args:
@@ -72,11 +57,15 @@ def SetCertificateStatusListWithTestData(engine,
expiration_period_seconds: number of seconds until certificate_status_list
expires after its creation time
verify_success: whether to verify that resulting status code equals OK
cert_type: The type of certificate to use for initializing SDK -
{kCertTesting/kCertDevelopment}
Returns:
OK on success, or an appropriate error status code otherwise.
"""
certificate_status_list = GetTestData('certificate_list')
logging.info('Setting certificate status list with test data.')
data_provider = test_data_provider.TestDataProvider(cert_type)
certificate_status_list = data_provider.certificate_list
status = engine.SetCertificateStatusList(certificate_status_list,
expiration_period_seconds)
@@ -87,9 +76,9 @@ def SetCertificateStatusListWithTestData(engine,
return status
def AddDrmIntermediateCertificateWithTestData(engine,
system_id,
verify_success=False):
def AddDrmIntermediateCertificateWithTestData(
engine, system_id, verify_success=False,
cert_type=pywrapcertificate_type.kCertTesting):
"""Generate an intermediate DRM cert and add it to provisioning engine.
The intermediate DRM certificate is generated with sample public key and
@@ -100,19 +89,23 @@ def AddDrmIntermediateCertificateWithTestData(engine,
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
system_id: Widevine system ID for the type of device
verify_success: whether to verify that resulting status code equals OK
cert_type: The type of certificate to use for initializing SDK -
{kCertTesting/kCertDevelopment}
Returns:
OK on success, or an appropriate error status code otherwise.
"""
ca_private_key = GetTestData('intermediate.encrypted.private')
ca_private_key_passphrase = GetTestData('intermediate.passphrase')
logging.info(
'Generating DRM intermediate certificate for system_id <%d>.', system_id)
data_provider = test_data_provider.TestDataProvider(cert_type)
gen_status, ca_certificate = engine.GenerateDrmIntermediateCertificate(
system_id, CA_PUBLIC_KEY)
system_id, data_provider.ca_public_key)
AssertSuccess(gen_status, 'Failed to generate intermediate certificate.')
logging.info('Adding DRM intermediate certificate.')
add_ca_status = engine.AddDrmIntermediateCertificate(
ca_certificate, ca_private_key, ca_private_key_passphrase)
ca_certificate, data_provider.ca_private_key,
data_provider.ca_private_key_passphrase)
if verify_success:
AssertSuccess(add_ca_status, 'Failed to add intermediate certificate.')
@@ -120,24 +113,57 @@ def AddDrmIntermediateCertificateWithTestData(engine,
return add_ca_status
def NewProvisioningSessionWithTestData(engine, verify_success=False):
def GenerateDeviceDrmCertificate(engine, system_id, serial_number,
verify_success=False,
cert_type=pywrapcertificate_type.kCertTesting):
"""Generate a device DRM certificate.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
system_id: Widevine system ID for the type of device
serial_number: The serial number for the device DRM certificate.
verify_success: whether to verify that resulting status code equals OK
cert_type: The type of certificate to use for initializing SDK -
{kCertTesting/kCertDevelopment}
Returns:
OK on success, or an appropriate error status code otherwise.
"""
logging.info(
'Generating Device cert for system_id <%d> and serial_number <%s>.',
system_id, serial_number)
data_provider = test_data_provider.TestDataProvider(cert_type)
gen_status, ca_certificate = engine.GenerateDeviceDrmCertificate(
system_id, data_provider.device_public_key, serial_number)
if verify_success:
AssertSuccess(gen_status, 'Failed to generate device DRM certificate.')
return ca_certificate
def NewProvisioningSessionWithTestData(
engine, verify_success=False,
cert_type=pywrapcertificate_type.kCertTesting):
"""Create a provisioning session with sample device public and private keys.
Args:
engine: a pywrapprovisioning_engine.ProvisioningEngine instance
verify_success: whether to verify that resulting status code equals OK
cert_type: The type of certificate to use for initializing SDK -
{kCertTesting/kCertDevelopment}
Returns:
status: OK on success, or an appropriate error status code otherwise.
new_session: A new provisioning_session.
"""
status, new_session = engine.NewProvisioningSession(DEVICE_PUBLIC_KEY,
DEVICE_PRIVATE_KEY)
logging.info('Starting a new provisioning session with'
'sample device public and private keys.')
data_provider = test_data_provider.TestDataProvider(cert_type)
status, new_session = engine.NewProvisioningSession(
data_provider.device_public_key, data_provider.device_private_key)
if verify_success:
AssertSuccess(status, 'Failed to create session.')
return (status, new_session)
return status, new_session
def AssertSuccess(status, message=None):