Export provisioning sdk
Change-Id: I4d47d80444c9507f84896767dc676112ca11e901
This commit is contained in:
210
provisioning_sdk/internal/oem_device_cert.cc
Normal file
210
provisioning_sdk/internal/oem_device_cert.cc
Normal file
@@ -0,0 +1,210 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2016 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "provisioning_sdk/internal/oem_device_cert.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "openssl/asn1.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "provisioning_sdk/internal/certificates/root_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->reset(new 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) {
|
||||
RootCertificates root_certificates;
|
||||
switch (certificate_type) {
|
||||
case kCertTesting:
|
||||
case kCertDevelopment:
|
||||
return Initialize(root_certificates.oem_root_dev_certificate());
|
||||
case kCertProduction:
|
||||
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
|
||||
Reference in New Issue
Block a user