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( git_repository(
name = "protobuf_repo", name = "protobuf_repo",
remote = "https://github.com/google/protobuf.git", remote = "https://github.com/google/protobuf.git",
tag = "v3.0.0", tag = "v3.2.0",
) )
git_repository( git_repository(

View File

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

View File

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

View File

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

View File

@@ -14,9 +14,12 @@
namespace widevine { namespace widevine {
// Generates a random string. // 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); 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 } // namespace widevine
#endif // COMMON_RANDOM_UTIL_H_ #endif // COMMON_RANDOM_UTIL_H_

View File

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

View File

@@ -17,6 +17,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <cstdint>
#include "base/macros.h" #include "base/macros.h"
#include "openssl/rsa.h" #include "openssl/rsa.h"
@@ -27,6 +28,7 @@ class RsaPublicKey;
class RsaPrivateKey { class RsaPrivateKey {
public: public:
explicit RsaPrivateKey(RSA* key); explicit RsaPrivateKey(RSA* key);
RsaPrivateKey(const RsaPrivateKey&);
virtual ~RsaPrivateKey(); virtual ~RsaPrivateKey();
// Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey.
@@ -55,17 +57,26 @@ class RsaPrivateKey {
// |public_key|. // |public_key|.
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; 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_; } const RSA* key() const { return key_; }
private: private:
RSA* key_; 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 { class RsaPublicKey {
public: public:
explicit RsaPublicKey(RSA* key); explicit RsaPublicKey(RSA* key);
RsaPublicKey(const RsaPublicKey&);
virtual ~RsaPublicKey(); virtual ~RsaPublicKey();
// Create an RsaPublicKey object using a DER encoded PKCS#1 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|. // Return true if the underlying key matches with |public_key|.
virtual bool MatchesPublicKey(const RsaPublicKey& public_key) const; 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_; } const RSA* key() const { return key_; }
private: private:
RSA* key_; 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 { class RsaKeyFactory {
@@ -114,7 +133,8 @@ class RsaKeyFactory {
virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey( virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs1PrivateKey(
const std::string& private_key); 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( virtual std::unique_ptr<RsaPrivateKey> CreateFromPkcs8PrivateKey(
const std::string& private_key, const std::string& private_key_passphrase); const std::string& private_key, const std::string& private_key_passphrase);

View File

@@ -38,6 +38,24 @@ class RsaKeyTest : public ::testing::Test {
RsaKeyFactory factory_; 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, void RsaKeyTest::TestEncryption(std::unique_ptr<RsaPrivateKey> private_key,
std::unique_ptr<RsaPublicKey> public_key) { std::unique_ptr<RsaPublicKey> public_key) {
ASSERT_TRUE(private_key); ASSERT_TRUE(private_key);
@@ -203,6 +221,16 @@ TEST_F(RsaKeyTest, SignAndVerifySha256Pkcs7_2048) {
factory_.CreateFromPkcs1PublicKey(public_key)); 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) { TEST_F(RsaKeyTest, RsaKeyMatch) {
std::unique_ptr<RsaPrivateKey> private_key2( std::unique_ptr<RsaPrivateKey> private_key2(
RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits())); RsaPrivateKey::Create(test_keys_.private_test_key_2_2048_bits()));

View File

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

Binary file not shown.

View File

@@ -15,10 +15,19 @@ py_binary(
srcs = ["oem_certificate.py"], srcs = ["oem_certificate.py"],
) )
py_library(
name = "oem_certificate_generator_helper",
srcs = ["oem_certificate_generator_helper.py"],
deps = [
":oem_certificate",
],
)
py_test( py_test(
name = "oem_certificate_test", name = "oem_certificate_test",
srcs = ["oem_certificate_test.py"], srcs = ["oem_certificate_test.py"],
deps = [ deps = [
":oem_certificate", ":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') return utils.int_from_bytes(os.urandom(16), byteorder='big')
def _build_certificate(subject_name, issuer_name, system_id, not_valid_before, def build_certificate(subject_name, issuer_name, system_id, not_valid_before,
valid_duration, public_key, signing_key, ca): valid_duration, public_key, signing_key, ca):
"""Utility function to build certificate.""" """Utility function to build certificate."""
builder = x509.CertificateBuilder() builder = x509.CertificateBuilder()
builder = builder.subject_name(subject_name).issuer_name(issuer_name) 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') raise ValueError('Root certificate does not match with root private key')
csr = x509.load_pem_x509_csr(args.csr_file.read(), backends.default_backend()) csr = x509.load_pem_x509_csr(args.csr_file.read(), backends.default_backend())
certificate = _build_certificate(csr.subject, root_cert.subject, certificate = build_certificate(csr.subject, root_cert.subject,
args.system_id, args.not_valid_before, args.system_id, args.not_valid_before,
args.valid_duration, args.valid_duration,
csr.public_key(), root_private_key, True) csr.public_key(), root_private_key, True)
args.output_certificate_file.write( args.output_certificate_file.write(
certificate.public_bytes(serialization.Encoding.DER)) certificate.public_bytes(serialization.Encoding.DER))
@@ -282,11 +282,11 @@ def generate_leaf_certificate(args):
format=serialization.PrivateFormat.PKCS8, format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=_get_encryption_algorithm(args.passphrase))) encryption_algorithm=_get_encryption_algorithm(args.passphrase)))
certificate = _build_certificate(subject_name, intermediate_cert.subject, certificate = build_certificate(subject_name, intermediate_cert.subject,
system_id, args.not_valid_before, system_id, args.not_valid_before,
args.valid_duration, args.valid_duration,
leaf_private_key.public_key(), leaf_private_key.public_key(),
intermediate_private_key, False) intermediate_private_key, False)
args.output_certificate_file.write( args.output_certificate_file.write(
X509CertificateChain([certificate, intermediate_cert]).der_bytes()) 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 import backends
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509 import oid from cryptography.x509 import oid
import oem_certificate import oem_certificate
import oem_certificate_generator_helper as oem_cert_gen_helper
class ArgParseObject(object): class ArgParseObject(object):
@@ -31,19 +31,6 @@ class ArgParseObject(object):
class OemCertificateTest(unittest.TestCase): 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): def test_widevine_system_id(self):
system_id = 1234567890123 system_id = 1234567890123
self.assertEqual( self.assertEqual(
@@ -51,7 +38,7 @@ class OemCertificateTest(unittest.TestCase):
system_id) system_id)
def test_generate_csr(self): def test_generate_csr(self):
args = self._setup_csr_args() args = oem_cert_gen_helper.setup_csr_rgs()
oem_certificate.generate_csr(args) oem_certificate.generate_csr(args)
# Verify CSR. # Verify CSR.
csr = x509.load_pem_x509_csr(args.output_csr_file.getvalue(), 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()) private_key.public_key().public_numbers())
def test_generate_csr_with_keysize4096_and_passphrase(self): 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) oem_certificate.generate_csr(args)
private_key = serialization.load_der_private_key( private_key = serialization.load_der_private_key(
args.output_private_key_file.getvalue(), args.output_private_key_file.getvalue(),
@@ -98,49 +86,16 @@ class OemCertificateTest(unittest.TestCase):
self.assertEqual(csr.public_key().public_numbers(), self.assertEqual(csr.public_key().public_numbers(),
private_key.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): 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) oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue() csr_bytes = csr_args.output_csr_file.getvalue()
csr = x509.load_pem_x509_csr(csr_bytes, backends.default_backend()) csr = x509.load_pem_x509_csr(csr_bytes, backends.default_backend())
root_key, root_certificate = self._create_root_certificate_and_key() root_key, root_certificate = (
args = self._setup_intermediate_cert_args(csr_bytes, root_key, oem_cert_gen_helper.create_root_certificate_and_key())
root_certificate) args = oem_cert_gen_helper.setup_intermediate_cert_args(
csr_bytes, root_key, root_certificate)
oem_certificate.generate_intermediate_certificate(args) oem_certificate.generate_intermediate_certificate(args)
cert = x509.load_der_x509_certificate( cert = x509.load_der_x509_certificate(
@@ -162,53 +117,20 @@ class OemCertificateTest(unittest.TestCase):
cert.signature_hash_algorithm) cert.signature_hash_algorithm)
def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self): def test_generate_intermediate_with_cert_mismatch_root_cert_and_key(self):
root_key1, _ = self._create_root_certificate_and_key() root_key1, _ = (
_, root_certificate2 = self._create_root_certificate_and_key() oem_cert_gen_helper.create_root_certificate_and_key())
args = self._setup_intermediate_cert_args('some csr data', root_key1, _, root_certificate2 = oem_cert_gen_helper.create_root_certificate_and_key()
root_certificate2) args = oem_cert_gen_helper.setup_intermediate_cert_args(
'some csr data', root_key1, root_certificate2)
with self.assertRaises(ValueError) as context: with self.assertRaises(ValueError) as context:
oem_certificate.generate_intermediate_certificate(args) oem_certificate.generate_intermediate_certificate(args)
self.assertTrue('certificate does not match' in str(context.exception)) 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): def test_generate_leaf_certificate(self):
intermediate_key_bytes, intermediate_certificate_bytes = ( intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes()) oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args(intermediate_key_bytes, args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_certificate_bytes) intermediate_key_bytes, intermediate_certificate_bytes)
oem_certificate.generate_leaf_certificate(args) oem_certificate.generate_leaf_certificate(args)
certificate_chain = oem_certificate.X509CertificateChain.load_der( certificate_chain = oem_certificate.X509CertificateChain.load_der(
@@ -233,7 +155,7 @@ class OemCertificateTest(unittest.TestCase):
oem_certificate.WidevineSystemId.oid).value.value oem_certificate.WidevineSystemId.oid).value.value
self.assertEqual( self.assertEqual(
oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(), oem_certificate.WidevineSystemId(system_id_raw_bytes).int_value(),
1234554321) 2001)
leaf_key = serialization.load_der_private_key( leaf_key = serialization.load_der_private_key(
args.output_private_key_file.getvalue(), args.output_private_key_file.getvalue(),
@@ -247,8 +169,8 @@ class OemCertificateTest(unittest.TestCase):
def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self): def test_generate_leaf_certificate_with_keysize4096_and_passphrase(self):
intermediate_key_bytes, intermediate_certificate_bytes = ( intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes()) oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args( args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_key_bytes, intermediate_key_bytes,
intermediate_certificate_bytes, intermediate_certificate_bytes,
key_size=4096, key_size=4096,
@@ -261,7 +183,7 @@ class OemCertificateTest(unittest.TestCase):
self.assertEqual(4096, args.key_size) self.assertEqual(4096, args.key_size)
def test_get_csr_info(self): def test_get_csr_info(self):
args = self._setup_csr_args() args = oem_cert_gen_helper.setup_csr_rgs()
oem_certificate.generate_csr(args) oem_certificate.generate_csr(args)
args.file = StringIO.StringIO(args.output_csr_file.getvalue()) args.file = StringIO.StringIO(args.output_csr_file.getvalue())
output = StringIO.StringIO() output = StringIO.StringIO()
@@ -278,7 +200,7 @@ class OemCertificateTest(unittest.TestCase):
def test_get_certificate_info(self): def test_get_certificate_info(self):
_, intermediate_certificate_bytes = ( _, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes()) oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = ArgParseObject() args = ArgParseObject()
args.file = StringIO.StringIO(intermediate_certificate_bytes) args.file = StringIO.StringIO(intermediate_certificate_bytes)
output = StringIO.StringIO() output = StringIO.StringIO()
@@ -293,23 +215,23 @@ class OemCertificateTest(unittest.TestCase):
Issuer Name: Issuer Name:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')> <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
Key Size: 4096 Key Size: 4096
Widevine System Id: 1234554321 Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00 Not valid before: 2001-08-09 00:00:00
Not valid after: 2001-11-17 00:00:00""" Not valid after: 2001-11-17 00:00:00"""
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))
def test_get_certificate_chain_info(self): def test_get_certificate_chain_info(self):
intermediate_key_bytes, intermediate_certificate_bytes = ( intermediate_key_bytes, intermediate_certificate_bytes = (
self._create_intermediate_certificate_and_key_bytes()) oem_cert_gen_helper.create_intermediate_certificate_and_key_bytes())
args = self._setup_leaf_cert_args(intermediate_key_bytes, args = oem_cert_gen_helper.setup_leaf_cert_args(
intermediate_certificate_bytes) intermediate_key_bytes, intermediate_certificate_bytes)
oem_certificate.generate_leaf_certificate(args) oem_certificate.generate_leaf_certificate(args)
args.file = StringIO.StringIO(args.output_certificate_file.getvalue()) args.file = StringIO.StringIO(args.output_certificate_file.getvalue())
output = StringIO.StringIO() output = StringIO.StringIO()
oem_certificate.get_info(args, output) oem_certificate.get_info(args, output)
expected_info = """\ expected_info = """\
Certificate Subject Name: 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.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.8, name=stateOrProvinceName)>, value=u'WA')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value=u'Kirkland')> <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.10, name=organizationName)>, value=u'CompanyXYZ')>
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')> <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value=u'ContentProtection')>
Key Size: 1024 Key Size: 1024
Widevine System Id: 1234554321 Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00 Not valid before: 2001-08-09 00:00:00
Not valid after: 2023-07-05 00:00:00 Not valid after: 2023-07-05 00:00:00
@@ -335,7 +257,7 @@ class OemCertificateTest(unittest.TestCase):
Issuer Name: Issuer Name:
<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')> <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'root_cert')>
Key Size: 4096 Key Size: 4096
Widevine System Id: 1234554321 Widevine System Id: 2001
Not valid before: 2001-08-09 00:00:00 Not valid before: 2001-08-09 00:00:00
Not valid after: 2001-11-17 00:00:00""" Not valid after: 2001-11-17 00:00:00"""
self.assertEqual(output.getvalue(), textwrap.dedent(expected_info)) self.assertEqual(output.getvalue(), textwrap.dedent(expected_info))

View File

@@ -85,8 +85,9 @@ message ProvisioningResponse {
// The message authentication key. // The message authentication key.
message SignedProvisioningMessage { message SignedProvisioningMessage {
enum ProtocolVersion { enum ProtocolVersion {
VERSION_2 = 2; // Keybox factory-provisioned devices. PROVISIONING_20 = 2; // Keybox factory-provisioned devices.
VERSION_3 = 3; // OEM certificate 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. // Serialized ProvisioningRequest or ProvisioningResponse. Required.
@@ -94,5 +95,5 @@ message SignedProvisioningMessage {
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required. // HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
optional bytes signature = 2; optional bytes signature = 2;
// Version number of provisioning protocol. // 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, bool GenerateCertificate(DrmDeviceCertificate::CertificateType type,
uint32_t system_id, uint32_t system_id, const std::string& provider_id,
const std::string& provider_id, const std::string& serial_number, const std::string& public_key,
const std::string& serial_number,
const std::string& public_key,
const RsaPrivateKey& signing_key, const RsaPrivateKey& signing_key,
const SignedDrmDeviceCertificate& signer, const SignedDrmDeviceCertificate& signer,
std::string* certificate) { std::string* certificate) {
@@ -145,10 +143,10 @@ ProvisioningEngineImpl::~ProvisioningEngineImpl() {}
ProvisioningStatus ProvisioningEngineImpl::Initialize( ProvisioningStatus ProvisioningEngineImpl::Initialize(
CertificateType certificate_type, const std::string& drm_service_certificate, CertificateType certificate_type, const std::string& drm_service_certificate,
const std::string& service_private_key, 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_drm_certificate,
const std::string& provisioning_private_key, 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) { const std::string& secret_spoid_sauce) {
if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE; if (!LoadDrmRootPublicKey(certificate_type)) return INVALID_CERTIFICATE_TYPE;
@@ -167,7 +165,7 @@ ProvisioningStatus ProvisioningEngineImpl::Initialize(
rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key()); rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE; if (!service_public_key_) return INVALID_SERVICE_DRM_CERTIFICATE;
service_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey( 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_private_key_) return INVALID_SERVICE_PRIVATE_KEY;
if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) { if (!service_public_key_->MatchesPrivateKey(*service_private_key_)) {
LOG(WARNING) << "Services public key and private key do not match."; 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()); rsa_key_factory_->CreateFromPkcs1PublicKey(drm_cert.public_key());
if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE; if (!provisioning_public_key_) return INVALID_PROVISIONER_DRM_CERTIFICATE;
provisioning_private_key_ = rsa_key_factory_->CreateFromPkcs8PrivateKey( 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_private_key_) return INVALID_PROVISIONER_PRIVATE_KEY;
if (!provisioning_public_key_->MatchesPrivateKey( if (!provisioning_public_key_->MatchesPrivateKey(
*provisioning_private_key_)) { *provisioning_private_key_)) {

View File

@@ -72,8 +72,7 @@ class ProvisioningEngineImpl {
// (creation_time_seconds). Zero means it will never expire. // (creation_time_seconds). Zero means it will never expire.
// * Returns OK on success, or an appropriate error status code otherwise. // * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus SetCertificateStatusList( ProvisioningStatus SetCertificateStatusList(
const std::string& certificate_status_list, const std::string& certificate_status_list, uint32_t expiration_period_seconds);
uint32_t expiration_period_seconds);
// Generate an intermediate DRM certificate. // Generate an intermediate DRM certificate.
// * |system_id| is the Widevine system ID for the type of device. // * |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; const std::string& certificate_serial_number, std::string* certificate) const;
// Get the device info for the given |system_id|. // Get the device info for the given |system_id|.
std::shared_ptr<ProvisionedDeviceInfo> GetDeviceInfo( std::shared_ptr<ProvisionedDeviceInfo> GetDeviceInfo(uint32_t system_id) const;
uint32_t system_id) const;
// Returns the service private key. // Returns the service private key.
const RsaPrivateKey* service_private_key() const { const RsaPrivateKey* service_private_key() const {

View File

@@ -18,11 +18,11 @@
#include "common/sha_util.h" #include "common/sha_util.h"
#include "provisioning_sdk/public/provisioning_status.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."); "parameter for LOG_EVERY_N to help abate log spamming.");
#define LOG_EVERY_N_WITH_PROTO(message, proto) \ #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() << "]" << (message) << " [proto: " << (proto).ShortDebugString() << "]"
namespace widevine { namespace widevine {
@@ -42,8 +42,10 @@ ProvisioningStatus ProvisioningSessionImpl::Initialize(
auto rsa_public_key = auto rsa_public_key =
rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key); rsa_key_factory_->CreateFromPkcs1PublicKey(device_public_key);
if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY; if (!rsa_public_key) return INVALID_DEVICE_PUBLIC_KEY;
auto rsa_private_key = // Use empty std::string to indicate the private key is not encrypted.
rsa_key_factory_->CreateFromPkcs1PrivateKey(device_private_key); 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_private_key) return INVALID_DEVICE_PRIVATE_KEY;
if (!rsa_public_key->MatchesPrivateKey(*rsa_private_key)) { if (!rsa_public_key->MatchesPrivateKey(*rsa_private_key)) {
LOG(WARNING) << "Device public key and private key do not match."; 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, const std::string& message, SignedProvisioningMessage* signed_request,
ProvisioningRequest* request) const { ProvisioningRequest* request) const {
if (!signed_request->ParseFromString(message)) { 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."; << "Failed to parse SignedProvisioningMessage.";
return false; return false;
} }

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ class ProvisioningSession;
class ProvisioningEngine { class ProvisioningEngine {
public: public:
ProvisioningEngine(); ProvisioningEngine();
~ProvisioningEngine(); virtual ~ProvisioningEngine();
// Initializes the provisioning engine with required credentials. // Initializes the provisioning engine with required credentials.
// * |certificate_type| indicates which type of certificate chains will be // * |certificate_type| indicates which type of certificate chains will be
@@ -66,7 +66,7 @@ class ProvisioningEngine {
// |certificate_status_list| expires after its creation time // |certificate_status_list| expires after its creation time
// (creation_time_seconds). Zero means it will never expire. // (creation_time_seconds). Zero means it will never expire.
// * Returns OK on success, or an appropriate error status code otherwise. // * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus SetCertificateStatusList( virtual ProvisioningStatus SetCertificateStatusList(
const std::string& certificate_status_list, const std::string& certificate_status_list,
uint32_t expiration_period_seconds); uint32_t expiration_period_seconds);
@@ -94,7 +94,7 @@ class ProvisioningEngine {
// * |cert_private_key_passphrase| is the passphrase for cert_private_key, // * |cert_private_key_passphrase| is the passphrase for cert_private_key,
// if any. // if any.
// * Returns OK on success, or an appropriate error status code otherwise. // * Returns OK on success, or an appropriate error status code otherwise.
ProvisioningStatus AddDrmIntermediateCertificate( virtual ProvisioningStatus AddDrmIntermediateCertificate(
const std::string& intermediate_cert, const std::string& intermediate_cert,
const std::string& cert_private_key, const std::string& cert_private_key,
const std::string& cert_private_key_passphrase); const std::string& cert_private_key_passphrase);
@@ -113,7 +113,7 @@ class ProvisioningEngine {
// message. // message.
// NOTE: All ProvisioningSession objects must be deleted before the // NOTE: All ProvisioningSession objects must be deleted before the
// ProvisioningEngine which created them. // ProvisioningEngine which created them.
ProvisioningStatus NewProvisioningSession( virtual ProvisioningStatus NewProvisioningSession(
const std::string& device_public_key, const std::string& device_public_key,
const std::string& device_private_key, const std::string& device_private_key,
std::unique_ptr<ProvisioningSession>* new_session) const; std::unique_ptr<ProvisioningSession>* new_session) const;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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