Restrict uses of BoringSSL.

(This is a merge of http://go/wvgerrit/71883)

This moves all the SSL code to privacy_crypto so we can use the
iOS-specific versions and not use any BoringSSL.  The iOS version
doesn't support OEM certificates.

Note that the tests still use BoringSSL.

Bug: 126559819
Test: build_and_run_all_unit_tests.sh
Change-Id: Ib0fad5d95b283b6cd6e02d8a08bcf248c5900bc4
This commit is contained in:
John W. Bruce
2019-02-28 17:32:22 -08:00
parent 50e4d67415
commit e5380ca59f
5 changed files with 200 additions and 168 deletions

View File

@@ -68,6 +68,23 @@ class RsaPublicKey {
CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey); CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey);
}; };
/**
* Extracts an integer value from the extensions in a certificate.
* @param cert A PKCS7 encoded X.509 certificate chain.
* @param extension_oid The ID of the extension to get.
* @param cert_index The zero-based index of the certificate in the chain to
* fetch from.
* @param value [OUT] Will contain the extracted value.
* @return True on success, false on error.
*/
bool ExtractExtensionValueFromCertificate(const std::string& cert,
const std::string& extension_oid,
size_t cert_index, uint32_t* value);
std::string Md5Hash(const std::string& data);
std::string Sha256Hash(const std::string& data);
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_CORE_PRIVACY_CRYPTO_H_ #endif // WVCDM_CORE_PRIVACY_CRYPTO_H_

View File

@@ -16,10 +16,8 @@
#include "crypto_key.h" #include "crypto_key.h"
#include "entitlement_key_session.h" #include "entitlement_key_session.h"
#include "log.h" #include "log.h"
#include "openssl/asn1.h"
#include "openssl/sha.h"
#include "openssl/x509v3.h"
#include "platform.h" #include "platform.h"
#include "privacy_crypto.h"
#include "properties.h" #include "properties.h"
#include "pst_report.h" #include "pst_report.h"
#include "string_conversions.h" #include "string_conversions.h"
@@ -48,30 +46,8 @@ const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13;
const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14; const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14;
// Constants and utility objects relating to OEM Certificates // Constants and utility objects relating to OEM Certificates
const int kExtensionOidSize = 64;
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1"; const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
// Helpers for working with BoringSSL
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
};
void DeleteX509Stack(STACK_OF(X509)* stack) {
sk_X509_pop_free(stack, X509_free);
}
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -503,13 +479,7 @@ CdmResponseType CryptoSession::GetExternalDeviceUniqueId(
pre_provision_token_type_ == kClientTokenOemCert) { pre_provision_token_type_ == kClientTokenOemCert) {
// To keep the size of the value passed back to the application down, hash // To keep the size of the value passed back to the application down, hash
// the large OEM Public Cert to a smaller value. // the large OEM Public Cert to a smaller value.
uint8_t hash[SHA256_DIGEST_LENGTH]; temp = Sha256Hash(temp);
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, temp.data(), temp.length());
SHA256_Final(hash, &ctx);
temp.assign(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
} }
*device_id = temp; *device_id = temp;
@@ -612,103 +582,8 @@ CdmResponseType CryptoSession::GetSystemIdInternal(uint32_t* system_id) {
bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert, bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
uint32_t* system_id) { uint32_t* system_id) {
// Load the certificate chain into a BoringSSL X509 Stack return ExtractExtensionValueFromCertificate(
const boringssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack( oem_cert, kWidevineSystemIdExtensionOid, /* cert_index */ 1, system_id);
sk_X509_new_null());
if (x509_stack.get() == NULL) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to allocate X509 Stack.");
return false;
}
CBS pkcs7;
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(oem_cert.data()),
oem_cert.size());
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Error getting certificate chain.");
return false;
}
STACK_OF(X509)* certs = x509_stack.get();
// Get the Widevine intermediate cert from the stack
if (sk_X509_num(certs) != 2) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Expected 2 certificates in chain, got %d",
sk_X509_num(certs));
return false;
}
X509* const intermediate_cert = sk_X509_value(certs, 1);
if (!intermediate_cert) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get intermediate cert.");
return false;
}
// Find the Widevine System ID extension in the intermediate cert
const int extension_count = X509_get_ext_count(intermediate_cert);
for (int i = 0; i < extension_count; ++i) {
X509_EXTENSION* const extension = X509_get_ext(intermediate_cert, i);
if (!extension) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get cert extension %d", i);
continue;
}
ASN1_OBJECT* const extension_object = X509_EXTENSION_get_object(extension);
if (!extension_object) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get object of cert extension %d", i);
continue;
}
char extension_name[kExtensionOidSize + 1];
OBJ_obj2txt(extension_name, kExtensionOidSize, extension_object, 1);
if (strcmp(extension_name, kWidevineSystemIdExtensionOid) != 0) {
// This extension is not the Widevine System ID, so we should move on to
// the next one.
continue;
}
ASN1_OCTET_STRING* const octet_str = X509_EXTENSION_get_data(extension);
if (!octet_str) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get data of Widevine System ID extension.");
return false;
}
const unsigned char* data = octet_str->data;
if (!data) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Null data in Widevine System ID extension.");
return false;
}
ASN1_INTEGER* const asn1_integer =
d2i_ASN1_INTEGER(NULL, &data, octet_str->length);
if (!asn1_integer) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to decode data in Widevine System ID extension.");
return false;
}
const long system_id_long = ASN1_INTEGER_get(asn1_integer);
ASN1_INTEGER_free(asn1_integer);
if (system_id_long == -1) {
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to decode ASN integer in Widevine System ID extension.");
return false;
}
*system_id = static_cast<uint32_t>(system_id_long);
return true;
}
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Widevine System ID extension not found.");
return false;
} }
CdmResponseType CryptoSession::GetProvisioningId(std::string* provisioning_id) { CdmResponseType CryptoSession::GetProvisioningId(std::string* provisioning_id) {

View File

@@ -10,21 +10,11 @@
#include "file_store.h" #include "file_store.h"
#include "license_protocol.pb.h" #include "license_protocol.pb.h"
#include "log.h" #include "log.h"
#include "privacy_crypto.h"
#include "properties.h" #include "properties.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#if defined(__APPLE__)
#include <CommonCrypto/CommonDigest.h>
#define SHA256 CC_SHA256
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
#define MD5 CC_MD5
#define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
#else
#include <openssl/md5.h>
#include <openssl/sha.h>
#endif
// Protobuf generated classes. // Protobuf generated classes.
using video_widevine_client::sdk::DeviceCertificate; using video_widevine_client::sdk::DeviceCertificate;
using video_widevine_client::sdk::HashedFile; using video_widevine_client::sdk::HashedFile;
@@ -67,17 +57,6 @@ const char kEmptyFileName[] = "";
const char kUsageTableFileName[] = "usgtable.bin"; const char kUsageTableFileName[] = "usgtable.bin";
const char kWildcard[] = "*"; const char kWildcard[] = "*";
bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
hash->resize(SHA256_DIGEST_LENGTH);
const unsigned char* input =
reinterpret_cast<const unsigned char*>(data.data());
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
SHA256(input, data.size(), output);
return true;
}
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -1183,12 +1162,7 @@ bool DeviceFiles::DeleteUsageTableInfo() {
DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash( DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash(
const std::string& name, const std::string& name,
const std::string& serialized_file) { const std::string& serialized_file) {
// calculate SHA hash std::string hash = Sha256Hash(serialized_file);
std::string hash;
if (!Hash(serialized_file, &hash)) {
LOGW("DeviceFiles::StoreFileWithHash: Hash computation failed");
return kHashComputationFailed;
}
// Fill in hashed file data // Fill in hashed file data
HashedFile hash_file; HashedFile hash_file;
@@ -1296,12 +1270,7 @@ DeviceFiles::ResponseType DeviceFiles::RetrieveHashedFile(
return kFileParseError1; return kFileParseError1;
} }
std::string hash; std::string hash = Sha256Hash(hash_file.file());
if (!Hash(hash_file.file(), &hash)) {
LOGW("DeviceFiles::RetrieveHashedFile: Hash computation failed");
return kHashComputationFailed;
}
if (hash != hash_file.hash()) { if (hash != hash_file.hash()) {
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch"); LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
// Remove the corrupted file so the caller will not get the same error // Remove the corrupted file so the caller will not get the same error
@@ -1387,11 +1356,9 @@ std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
} }
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) { std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH); std::string hash = Md5Hash(input);
const unsigned char* input_ptr = return wvcdm::Base64SafeEncode(
reinterpret_cast<const unsigned char*>(input.data()); std::vector<uint8_t>(hash.begin(), hash.end()));
MD5(input_ptr, input.size(), &hash[0]);
return wvcdm::Base64SafeEncode(hash);
} }
} // namespace wvcdm } // namespace wvcdm

View File

@@ -10,12 +10,15 @@
#include "privacy_crypto.h" #include "privacy_crypto.h"
#include <openssl/aes.h> #include <openssl/aes.h>
#include <openssl/asn1.h>
#include <openssl/bio.h> #include <openssl/bio.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/md5.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/x509v3.h>
#include "log.h" #include "log.h"
@@ -49,6 +52,38 @@ void FreeKey(RSA* key) {
} }
} }
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
};
void DeleteX509Stack(STACK_OF(X509)* stack) {
sk_X509_pop_free(stack, X509_free);
}
std::string GetExtensionOid(X509_EXTENSION* extension) {
ASN1_OBJECT* const extension_object = X509_EXTENSION_get_object(extension);
if (!extension_object) {
return "";
}
const int size = OBJ_obj2txt(nullptr, 0, extension_object, 1);
std::string ret(size, '\0');
OBJ_obj2txt(&ret[0], ret.size() + 1, extension_object, 1);
return ret;
}
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
@@ -287,4 +322,110 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return true; return true;
} }
bool ExtractExtensionValueFromCertificate(const std::string& cert,
const std::string& extension_oid,
size_t cert_index, uint32_t* value) {
// Load the certificate chain into a BoringSSL X509 Stack
const boringssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack(
sk_X509_new_null());
if (x509_stack.get() == NULL) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to allocate X509 Stack.");
return false;
}
CBS pkcs7;
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(cert.data()), cert.size());
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
LOGE("ExtractExtensionValueFromCertificate: "
"Error getting certificate chain.");
return false;
}
STACK_OF(X509)* certs = x509_stack.get();
// Find the desired certificate from the stack.
if (sk_X509_num(certs) <= cert_index) {
LOGE("ExtractExtensionValueFromCertificate: "
"Expected at least %zu certificates in chain, got %d",
cert_index + 1, sk_X509_num(certs));
return false;
}
X509* const intermediate_cert = sk_X509_value(certs, cert_index);
if (!intermediate_cert) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to get intermediate cert.");
return false;
}
// Find the Widevine System ID extension in the intermediate cert
const int extension_count = X509_get_ext_count(intermediate_cert);
for (int i = 0; i < extension_count; ++i) {
X509_EXTENSION* const extension = X509_get_ext(intermediate_cert, i);
if (!extension) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to get cert extension %d", i);
continue;
}
if (GetExtensionOid(extension) != extension_oid) {
// This extension is not the Widevine System ID, so we should move on to
// the next one.
continue;
}
ASN1_OCTET_STRING* const octet_str = X509_EXTENSION_get_data(extension);
if (!octet_str) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to get data of extension.");
return false;
}
const unsigned char* data = octet_str->data;
if (!data) {
LOGE("ExtractExtensionValueFromCertificate: "
"Null data in extension.");
return false;
}
ASN1_INTEGER* const asn1_integer =
d2i_ASN1_INTEGER(NULL, &data, octet_str->length);
if (!asn1_integer) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to decode data in extension.");
return false;
}
const long system_id_long = ASN1_INTEGER_get(asn1_integer);
ASN1_INTEGER_free(asn1_integer);
if (system_id_long == -1) {
LOGE("ExtractExtensionValueFromCertificate: "
"Unable to decode ASN integer in extension.");
return false;
}
*value = static_cast<uint32_t>(system_id_long);
return true;
}
LOGE("ExtractExtensionValueFromCertificate: Extension not found.");
return false;
}
std::string Md5Hash(const std::string& data) {
std::string hash(MD5_DIGEST_LENGTH, '\0');
MD5(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
std::string Sha256Hash(const std::string& data) {
std::string hash(SHA256_DIGEST_LENGTH, '\0');
SHA256(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
} // namespace wvcdm } // namespace wvcdm

View File

@@ -9,6 +9,18 @@
#include "privacy_crypto.h" #include "privacy_crypto.h"
#include "log.h"
#ifdef __APPLE__
# include <CommonCrypto/CommonDigest.h>
# define SHA256 CC_SHA256
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
# define MD5 CC_MD5
# define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
#else
# error "No hash algorithm known for this platform."
#endif
namespace wvcdm { namespace wvcdm {
AesCbcKey::AesCbcKey() {} AesCbcKey::AesCbcKey() {}
@@ -38,4 +50,24 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return false; return false;
} }
bool ExtractExtensionValueFromCertificate(const std::string& cert,
const std::string& extension_oid,
size_t cert_index, uint32_t* value) {
LOGE("ExtractExtensionValueFromCertificate: Not supported in this build.");
return false;
}
std::string Md5Hash(const std::string& data) {
std::string hash(MD5_DIGEST_LENGTH, '\0');
MD5(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
std::string Sha256Hash(const std::string& data) {
std::string hash(SHA256_DIGEST_LENGTH, '\0');
SHA256(data.data(), data.size(), reinterpret_cast<uint8_t*>(&hash[0]));
return hash;
}
} // namespace wvcdm } // namespace wvcdm