Files
provisioning_sdk_source/provisioning_sdk/internal/oem_device_cert.cc
2019-01-23 15:16:31 -08:00

213 lines
6.9 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// Copyright 2016 Google LLC.
//
// This software is licensed under the terms defined in the Widevine Master
// License Agreement. For a copy of this agreement, please contact
// widevine-licensing@google.com.
////////////////////////////////////////////////////////////////////////////////
#include "provisioning_sdk/internal/oem_device_cert.h"
#include <string.h>
#include "glog/logging.h"
#include "absl/memory/memory.h"
#include "openssl/asn1.h"
#include "openssl/pkcs7.h"
#include "openssl/x509.h"
#include "openssl/x509v3.h"
#include "provisioning_sdk/internal/certificates/root_oem_certificates.h"
namespace widevine {
namespace {
const int kExtensionOidSize = 64;
const char kWidevineSystemIdExtensionOid[] = "1.3.6.1.4.1.11129.4.1.1";
bool ExtractPublicKey(X509* x509, std::unique_ptr<RsaPublicKey>* public_key) {
DCHECK(x509);
ScopedPKEY pkey(X509_get_pubkey(x509));
if (!pkey) {
LOG(WARNING) << "X509_get_pubkey failed.";
return false;
}
*public_key = absl::make_unique<RsaPublicKey>(EVP_PKEY_get1_RSA(pkey.get()));
return true;
}
bool ExtractSystemId(X509* x509, uint32_t* system_id) {
DCHECK(x509);
const int num_of_extensions = X509_get_ext_count(x509);
for (int i = 0; i < num_of_extensions; ++i) {
X509_EXTENSION* extension = X509_get_ext(x509, i);
if (!extension) {
LOG(WARNING) << "X509_get_ext failed.";
return false;
}
ASN1_OBJECT* extension_object = X509_EXTENSION_get_object(extension);
if (!extension_object) {
LOG(WARNING) << "X509_EXTENSION_get_object failed.";
return false;
}
char extension_name[kExtensionOidSize + 1];
OBJ_obj2txt(extension_name, kExtensionOidSize, extension_object, 1);
if (strcmp(extension_name, kWidevineSystemIdExtensionOid)) continue;
ASN1_OCTET_STRING* octet_str = X509_EXTENSION_get_data(extension);
if (!octet_str) {
LOG(WARNING) << "X509_EXTENSION_get_data failed.";
return false;
}
const unsigned char* data = octet_str->data;
if (!data) {
LOG(WARNING) << "null data in octet string.";
return false;
}
ScopedAsn1Integer asn1_integer(
d2i_ASN1_INTEGER(nullptr, &data, octet_str->length));
if (!asn1_integer) {
LOG(WARNING) << "d2i_ASN1_INTEGER failed.";
return false;
}
int64_t system_id_long = ASN1_INTEGER_get(asn1_integer.get());
if (system_id_long == -1) {
LOG(WARNING) << "ASN1_INTEGER_get failed.";
return false;
}
*system_id = system_id_long;
return true;
}
LOG(WARNING) << "Widevine system ID extension not found.";
return false;
}
bool ExtractSerialNumber(X509* x509, std::string* oem_ca_serial_number) {
ASN1_INTEGER* serial = X509_get_serialNumber(x509);
if (!serial) {
LOG(WARNING) << "X509_get_serialNumber failed.";
return false;
}
uint8_t* buffer = ASN1_STRING_data(serial);
if (!buffer) {
LOG(WARNING) << "ASN1_STRING_data failed.";
return false;
}
int length = ASN1_STRING_length(serial);
if (length <= 0) {
LOG(WARNING) << "ASN1_STRING_length returns " << length;
}
oem_ca_serial_number->assign(reinterpret_cast<char*>(buffer), length);
return true;
}
} // namespace
OemDeviceCert::OemDeviceCert() {}
OemDeviceCert::~OemDeviceCert() {}
bool OemDeviceCert::Initialize(CertificateType certificate_type) {
RootOemCertificates root_certificates;
switch (certificate_type) {
case kCertificateTypeTesting:
case kCertificateTypeDevelopment:
return Initialize(root_certificates.oem_root_dev_certificate());
case kCertificateTypeProduction:
return Initialize(root_certificates.oem_root_prod_certificate());
default:
LOG(WARNING) << "Invalid certificate type " << certificate_type;
return false;
}
}
bool OemDeviceCert::VerifyCertificateChain(
const std::string& certificate_chain,
std::unique_ptr<RsaPublicKey>* leaf_public_key, uint32_t* system_id,
std::string* oem_ca_serial_number) const {
if (certificate_chain.empty()) {
LOG(WARNING) << "Empty certificate.";
return false;
}
ScopedX509Stack x509_stack(sk_X509_new_null());
if (!x509_stack) {
LOG(WARNING) << "sk_X509_new_null returned NULL.";
return false;
}
CBS pkcs7;
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(certificate_chain.data()),
certificate_chain.size());
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
LOG(WARNING) << "PKCS7_get_certificates failed.";
return false;
}
if (sk_X509_num(x509_stack.get()) != 2) {
LOG(WARNING) << "Expecting one and only one intermediate certificate.";
return false;
}
X509* leaf_certificate = sk_X509_value(x509_stack.get(), 0);
X509* intermediate_certificate = sk_X509_value(x509_stack.get(), 1);
DCHECK(leaf_certificate);
DCHECK(intermediate_certificate);
// Verify the cert.
// TODO(user): Implement some form of caching mechanism so that we do not
// have to verify the same intermediate certs over and over.
ScopedX509StoreCtx context(X509_STORE_CTX_new());
if (!context) {
LOG(WARNING) << "X509_STORE_CTX_new returned NULL.";
return false;
}
if (!X509_STORE_CTX_init(context.get(), store_.get(), leaf_certificate,
x509_stack.get())) {
LOG(WARNING) << "X509_STORE_CTX_init failed.";
return false;
}
if (X509_verify_cert(context.get()) != 1) {
LOG(WARNING) << "Verification error: "
<< X509_verify_cert_error_string(
X509_STORE_CTX_get_error(context.get()));
return false;
}
if (sk_X509_num(context->chain) < 3) {
LOG(WARNING) << "The leaf certificate should not be signed by the root "
"certificate directly.";
return false;
}
// Extract public key from the leaf certificate and system_id extension and
// oem serial number from the intermediate certificate.
return ExtractPublicKey(leaf_certificate, leaf_public_key) &&
ExtractSystemId(intermediate_certificate, system_id) &&
ExtractSerialNumber(intermediate_certificate, oem_ca_serial_number);
}
bool OemDeviceCert::Initialize(const std::string& serialized_root_certificate) {
if (serialized_root_certificate.empty()) {
LOG(WARNING) << "Empty root certificate.";
return false;
}
store_.reset(X509_STORE_new());
if (!store_) {
LOG(WARNING) << "X509_STORE_new returned NULL";
return false;
}
const char* data = serialized_root_certificate.data();
ScopedX509 root_certificate(d2i_X509(nullptr,
reinterpret_cast<const uint8_t**>(&data),
serialized_root_certificate.size()));
if (!root_certificate) {
LOG(WARNING) << "d2i_X509 returned NULL.";
return false;
}
if (!X509_STORE_add_cert(store_.get(), root_certificate.get())) {
LOG(WARNING) << "X509_STORE_add_cert failed.";
return false;
}
return true;
}
} // namespace widevine