Rollback
Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=224206719
This commit is contained in:
committed by
Fang Yu
parent
df7566c0c1
commit
7f649cf826
@@ -6,23 +6,34 @@
|
||||
// widevine-licensing@google.com.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// common_typos_disable. Successful / successfull.
|
||||
|
||||
#include "common/drm_root_certificate.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "common/certificate_type.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "common/error_space.h"
|
||||
#include "common/rsa_key.h"
|
||||
#include "common/sha_util.h"
|
||||
#include "protos/public/drm_certificate.pb.h"
|
||||
#include "protos/public/errors.pb.h"
|
||||
#include "protos/public/signed_drm_certificate.pb.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// From common::TestCertificates.
|
||||
namespace {
|
||||
|
||||
const char kDevelopmentString[] = "dev"; // QA systems.
|
||||
const char kProductionString[] = "prod"; // Production.
|
||||
const char kTestingString[] = "test"; // Code development / unit tests.
|
||||
|
||||
const bool kUseCache = true;
|
||||
|
||||
// From common::TestDrmCertificates.
|
||||
// TODO(user): common::test_certificates is a testonly target, consider
|
||||
// how to use instead of dupliciating the test cert here.
|
||||
static const unsigned char kTestRootCertificate[] = {
|
||||
@@ -231,14 +242,145 @@ static const unsigned char kProdRootCertificate[] = {
|
||||
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd, 0xc4, 0x46, 0xf5, 0xa3,
|
||||
0x62, 0x13, 0x74, 0x02, 0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
|
||||
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5, 0x02, 0x0c};
|
||||
} // namespace
|
||||
|
||||
util::Status DrmRootCertificate::Create(
|
||||
const std::string& signed_drm_certificate,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
// Caches an individual signature for a certificate with a specific serial
|
||||
// number (signer).
|
||||
struct VerifiedCertSignature {
|
||||
VerifiedCertSignature(const std::string& cert, const std::string& sig,
|
||||
const std::string& signer_sn)
|
||||
: signed_cert(cert), signature(sig), signer_serial(signer_sn) {}
|
||||
|
||||
std::string signed_cert;
|
||||
std::string signature;
|
||||
std::string signer_serial;
|
||||
};
|
||||
|
||||
// Map of certificate serial number to its signature.
|
||||
typedef std::map<std::string, VerifiedCertSignature> VerifiedCertSignatures;
|
||||
class VerifiedCertSignatureCache {
|
||||
public:
|
||||
explicit VerifiedCertSignatureCache(const RsaKeyFactory* key_factory)
|
||||
: key_factory_(key_factory) {}
|
||||
|
||||
// Checks cache, on miss, uses public key. If successful, adds to
|
||||
// cache.
|
||||
util::Status VerifySignature(const std::string& cert, const std::string& serial_number,
|
||||
const std::string& signature,
|
||||
const std::string& signer_public_key,
|
||||
const std::string& signer_serial_number) {
|
||||
{
|
||||
VerifiedCertSignatures::iterator cached_signature;
|
||||
absl::ReaderMutexLock read_lock(&signature_cache_mutex_);
|
||||
cached_signature = signature_cache_.find(serial_number);
|
||||
if (cached_signature != signature_cache_.end()) {
|
||||
// TODO(user): Log which of the following three conditions occurs.
|
||||
if ((cert != cached_signature->second.signed_cert) ||
|
||||
(signature != cached_signature->second.signature) ||
|
||||
(signer_serial_number != cached_signature->second.signer_serial)) {
|
||||
// Cached signature mismatch.
|
||||
return util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cached-signature-mismatch");
|
||||
}
|
||||
// Cached signature match.
|
||||
return util::OkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Cache miss. Verify signature.
|
||||
std::unique_ptr<RsaPublicKey> signer_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer_public_key));
|
||||
if (!signer_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-public-key");
|
||||
}
|
||||
if (!signer_key->VerifySignature(cert, signature)) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
|
||||
// Add signature to cache.
|
||||
absl::WriterMutexLock write_lock(&signature_cache_mutex_);
|
||||
signature_cache_.emplace(
|
||||
serial_number,
|
||||
VerifiedCertSignature(cert, signature, signer_serial_number));
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
VerifiedCertSignatures signature_cache_ GUARDED_BY(&signature_cache_mutex_);
|
||||
absl::Mutex signature_cache_mutex_;
|
||||
const RsaKeyFactory* key_factory_;
|
||||
};
|
||||
|
||||
util::Status DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
return Create(cert_type, absl::make_unique<RsaKeyFactory>(), cert);
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, util::Status* status) {
|
||||
CHECK(status);
|
||||
|
||||
std::unique_ptr<DrmRootCertificate> new_root_cert;
|
||||
*status = CreateByType(cert_type, &new_root_cert);
|
||||
return new_root_cert;
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::CreateByTypeString(
|
||||
const std::string& cert_type_string, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
|
||||
CertificateType cert_type;
|
||||
if (cert_type_string == kDevelopmentString) {
|
||||
cert_type = kCertificateTypeDevelopment;
|
||||
} else if (cert_type_string == kProductionString) {
|
||||
cert_type = kCertificateTypeProduction;
|
||||
} else if (cert_type_string == kTestingString) {
|
||||
cert_type = kCertificateTypeTesting;
|
||||
} else {
|
||||
return util::Status(
|
||||
error_space, INVALID_PARAMETER,
|
||||
absl::StrCat("invalid-certificate-type ", cert_type_string));
|
||||
}
|
||||
|
||||
return CreateByType(cert_type, cert);
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::Create(
|
||||
CertificateType cert_type, std::unique_ptr<RsaKeyFactory> key_factory,
|
||||
std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
DCHECK(cert);
|
||||
|
||||
std::string serialized_certificate;
|
||||
switch (cert_type) {
|
||||
case kCertificateTypeProduction: {
|
||||
serialized_certificate.assign(
|
||||
kProdRootCertificate,
|
||||
kProdRootCertificate + sizeof(kProdRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeDevelopment: {
|
||||
serialized_certificate.assign(
|
||||
kDevRootCertificate,
|
||||
kDevRootCertificate + sizeof(kDevRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeTesting: {
|
||||
serialized_certificate.assign(
|
||||
kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return util::Status(error_space, INVALID_PARAMETER,
|
||||
"invalid-certificate-type");
|
||||
}
|
||||
|
||||
SignedDrmCertificate signed_root_cert;
|
||||
if (!signed_root_cert.ParseFromString(signed_drm_certificate)) {
|
||||
if (!signed_root_cert.ParseFromString(serialized_certificate)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"signed-root-cert-deserialize-fail");
|
||||
}
|
||||
@@ -259,8 +401,9 @@ util::Status DrmRootCertificate::Create(
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-root-certificate-signature");
|
||||
}
|
||||
|
||||
std::unique_ptr<RsaPublicKey> public_key(
|
||||
RsaPublicKey::Create(root_cert.public_key()));
|
||||
key_factory->CreateFromPkcs1PublicKey(root_cert.public_key()));
|
||||
if (!public_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-public-key");
|
||||
@@ -270,51 +413,123 @@ util::Status DrmRootCertificate::Create(
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-root-certificate-signature");
|
||||
}
|
||||
cert->reset(new DrmRootCertificate(root_cert.public_key()));
|
||||
|
||||
cert->reset(new DrmRootCertificate(
|
||||
cert_type, serialized_certificate, root_cert.serial_number(),
|
||||
root_cert.public_key(), std::move(key_factory)));
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
util::Status DrmRootCertificate::CreateByType(
|
||||
CertificateType cert_type, std::unique_ptr<DrmRootCertificate>* cert) {
|
||||
CHECK(cert);
|
||||
return Create(GetDrmRootCertificate(cert_type), cert);
|
||||
DrmRootCertificate::DrmRootCertificate(
|
||||
CertificateType type, const std::string& serialized_certificate,
|
||||
const std::string& serial_number, const std::string& public_key,
|
||||
std::unique_ptr<RsaKeyFactory> key_factory)
|
||||
: type_(type),
|
||||
serialized_certificate_(serialized_certificate),
|
||||
serial_number_(serial_number),
|
||||
public_key_(public_key),
|
||||
key_factory_(std::move(key_factory)),
|
||||
signature_cache_(new VerifiedCertSignatureCache(key_factory_.get())) {}
|
||||
|
||||
DrmRootCertificate::~DrmRootCertificate() {}
|
||||
|
||||
std::string DrmRootCertificate::GetDigest() const {
|
||||
return absl::BytesToHexString(Sha256_Hash(serialized_certificate_));
|
||||
}
|
||||
|
||||
std::string DrmRootCertificate::GetDrmRootCertificate(CertificateType cert_type) {
|
||||
std::string root_cert;
|
||||
switch (cert_type) {
|
||||
case kCertificateTypeProduction: {
|
||||
root_cert.assign(kProdRootCertificate,
|
||||
kProdRootCertificate + sizeof(kProdRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeDevelopment: {
|
||||
root_cert.assign(kDevRootCertificate,
|
||||
kDevRootCertificate + sizeof(kDevRootCertificate));
|
||||
break;
|
||||
}
|
||||
case kCertificateTypeTesting: {
|
||||
root_cert.assign(kTestRootCertificate,
|
||||
kTestRootCertificate + sizeof(kTestRootCertificate));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO(user): Consider returning util::Status indicating unsupported
|
||||
// cert type.
|
||||
break;
|
||||
util::Status DrmRootCertificate::VerifyCertificate(
|
||||
const std::string& serialized_certificate,
|
||||
SignedDrmCertificate* signed_certificate,
|
||||
DrmCertificate* certificate) const {
|
||||
std::unique_ptr<SignedDrmCertificate> local_signed_certificate;
|
||||
if (!signed_certificate) {
|
||||
local_signed_certificate = absl::make_unique<SignedDrmCertificate>();
|
||||
signed_certificate = local_signed_certificate.get();
|
||||
}
|
||||
return root_cert;
|
||||
if (!signed_certificate->ParseFromString(serialized_certificate)) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signed-drm-certificate");
|
||||
}
|
||||
|
||||
std::unique_ptr<DrmCertificate> local_certificate;
|
||||
if (!certificate) {
|
||||
local_certificate = absl::make_unique<DrmCertificate>();
|
||||
certificate = local_certificate.get();
|
||||
}
|
||||
if (signed_certificate->drm_certificate().empty() ||
|
||||
!certificate->ParseFromString(signed_certificate->drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-drm-certificate");
|
||||
}
|
||||
if (certificate->serial_number().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-serial-number");
|
||||
}
|
||||
if (!certificate->has_creation_time_seconds()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-creation-time");
|
||||
}
|
||||
if (certificate->public_key().empty()) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"missing-public-key");
|
||||
}
|
||||
|
||||
// Verify signature chain, but do not use cache for leaf certificates.
|
||||
return VerifySignatures(*signed_certificate, certificate->serial_number(),
|
||||
!kUseCache);
|
||||
}
|
||||
|
||||
std::string DrmRootCertificate::GetDigest(CertificateType cert_type) {
|
||||
std::string cert(GetDrmRootCertificate(cert_type));
|
||||
if (cert.empty()) {
|
||||
return std::string();
|
||||
// Recursively verifies certificates with their signing certs or the root.
|
||||
// use_cache should be false when initially called so that signatures do not
|
||||
// cached leaf certificates not signed with the root certificate, such as for
|
||||
// the case of device-unique device certificates.
|
||||
// Signatures for root-signed certificates are always cached, even if they are
|
||||
// leaf certificates. For example service, and provisioner certificates.
|
||||
util::Status DrmRootCertificate::VerifySignatures(
|
||||
const SignedDrmCertificate& signed_cert, const std::string& cert_serial_number,
|
||||
bool use_cache) const {
|
||||
if (!signed_cert.has_signer()) {
|
||||
// Always use cache for root-signed certificates.
|
||||
return signature_cache_->VerifySignature(
|
||||
signed_cert.drm_certificate(), cert_serial_number,
|
||||
signed_cert.signature(), public_key(), serial_number_);
|
||||
}
|
||||
std::string hash(SHA256_DIGEST_LENGTH, 0);
|
||||
SHA256(reinterpret_cast<const unsigned char*>(cert.data()), cert.size(),
|
||||
reinterpret_cast<unsigned char*>(&hash[0]));
|
||||
return absl::BytesToHexString(hash);
|
||||
|
||||
DrmCertificate signer;
|
||||
if (!signer.ParseFromString(signed_cert.signer().drm_certificate())) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-signer-certificate");
|
||||
}
|
||||
|
||||
// Verify the signer before verifying signed_cert.
|
||||
util::Status status =
|
||||
VerifySignatures(signed_cert.signer(), signer.serial_number(), kUseCache);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (use_cache) {
|
||||
status = signature_cache_->VerifySignature(
|
||||
signed_cert.drm_certificate(), cert_serial_number,
|
||||
signed_cert.signature(), signer.public_key(), signer.serial_number());
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<RsaPublicKey> signer_public_key(
|
||||
key_factory_->CreateFromPkcs1PublicKey(signer.public_key()));
|
||||
if (!signer_public_key) {
|
||||
return util::Status(error_space, INVALID_DRM_CERTIFICATE,
|
||||
"invalid-leaf-signer-public-key");
|
||||
}
|
||||
if (!signer_public_key->VerifySignature(signed_cert.drm_certificate(),
|
||||
signed_cert.signature())) {
|
||||
return util::Status(error_space, INVALID_SIGNATURE,
|
||||
"cache-miss-invalid-signature");
|
||||
}
|
||||
}
|
||||
|
||||
return util::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
Reference in New Issue
Block a user