Add Provisioning 4 support

Widevine provisioning 4 support is added in this patch.
This commit is contained in:
Lu Chen
2025-02-25 13:49:37 -08:00
parent 5f209e6980
commit 41829ca1e5
37 changed files with 2915 additions and 356 deletions

View File

@@ -10,7 +10,10 @@
#include "cas_status.h"
#include "crypto_session.h"
#include "crypto_wrapped_key.h"
#include "file_store.h"
#include "policy_engine.h"
#include "service_certificate.h"
#include "timer.h"
namespace wvcas {
@@ -28,32 +31,33 @@ class CasLicense : public wvutil::TimerHandler, public wvcas::CasEventListener {
// Generate a request to obtain a device certificate for requesting
// entitlements. The generated message is set in |provisioning_request|.
virtual CasStatus GenerateDeviceProvisioningRequest(
std::string* provisioning_request) const;
wvutil::FileSystem& file_system, std::string* provisioning_request);
// Process a server response containing a device certificate for use in
// requesting entitlements. The contained certificate data will be extracted
// and wrapped for storage. The public key cert will be returned in
// |device_certificate|. The private key information will be wrapped by the
// crypto session and returned in |wrapped_rsa_key|.
// crypto session and returned in |wrapped_private_key|.
// A secure binary file image containing the device cert is returned in
// |device_file| if not nullptr. This file is suitable for storage on a device
virtual CasStatus HandleDeviceProvisioningResponse(
wvutil::FileSystem* file_system,
const std::string& signed_provisioning_response,
std::string* device_certificate, std::string* wrapped_rsa_key,
std::string* device_file) const;
std::string* device_certificate,
CryptoWrappedKey* wrapped_private_key) const;
// Generate a request to obtain an EMM (Entitlement Management Message) to
// use to enable processing of ECM(s) (Encryption Management Message).
// |init_data| is widevine metadata about the stream needed in the request.
// |wrapped_rsa_key| and |signed_license_request| are the device certificate
// |private_key| and |signed_license_request| are the device certificate
// obtained by HandleDeviceProvisioningResponse.
virtual CasStatus GenerateEntitlementRequest(
const std::string& init_data, const std::string& device_certificate,
const std::string& wrapped_rsa_key, LicenseType license_type,
const CryptoWrappedKey& private_key, LicenseType license_type,
std::string* signed_license_request);
// Restores a stored license making the keys available for use.
virtual CasStatus HandleStoredLicense(const std::string& wrapped_rsa_key,
virtual CasStatus HandleStoredLicense(const CryptoWrappedKey& private_key,
const std::string& license_file);
// Process a server response containing a EMM for use in the processing of
@@ -68,7 +72,7 @@ class CasLicense : public wvutil::TimerHandler, public wvcas::CasEventListener {
// for use in an EMM request.
virtual CasStatus HandleStoredDrmCert(const std::string& certificate,
std::string* device_certificate,
std::string* wrapped_rsa_key);
CryptoWrappedKey* private_key);
// Generate an entitlement renewal request message in
// |signed_renewal_request|.
@@ -156,6 +160,19 @@ class CasLicense : public wvutil::TimerHandler, public wvcas::CasEventListener {
private:
CasStatus GenerateDeviceProvisioningRequestWithKeybox(
std::string* provisioning_request) const;
CasStatus GetProvisioning40RequestInternal(
wvutil::FileSystem& file_system,
std::string* serialized_provisioning_request);
CasStatus FillEncryptedClientId(
const std::string& client_token,
video_widevine::ProvisioningRequest& provisioning_request,
const ServiceCertificate& service_certificate) const;
void FillClientProperties(
video_widevine::ClientIdentification& client_id) const;
CasStatus HandleProvisioning40Response(
wvutil::FileSystem* file_system,
const video_widevine::SignedProvisioningMessage& signed_response,
std::string* cert, CryptoWrappedKey* wrapped_key) const;
CasStatus GenerateDeviceProvisioningRequestWithOEMCert() const;
CasStatus InstallLicense(const std::string& session_key,
const std::string& serialized_license,
@@ -177,6 +194,13 @@ class CasLicense : public wvutil::TimerHandler, public wvcas::CasEventListener {
std::string renewal_response_;
std::string init_data_;
bool is_renewal_in_license_file_ = false;
std::unique_ptr<ServiceCertificate> wv_service_cert_;
// The wrapped private key in provisioning 4 generated by calling
// GenerateCertificateKeyPair. It will be saved to file system if a valid
// response is received.
std::string provisioning_40_wrapped_private_key_;
// Key type of the generated key pair in provisioning 4.
CryptoWrappedKey::Type provisioning_40_key_type_;
};
} // namespace wvcas

View File

@@ -29,6 +29,9 @@ enum class CasStatusCode : int32_t {
kAccessDeniedByParentalControl = 15,
kUnknownEvent = 16,
kOEMCryptoVersionMismatch = 17,
kDeviceCertificateError = 18,
kClientIdEncryptionError = 19,
kProvisioningError = 20,
};
class CasStatus {

View File

@@ -24,10 +24,10 @@ enum class LicenseType {
typedef enum {
ProvisioningError = 0, // Device cannot be provisioned.
DrmCertificate = 1, // Device has baked in DRM certificate
// (level 3 only)
DrmCertificate = 1, // Device has baked in DRM certificate (level 3 only)
Keybox = 2, // Device has factory installed unique keybox.
OEMCertificate = 3 // Device has factory installed OEM certificate.
OEMCertificate = 3, // Device has factory installed OEM certificate.
BootCertificateChain = 4, // Provisioning 4.0
} CasProvisioningMethod;
enum class CryptoMode {

View File

@@ -13,6 +13,7 @@
#include "OEMCryptoCENC.h"
#include "cas_status.h"
#include "cas_types.h"
#include "crypto_wrapped_key.h"
#include "oemcrypto_interface.h"
#include "rw_lock.h"
@@ -36,7 +37,7 @@ typedef OEMCrypto_HDCP_Capability HdcpCapability;
class CryptoLock {
public:
CryptoLock(){};
CryptoLock() {};
// These methods should be used to take the various CryptoSession mutexes in
// preference to taking the mutexes directly.
//
@@ -175,7 +176,21 @@ class CryptoInterface {
OEMCrypto_SESSION key_session, uint8_t* key_token,
size_t* key_token_length);
virtual OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm(
OEMCrypto_SESSION session, OEMCrypto_SignatureHashAlgorithm* algorithm);
OEMCrypto_SESSION session, OEMCrypto_SignatureHashAlgorithm* algorithm);
virtual OEMCryptoResult OEMCrypto_GetBootCertificateChain(
uint8_t* bcc, size_t* bcc_length, uint8_t* additional_signature,
size_t* additional_signature_length);
virtual OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair(
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_length,
uint8_t* public_key_signature, size_t* public_key_signature_length,
uint8_t* wrapped_private_key, size_t* wrapped_private_key_length,
OEMCrypto_PrivateKeyType* key_type);
virtual OEMCryptoResult OEMCrypto_InstallOemPrivateKey(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_private_key, size_t wrapped_private_key_length);
virtual uint8_t OEMCrypto_Security_Patch_Level();
virtual OEMCryptoResult OEMCrypto_BuildInformation(char* buffer,
size_t* buffer_length);
// This is the factory method used to enable the oemcrypto interface.
static OEMCryptoResult create(std::unique_ptr<CryptoInterface>* init) {
@@ -252,8 +267,6 @@ class CryptoSession {
std::string* wrapped_private_key);
virtual CasStatus GetOEMPublicCertificate(uint8_t* public_cert,
size_t* public_cert_length);
virtual CasStatus LoadDeviceRSAKey(const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
virtual CasStatus GenerateRSASignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
@@ -291,6 +304,17 @@ class CryptoSession {
virtual CasStatus APIVersion(uint32_t* api_version);
virtual CasStatus GetOEMKeyToken(OEMCrypto_SESSION entitled_key_session_id,
std::vector<uint8_t>& token);
virtual CasStatus GetBootCertificateChain(std::string* bcc,
std::string* additional_signature);
virtual CasStatus GenerateCertificateKeyPair(
std::string* public_key, std::string* public_key_signature,
std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type);
virtual CasStatus LoadOemCertificatePrivateKey(
const CryptoWrappedKey& private_key);
virtual CasStatus LoadCertificatePrivateKey(
const CryptoWrappedKey& private_key);
virtual uint8_t GetSecurityPatchLevel();
virtual bool GetBuildInformation(std::string* info);
CryptoSession(const CryptoSession&) = delete;
CryptoSession& operator=(const CryptoSession&) = delete;

View File

@@ -0,0 +1,50 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WIDEVINE_CAS_CRYPTO_WRAPPED_KEY_H_
#define WIDEVINE_CAS_CRYPTO_WRAPPED_KEY_H_
#include <string>
namespace wvcas {
// Represents OEMCrypto's wrapped private DRM key. As of v16, it is
// possible for OEMCrypto to support ECC-based DRM certificates. The
// format of the wrapped key is vendor specific; however, the API
// requires that the CDM track whether the wrapped key is RSA or ECC.
class CryptoWrappedKey {
public:
enum Type : int32_t { kUninitialized = 0, kRsa = 1, kEcc = 2 };
CryptoWrappedKey() = default;
CryptoWrappedKey(Type type, const std::string& key)
: type_(type), key_(key) {}
Type type() const { return type_; }
void set_type(Type type) { type_ = type; }
const std::string& key() const { return key_; }
// Mutable reference getter for passing to OMECrypto.
std::string& key() { return key_; }
void set_key(const std::string& key) { key_ = key; }
void Clear() {
type_ = kUninitialized;
key_.clear();
}
// A valid key must have a known key type and have key data.
bool IsValid() const { return type_ != kUninitialized && !key_.empty(); }
// Equality operator is for testing only. Real keys may have
// different meta data but the same logical key.
bool IsEqualTo(const CryptoWrappedKey& other) const {
return type_ == other.type_ && key_ == other.key_;
}
private:
// DRM key type of the wrapped key. For wrapped keys which the type
// of key is unknown, assume it to be RSA.
Type type_ = kUninitialized;
// Vendor-specific wrapped DRM key.
std::string key_;
}; // class CryptoWrappedKey
} // namespace wvcas
#endif // WIDEVINE_CAS_CRYPTO_WRAPPED_KEY_H_

View File

@@ -0,0 +1,35 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef FILE_UTIL_H_
#define FILE_UTIL_H_
#include <string>
#include "cas_status.h"
#include "crypto_wrapped_key.h"
#include "device_files.pb.h"
#include "file_store.h"
namespace wvcas {
bool ReadFileFromStorage(wvutil::FileSystem& file_system,
const std::string& filename, std::string* file_data);
bool RemoveFile(wvutil::FileSystem& file_system, const std::string& filename);
bool StoreFile(wvutil::FileSystem& file_system, const std::string& filename,
const std::string& file_data);
bool StoreFileWithHash(wvutil::FileSystem& file_system, const std::string& name,
const std::string& serialized_file);
bool StoreOemCertificate(wvutil::FileSystem& file_system,
const std::string& certificate,
const CryptoWrappedKey& private_key);
bool StoreCertificate(wvutil::FileSystem& file_system,
const std::string& certificate,
const CryptoWrappedKey& private_key);
CasStatus RetrieveHashedFile(const std::string& certificate,
video_widevine_client::sdk::File& file);
bool RetrieveOemCertificate(wvutil::FileSystem& file_system,
std::string& certificate,
CryptoWrappedKey* wrapped_private_key);
} // namespace wvcas
#endif // FILE_UTIL_H_

View File

@@ -21,7 +21,7 @@ struct InputStreamParams {
size_t data_length;
bool is_encrypted;
InputStreamParams(){};
InputStreamParams() {};
InputStreamParams(const uint8_t* data_addr, size_t data_length,
bool is_encrypted)
: data_addr(data_addr),
@@ -124,6 +124,20 @@ class OEMCryptoInterface {
virtual OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm(
OEMCrypto_SESSION session,
OEMCrypto_SignatureHashAlgorithm* algorithm) const;
virtual OEMCryptoResult OEMCrypto_GetBootCertificateChain(
uint8_t* bcc, size_t* bcc_length, uint8_t* additional_signature,
size_t* additional_signature_length);
virtual OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair(
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_length,
uint8_t* public_key_signature, size_t* public_key_signature_length,
uint8_t* wrapped_private_key, size_t* wrapped_private_key_length,
OEMCrypto_PrivateKeyType* key_type);
virtual OEMCryptoResult OEMCrypto_InstallOemPrivateKey(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_private_key, size_t wrapped_private_key_length);
virtual uint8_t OEMCrypto_Security_Patch_Level();
virtual OEMCryptoResult OEMCrypto_BuildInformation(char* buffer,
size_t* buffer_length);
OEMCryptoInterface(const OEMCryptoInterface&) = delete;
OEMCryptoInterface& operator=(const OEMCryptoInterface&) = delete;

View File

@@ -108,9 +108,9 @@ class PolicyEngine : public wvutil::TimerHandler {
int64_t GetPlaybackStartTime() const { return playback_start_time_; }
int64_t GetLastPlaybackTime() const { return last_playback_time_; }
int64_t GetGracePeriodEndTime() const { return grace_period_end_time_; }
void RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time,
int64_t grace_period_end_time);
virtual void RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time,
int64_t grace_period_end_time);
PolicyEngine(const PolicyEngine&) = delete;
PolicyEngine& operator=(const PolicyEngine&) = delete;

View File

@@ -0,0 +1,86 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Description:
// Declaration of classes representing AES and RSA public keys used
// for signature verification and encryption.
//
// AES encryption details:
// Algorithm: AES-CBC
//
// RSA signature details:
// Algorithm: RSASSA-PSS
// Hash algorithm: SHA1
// Mask generation function: mgf1SHA1
// Salt length: 20 bytes
// Trailer field: 0xbc
//
// RSA encryption details:
// Algorithm: RSA-OAEP
// Mask generation function: mgf1SHA1
// Label (encoding paramter): empty string
//
#ifndef WIDEVINE_CAS_PRIVACY_CRYPTO_H_
#define WIDEVINE_CAS_PRIVACY_CRYPTO_H_
#include <cstdint>
#include <string>
namespace wvcas {
class AesCbcKey {
public:
AesCbcKey();
~AesCbcKey();
bool Init(const std::string& key);
bool Encrypt(const std::string& in, const std::string& iv, std::string* out,
bool has_padding = true);
bool Decrypt(const std::string& in, const std::string& iv, std::string* out,
bool has_padding = true);
private:
std::string key_;
}; // class AesCbcKey
class RsaPublicKey {
public:
RsaPublicKey();
~RsaPublicKey();
// Initializes an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey
bool Init(const std::string& serialized_key);
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
// parameters. Returns true if successful, false otherwise.
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
// Verify RSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if validation succeeds, false otherwise.
bool VerifySignature(const std::string& message,
const std::string& signature);
private:
std::string serialized_key_;
}; // class 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 Sha1Hash(const std::string& data);
std::string Sha256Hash(const std::string& data);
std::string Sha512Hash(const std::string& data);
} // namespace wvcas
#endif // WIDEVINE_CAS_PRIVACY_CRYPTO_H_

View File

@@ -0,0 +1,64 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WIDEVINE_CAS_SERVICE_CERTIFICATE_H_
#define WIDEVINE_CAS_SERVICE_CERTIFICATE_H_
#include <memory>
#include <string>
#include "cas_status.h"
#include "license_protocol.pb.h"
#include "privacy_crypto.h"
namespace wvcas {
// Service Certificates are used to encrypt the ClientIdentification message
// that is part of Device Provisioning, License, Renewal, and Release requests.
class ServiceCertificate {
public:
ServiceCertificate() = default;
virtual ~ServiceCertificate() {}
// Set up a new service certificate.
// Accept a serialized video_widevine::SignedDrmDeviceCertificate message.
virtual CasStatus Init(const std::string& signed_certificate);
bool HasSignedCertificate() const { return !signed_certificate_.empty(); }
const std::string& signed_certificate() const { return signed_certificate_; }
const std::string& provider_id() const { return provider_id_; }
// Encrypt the ClientIdentification message for a provisioning or
// licensing request. Encryption is performed using the current
// service certificate. Return a failure if the service certificate is
// not present, not valid, or if some other error occurs.
// The routine should not be called if privacy mode is off or if the
// certificate is empty.
CasStatus EncryptClientId(
const video_widevine::ClientIdentification* clear_client_id,
video_widevine::EncryptedClientIdentification* encrypted_client_id) const;
private:
// Encrypt data using RSA with OAEP padding.
// |plaintext| is the data to be encrypted. |ciphertext| is a pointer to a
// string to contain the decrypted data on return, and may not be null.
// returns NO_ERROR if successful or an appropriate error code otherwise.
virtual CasStatus EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext) const;
// Proto serialized SignedDrmCertificate.
// Verified by Init() to be valid.
std::string signed_certificate_;
// Certificate serial number.
std::string serial_number_;
// Provider ID, extracted from certificate message.
std::string provider_id_;
// Public key.
std::unique_ptr<RsaPublicKey> public_key_;
}; // class ServiceCertificate
} // namespace wvcas
#endif // WIDEVINE_CAS_SERVICE_CERTIFICATE_H_

View File

@@ -160,7 +160,7 @@ class WidevineCas : public wvutil::TimerHandler {
std::unique_ptr<CasLicense> cas_license_;
std::unique_ptr<wvutil::FileSystem> file_system_;
std::string device_certificate_;
std::string wrapped_rsa_key_;
std::unique_ptr<CryptoWrappedKey> wrapped_private_key_;
CasEventListener* event_listener_ = nullptr;
std::mutex lock_;
wvutil::Timer policy_timer_;

View File

@@ -91,7 +91,7 @@ class WidevineCasPlugin : public CasPlugin, public CasEventListener {
std::unique_ptr<WidevineCas> widevine_cas_api) {
widevine_cas_api_ = std::move(widevine_cas_api);
}
WidevineCasPlugin(){};
WidevineCasPlugin() {};
private:
// |sessionId| is nullptr if the event is not a session event.
@@ -107,6 +107,7 @@ class WidevineCasPlugin : public CasPlugin, public CasEventListener {
CasStatus HandleAssignLicenseID(const CasData& license_id);
CasStatus HandlePluginVersionQuery();
CasStatus HandleEntitlementPeriodUpdateResponse(const CasData& response);
status_t provisionInternal();
// Returns true if the device has been provisioned with a device certificate.
bool is_provisioned() const;