From e5380ca59f2cb2909ff25a4492cfcb0a6e82c5a0 Mon Sep 17 00:00:00 2001 From: "John W. Bruce" Date: Thu, 28 Feb 2019 17:32:22 -0800 Subject: [PATCH] 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 --- .../cdm/core/include/privacy_crypto.h | 17 +++ .../cdm/core/src/crypto_session.cpp | 133 +---------------- libwvdrmengine/cdm/core/src/device_files.cpp | 45 +----- .../cdm/core/src/privacy_crypto_boringssl.cpp | 141 ++++++++++++++++++ .../cdm/core/src/privacy_crypto_dummy.cpp | 32 ++++ 5 files changed, 200 insertions(+), 168 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/privacy_crypto.h b/libwvdrmengine/cdm/core/include/privacy_crypto.h index 6258bd80..02e8c9aa 100644 --- a/libwvdrmengine/cdm/core/include/privacy_crypto.h +++ b/libwvdrmengine/cdm/core/include/privacy_crypto.h @@ -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_ diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index e51e0d40..a8665403 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -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 -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(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 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(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(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) { diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 2d2afc61..781493f9 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -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 -#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 -#include -#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(data.data()); - unsigned char* output = reinterpret_cast(&(*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 hash(MD5_DIGEST_LENGTH); - const unsigned char* input_ptr = - reinterpret_cast(input.data()); - MD5(input_ptr, input.size(), &hash[0]); - return wvcdm::Base64SafeEncode(hash); + std::string hash = Md5Hash(input); + return wvcdm::Base64SafeEncode( + std::vector(hash.begin(), hash.end())); } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto_boringssl.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto_boringssl.cpp index fa2e0739..5fb83e2a 100644 --- a/libwvdrmengine/cdm/core/src/privacy_crypto_boringssl.cpp +++ b/libwvdrmengine/cdm/core/src/privacy_crypto_boringssl.cpp @@ -10,12 +10,15 @@ #include "privacy_crypto.h" #include +#include #include #include #include +#include #include #include #include +#include #include "log.h" @@ -49,6 +52,38 @@ void FreeKey(RSA* key) { } } +template +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 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(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(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(data.data()), data.size(), + reinterpret_cast(&hash[0])); + return hash; +} + +std::string Sha256Hash(const std::string& data) { + std::string hash(SHA256_DIGEST_LENGTH, '\0'); + SHA256(reinterpret_cast(data.data()), data.size(), + reinterpret_cast(&hash[0])); + return hash; +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp index ee40e879..a5f89f7a 100644 --- a/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp +++ b/libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp @@ -9,6 +9,18 @@ #include "privacy_crypto.h" +#include "log.h" + +#ifdef __APPLE__ +# include +# 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(&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(&hash[0])); + return hash; +} + } // namespace wvcdm