259 lines
9.9 KiB
C++
259 lines
9.9 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2016 Google LLC.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "provisioning_sdk/internal/provisioning30_session_impl.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "glog/logging.h"
|
|
#include "common/aes_cbc_util.h"
|
|
#include "common/core_message_util.h"
|
|
#include "common/hash_algorithm.h"
|
|
#include "common/hash_algorithm_util.h"
|
|
#include "common/random_util.h"
|
|
#include "common/rsa_key.h"
|
|
#include "common/sha_util.h"
|
|
#include "provisioning_sdk/internal/provisioning_engine_impl.h"
|
|
#include "provisioning_sdk/public/provisioning_status.h"
|
|
|
|
#define LOG_EVERY_N_WITH_PROTO(message, proto) \
|
|
LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n) \
|
|
<< (message) << " [proto: " << (proto).ShortDebugString() << "]"
|
|
// TODO(user): Use instantiate_test_suite_p to reorg the test case to cover
|
|
// with or withoutcore_message.
|
|
namespace widevine {
|
|
|
|
Provisioning30SessionImpl::Provisioning30SessionImpl(
|
|
const ProvisioningEngineImpl& engine, const OemDeviceCert& oem_device_cert,
|
|
const RsaPrivateKey& service_private_key)
|
|
: ProvisioningSessionImpl(engine),
|
|
oem_device_cert_(oem_device_cert),
|
|
service_private_key_(service_private_key) {}
|
|
|
|
ProvisioningStatus Provisioning30SessionImpl::ProcessMessage(
|
|
const std::string& message, std::string* response, bool* done) {
|
|
SignedProvisioningMessage signed_request;
|
|
ProvisioningRequest request;
|
|
|
|
if (!ValidateAndDeserializeRequest(message, &signed_request, &request))
|
|
return INVALID_REQUEST_MESSAGE;
|
|
|
|
ClientIdentification client_id;
|
|
if (request.has_encrypted_client_id()) {
|
|
if (!DecryptClientIdentification(request.encrypted_client_id(), &client_id))
|
|
return INVALID_REQUEST_MESSAGE;
|
|
} else {
|
|
DCHECK(request.has_client_id());
|
|
client_id.Swap(request.mutable_client_id());
|
|
}
|
|
|
|
if (client_id.type() != ClientIdentification::OEM_DEVICE_CERTIFICATE) {
|
|
LOG_EVERY_N_WITH_PROTO("Invalid client_id type", client_id);
|
|
return INVALID_REQUEST_MESSAGE;
|
|
}
|
|
if (client_id.token().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing client_id.token", client_id);
|
|
return INVALID_REQUEST_MESSAGE;
|
|
}
|
|
|
|
std::unique_ptr<RsaPublicKey> cert_public_key;
|
|
uint32_t system_id;
|
|
std::string oem_ca_serial_number;
|
|
if (!oem_device_cert_.VerifyCertificateChain(client_id.token(),
|
|
&cert_public_key, &system_id,
|
|
&oem_ca_serial_number)) {
|
|
LOG_EVERY_N_WITH_PROTO("Invalid token", client_id);
|
|
return INVALID_REQUEST_MESSAGE;
|
|
}
|
|
const HashAlgorithm hash_algorithm =
|
|
HashAlgorithmProtoToEnum(signed_request.hash_algorithm());
|
|
if (!cert_public_key->VerifySignature(
|
|
signed_request.oemcrypto_core_message() + signed_request.message(),
|
|
hash_algorithm, signed_request.signature())) {
|
|
LOG_EVERY_N_WITH_PROTO("Signature verification failed", client_id);
|
|
return INVALID_REQUEST_MESSAGE;
|
|
}
|
|
// Save device_info for query later.
|
|
device_info_ = engine_.GetDeviceInfo(system_id);
|
|
|
|
std::string certificate_serial_number;
|
|
if (request.has_spoid()) {
|
|
certificate_serial_number = request.spoid();
|
|
} else {
|
|
// Generate stable serial number.
|
|
const std::string stable_data(client_id.token() + request.stable_id() +
|
|
request.provider_id() +
|
|
engine_.secret_spoid_sauce());
|
|
const std::string hash = Sha256_Hash(stable_data);
|
|
const size_t RootCertificateSerialNumberSize = 16;
|
|
certificate_serial_number = hash.substr(0, RootCertificateSerialNumberSize);
|
|
}
|
|
|
|
ProvisioningResponse provisioning_response;
|
|
ProvisioningStatus status = GenerateProvisioningResponse(
|
|
system_id, oem_ca_serial_number, request.provider_id(),
|
|
certificate_serial_number, *cert_public_key, &provisioning_response);
|
|
if (status != OK) return status;
|
|
provisioning_response.set_nonce(request.nonce());
|
|
|
|
// Sign the response.
|
|
SignedProvisioningMessage signed_message;
|
|
if (!provisioning_response.SerializeToString(
|
|
signed_message.mutable_message())) {
|
|
LOG(WARNING) << "Error serializing ProvisioningResponse.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
if (signed_request.has_oemcrypto_core_message() &&
|
|
!signed_request.oemcrypto_core_message().empty()) {
|
|
if (!core_message_util::GetCoreProvisioningResponse(
|
|
signed_message.message(), signed_request.oemcrypto_core_message(),
|
|
signed_message.mutable_oemcrypto_core_message())) {
|
|
LOG(WARNING) << "Failed to get signed core message, response: "
|
|
<< signed_message.ShortDebugString();
|
|
return INTERNAL_ERROR;
|
|
}
|
|
if (signed_message.oemcrypto_core_message().empty()) {
|
|
LOG(WARNING) << "Failed to get signed core message, response: "
|
|
<< signed_message.ShortDebugString();
|
|
return INTERNAL_ERROR;
|
|
}
|
|
}
|
|
signed_message.set_hash_algorithm(HashAlgorithmEnumToProto(hash_algorithm));
|
|
if (!service_private_key_.GenerateSignature(
|
|
signed_message.oemcrypto_core_message() + signed_message.message(),
|
|
hash_algorithm, signed_message.mutable_signature())) {
|
|
LOG(WARNING) << "Failed to sign ProvisioningResponse.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
if (!signed_message.SerializeToString(response)) {
|
|
LOG(WARNING) << "Error serializing SignedProvisioningMessage.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
*done = true;
|
|
return OK;
|
|
}
|
|
|
|
bool Provisioning30SessionImpl::ValidateAndDeserializeRequest(
|
|
const std::string& message, SignedProvisioningMessage* signed_request,
|
|
ProvisioningRequest* request) const {
|
|
if (!signed_request->ParseFromString(message)) {
|
|
LOG_EVERY_N(WARNING, FLAGS_prov_sdk_log_every_n)
|
|
<< "Failed to parse SignedProvisioningMessage.";
|
|
return false;
|
|
}
|
|
VLOG(1) << "signed_request: " << signed_request->ShortDebugString();
|
|
|
|
if (signed_request->message().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing message", *signed_request);
|
|
return false;
|
|
}
|
|
if (signed_request->signature().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing signature", *signed_request);
|
|
return false;
|
|
}
|
|
if (!request->ParseFromString(signed_request->message())) {
|
|
LOG_EVERY_N_WITH_PROTO("Failed to parse ProvisioningRequest",
|
|
*signed_request);
|
|
return false;
|
|
}
|
|
|
|
if (request->has_encrypted_client_id()) {
|
|
const EncryptedClientIdentification& encrypted_client_id =
|
|
request->encrypted_client_id();
|
|
if (encrypted_client_id.encrypted_client_id().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id",
|
|
encrypted_client_id);
|
|
return false;
|
|
}
|
|
if (encrypted_client_id.encrypted_client_id_iv().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_client_id_iv",
|
|
encrypted_client_id);
|
|
return false;
|
|
}
|
|
if (encrypted_client_id.encrypted_privacy_key().empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing encrypted_privacy_key",
|
|
encrypted_client_id);
|
|
return false;
|
|
}
|
|
} else if (!request->has_client_id()) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing clear_or_encrypted_client_id", *request);
|
|
return false;
|
|
}
|
|
|
|
const size_t kMinimumRequiredNonceLength = 4;
|
|
if (request->nonce().size() < kMinimumRequiredNonceLength) {
|
|
LOG_EVERY_N_WITH_PROTO("Missing or invalid nonce", *request);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Provisioning30SessionImpl::DecryptClientIdentification(
|
|
const EncryptedClientIdentification& encrypted_client_id,
|
|
ClientIdentification* client_id) {
|
|
std::string privacy_key;
|
|
if (!service_private_key_.Decrypt(encrypted_client_id.encrypted_privacy_key(),
|
|
&privacy_key)) {
|
|
LOG_EVERY_N_WITH_PROTO("Failed to decrypt encrypted_privacy_key",
|
|
encrypted_client_id);
|
|
return false;
|
|
}
|
|
std::string serialized_client_id(crypto_util::DecryptAesCbc(
|
|
privacy_key, encrypted_client_id.encrypted_client_id_iv(),
|
|
encrypted_client_id.encrypted_client_id()));
|
|
if (serialized_client_id.empty()) {
|
|
LOG_EVERY_N_WITH_PROTO("Failed to decrypt client_id", encrypted_client_id);
|
|
return false;
|
|
}
|
|
if (!client_id->ParseFromString(serialized_client_id)) {
|
|
LOG_EVERY_N_WITH_PROTO("Failed to parse client_id", encrypted_client_id);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ProvisioningStatus Provisioning30SessionImpl::GenerateProvisioningResponse(
|
|
uint32_t system_id, const std::string& oem_ca_serial_number,
|
|
const std::string& provider_id,
|
|
const std::string& certificate_serial_number,
|
|
const RsaPublicKey& cert_public_key, ProvisioningResponse* response) {
|
|
ProvisioningStatus status = engine_.GenerateProviderDeviceDrmCertificate(
|
|
system_id, oem_ca_serial_number, provider_id, device_drm_public_key_,
|
|
certificate_serial_number, response->mutable_device_certificate());
|
|
if (status != OK) return status;
|
|
|
|
const size_t kAesKeySize = 16;
|
|
const size_t kIvSize = 16;
|
|
|
|
// Encrypt private key.
|
|
std::string message_key;
|
|
if (!RandomBytes(kAesKeySize, &message_key)) {
|
|
LOG(WARNING) << "Failed to generate message_key.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
std::string iv;
|
|
if (!RandomBytes(kIvSize, &iv)) {
|
|
LOG(WARNING) << "Failed to generate iv.";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
response->set_device_rsa_key_iv(iv);
|
|
response->set_device_rsa_key(
|
|
crypto_util::EncryptAesCbc(message_key, iv, device_drm_private_key_));
|
|
if (response->device_rsa_key().empty()) {
|
|
LOG(WARNING) << "Failed to encrypt device_rsa_key";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
if (!cert_public_key.Encrypt(message_key, response->mutable_wrapping_key())) {
|
|
LOG(WARNING) << "Failed to encrypt wrapping_key";
|
|
return INTERNAL_ERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
} // namespace widevine
|