Files
provisioning_sdk_source/provisioning_sdk/internal/provisioning30_session_impl.cc
2020-09-21 15:54:27 -07:00

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