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:
@@ -68,6 +68,23 @@ class 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
|
||||
|
||||
#endif // WVCDM_CORE_PRIVACY_CRYPTO_H_
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
#include "crypto_key.h"
|
||||
#include "entitlement_key_session.h"
|
||||
#include "log.h"
|
||||
#include "openssl/asn1.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "openssl/x509v3.h"
|
||||
#include "platform.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "properties.h"
|
||||
#include "pst_report.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -48,30 +46,8 @@ const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13;
|
||||
const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14;
|
||||
|
||||
// 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";
|
||||
|
||||
// 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 wvcdm {
|
||||
@@ -503,13 +479,7 @@ CdmResponseType CryptoSession::GetExternalDeviceUniqueId(
|
||||
pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
// To keep the size of the value passed back to the application down, hash
|
||||
// the large OEM Public Cert to a smaller value.
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
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);
|
||||
temp = Sha256Hash(temp);
|
||||
}
|
||||
|
||||
*device_id = temp;
|
||||
@@ -612,103 +582,8 @@ CdmResponseType CryptoSession::GetSystemIdInternal(uint32_t* system_id) {
|
||||
|
||||
bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
uint32_t* system_id) {
|
||||
// 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("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;
|
||||
return ExtractExtensionValueFromCertificate(
|
||||
oem_cert, kWidevineSystemIdExtensionOid, /* cert_index */ 1, system_id);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
|
||||
@@ -10,21 +10,11 @@
|
||||
#include "file_store.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.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.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
using video_widevine_client::sdk::HashedFile;
|
||||
@@ -67,17 +57,6 @@ const char kEmptyFileName[] = "";
|
||||
const char kUsageTableFileName[] = "usgtable.bin";
|
||||
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 wvcdm {
|
||||
@@ -1183,12 +1162,7 @@ bool DeviceFiles::DeleteUsageTableInfo() {
|
||||
DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash(
|
||||
const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_file, &hash)) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Hash computation failed");
|
||||
return kHashComputationFailed;
|
||||
}
|
||||
std::string hash = Sha256Hash(serialized_file);
|
||||
|
||||
// Fill in hashed file data
|
||||
HashedFile hash_file;
|
||||
@@ -1296,12 +1270,7 @@ DeviceFiles::ResponseType DeviceFiles::RetrieveHashedFile(
|
||||
return kFileParseError1;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hash_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash computation failed");
|
||||
return kHashComputationFailed;
|
||||
}
|
||||
|
||||
std::string hash = Sha256Hash(hash_file.file());
|
||||
if (hash != hash_file.hash()) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
|
||||
// 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::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
|
||||
const unsigned char* input_ptr =
|
||||
reinterpret_cast<const unsigned char*>(input.data());
|
||||
MD5(input_ptr, input.size(), &hash[0]);
|
||||
return wvcdm::Base64SafeEncode(hash);
|
||||
std::string hash = Md5Hash(input);
|
||||
return wvcdm::Base64SafeEncode(
|
||||
std::vector<uint8_t>(hash.begin(), hash.end()));
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
#include "privacy_crypto.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509v3.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 wvcdm {
|
||||
@@ -287,4 +322,110 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
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
|
||||
|
||||
@@ -9,6 +9,18 @@
|
||||
|
||||
#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 {
|
||||
|
||||
AesCbcKey::AesCbcKey() {}
|
||||
@@ -38,4 +50,24 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user