213 lines
6.9 KiB
C++
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
|