[ Cherry-pick of v19 http://go/wvgerrit/219291 ] [ Merge of http://go/wvgerrit/219432 ] If the same app/origin generates multiple provisioning 4.0 requests it is possible that a mismatch between the OEM/DRM certificate and the wrapped OEM/DRM private key occurs. The CDM would use the OEM/DRM certificate of the first response one received, and the wrapped private key of the last request generated. To avoid this issue, the public key from the most recent request is cached and checked against the responses received. If the keys match, that response is accepted; if the keys don't match than the response is assumed "stale" and the response is dropped. In an attempt to maintain existing behavior of the CDM, "stale" responses will return NO_ERROR to the app. Note: This was tested using both RSA and ECC cert key types. VIC-specific: Needed to add implementation of StringContains() and StringEndsWith(). Bug: 391469176 Test: run_prov40_tests Change-Id: Id45d40d9af355c46a61c3cc2c19c252cf17c7489
175 lines
7.0 KiB
C++
175 lines
7.0 KiB
C++
// 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 WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
|
#define WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "crypto_session.h"
|
|
#include "disallow_copy_and_assign.h"
|
|
#include "license_protocol.pb.h"
|
|
#include "metrics_collections.h"
|
|
#include "oemcrypto_adapter.h"
|
|
#include "service_certificate.h"
|
|
#include "wv_cdm_types.h"
|
|
|
|
namespace wvutil {
|
|
class FileSystem;
|
|
}
|
|
|
|
namespace wvcdm {
|
|
|
|
class CdmClientPropertySet;
|
|
class CdmSession;
|
|
class ServiceCertificate;
|
|
|
|
class CertificateProvisioning {
|
|
public:
|
|
CertificateProvisioning(metrics::CryptoMetrics* metrics)
|
|
: crypto_session_(CryptoSession::MakeCryptoSession(metrics)),
|
|
cert_type_(kCertificateWidevine),
|
|
service_certificate_(new ServiceCertificate()) {}
|
|
~CertificateProvisioning() {}
|
|
|
|
CdmResponseType Init(const std::string& service_certificate);
|
|
|
|
// Construct a valid provisioning request.
|
|
// The request will be sent to the provisioning server.
|
|
CdmResponseType GetProvisioningRequest(
|
|
wvutil::FileSystem* file_system,
|
|
RequestedSecurityLevel requested_security_level,
|
|
CdmCertificateType cert_type, const std::string& cert_authority,
|
|
const std::string& origin, const std::string& spoid,
|
|
CdmProvisioningRequest* request, std::string* default_url);
|
|
|
|
// Process the provisioning response.
|
|
CdmResponseType HandleProvisioningResponse(
|
|
wvutil::FileSystem* file_system, const CdmProvisioningResponse& response,
|
|
std::string* cert, std::string* wrapped_key);
|
|
|
|
// Helper methods
|
|
|
|
// Extract serial number and system ID from a DRM Device certificate.
|
|
// Either |serial_number| or |system_id| may be null, but not both.
|
|
// Both |creation_time_seconds| and |expiration_time_seconds| may be null.
|
|
// |creation_time_seconds| and |expiration_time_seconds| will be set to -1
|
|
// if not present, 0 if unlimited and a valid time otherwise
|
|
static bool ExtractDeviceInfo(const std::string& device_certificate,
|
|
std::string* serial_number, uint32_t* system_id,
|
|
int64_t* creation_time_seconds,
|
|
int64_t* expiration_time_seconds);
|
|
|
|
// Removes json wrapping if applicable to extract the
|
|
// SignedProvisioningMessage
|
|
static bool ExtractAndDecodeSignedMessage(
|
|
const std::string& provisioning_response, std::string* result);
|
|
|
|
// Retrieve the provisioning server URL used for certificate
|
|
// provisioning. This will be the same value as returned in
|
|
// |default_url| by GetProvisioningRequest().
|
|
static void GetProvisioningServerUrl(std::string* default_url);
|
|
|
|
enum State {
|
|
// Freshly created, not yet initialized.
|
|
kUninitialized,
|
|
// A successful call to Init() has been made.
|
|
kInitialized,
|
|
// Has generated a DRM request; apps are allowed generate
|
|
// another one even if a response has not been received.
|
|
kDrmRequestSent,
|
|
// Has received (and successfully loaded) a DRM response.
|
|
kDrmResponseReceived,
|
|
// Has generated an OEM (Prov 4.0) request; apps are allowed
|
|
// generate another one even if a response has not been
|
|
// received.
|
|
kOemRequestSent,
|
|
// Has received (and successfully loaded) an OEM response.
|
|
kOemResponseReceived,
|
|
};
|
|
static const char* StateToString(State state);
|
|
|
|
// State setter for testing only.
|
|
void SetStateForTesting(State state) { state_ = state; }
|
|
|
|
private:
|
|
#if defined(UNIT_TEST)
|
|
friend class CertificateProvisioningTest;
|
|
#endif
|
|
CdmResponseType GetProvisioningRequestInternal(
|
|
wvutil::FileSystem* file_system,
|
|
RequestedSecurityLevel requested_security_level,
|
|
CdmCertificateType cert_type, const std::string& cert_authority,
|
|
const std::string& origin, const std::string& spoid,
|
|
CdmProvisioningRequest* request, std::string* default_url);
|
|
CdmResponseType GetProvisioning40RequestInternal(
|
|
wvutil::FileSystem* file_system, const std::string& origin,
|
|
const std::string& spoid, CdmProvisioningRequest* request,
|
|
std::string* default_url, CdmCertificateType cert_type,
|
|
const std::string& cert_authority);
|
|
CdmResponseType FillEncryptedClientId(
|
|
const std::string& client_token,
|
|
video_widevine::ProvisioningRequest& provisioning_request,
|
|
const ServiceCertificate& service_certificate);
|
|
CdmResponseType FillEncryptedClientIdWithAdditionalParameter(
|
|
const std::string& client_token,
|
|
const CdmAppParameterMap& additional_parameter,
|
|
video_widevine::ProvisioningRequest& provisioning_request,
|
|
const ServiceCertificate& service_certificate);
|
|
CdmResponseType HandleProvisioning40Response(
|
|
wvutil::FileSystem* file_system,
|
|
const video_widevine::SignedProvisioningMessage& signed_message,
|
|
std::string* cert, std::string* wrapped_key);
|
|
// Assign the cert type for provisioning request
|
|
// Required by Cast cert provisioning flow
|
|
CdmResponseType CertTypeAssign(
|
|
video_widevine::ProvisioningRequest& provisioning_request,
|
|
CdmCertificateType cert_type, const std::string& cert_authority);
|
|
|
|
CdmResponseType SetSpoidParameter(
|
|
const std::string& origin, const std::string& spoid,
|
|
video_widevine::ProvisioningRequest* request);
|
|
|
|
video_widevine::SignedProvisioningMessage::ProvisioningType
|
|
GetProvisioningType();
|
|
|
|
// Closes crypto session if one is open. Avoid calling this method when
|
|
// processing a response. Multiple provisioning responses might be
|
|
// simultaneously in flight. Only the response associated with the last
|
|
// provisioning request can be processed. All the other responses will
|
|
// fail. If the session is closed when these responses fail, even the one
|
|
// associated with the last provisioning request may fail.
|
|
CdmResponseType CloseSessionOnError(CdmResponseType status);
|
|
void CloseSession();
|
|
|
|
// Tracks the state of CertificateProvisioning.
|
|
State state_ = kUninitialized;
|
|
|
|
std::unique_ptr<CryptoSession> crypto_session_;
|
|
CdmCertificateType cert_type_;
|
|
std::unique_ptr<ServiceCertificate> service_certificate_;
|
|
std::string request_;
|
|
|
|
// == Provisioning 4.0 Variables ==
|
|
// The wrapped private key in provisioning 4 generated by calling
|
|
// GenerateCertificateKeyPair. It will be saved to file system if a valid
|
|
// response is received.
|
|
CryptoWrappedKey prov40_wrapped_private_key_;
|
|
// Cache of the most recently sent OEM/DRM public key sent. Used
|
|
// to match the response with the request.
|
|
// This MUST be matched with the current |prov40_wrapped_private_key_|.
|
|
std::string prov40_public_key_;
|
|
|
|
// Store the last provisioning request message.
|
|
// This is the serialized ProvisioningRequest.
|
|
// Used for X.509 responses which require the original
|
|
// request to verify the signature of the response.
|
|
std::string prov40_request_;
|
|
|
|
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
|
|
}; // class CertificateProvisioning
|
|
} // namespace wvcdm
|
|
#endif // WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|