// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine // License Agreement. // // Reference implementation of OEMCrypto APIs // #include "oemcrypto_session.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "advance_iv_ctr.h" #include "disallow_copy_and_assign.h" #include "keys.h" #include "log.h" #include "odk.h" #include "oemcrypto_engine_ref.h" #include "oemcrypto_key_ref.h" #include "oemcrypto_rsa_key_shared.h" #include "oemcrypto_types.h" #include "platform.h" #include "string_conversions.h" #include "wvcrc32.h" static const int kPssSaltLength = 20; namespace { // Increment counter for AES-CTR. The CENC spec specifies we increment only // the low 64 bits of the IV counter, and leave the high 64 bits alone. void ctr128_inc64(uint8_t* counter) { uint32_t n = 16; do { if (++counter[--n] != 0) return; } while (n > 8); } void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) { switch (dest_buffer->type) { case OEMCrypto_BufferType_Clear: dest_buffer->buffer.clear.address += bytes; dest_buffer->buffer.clear.address_length -= bytes; break; case OEMCrypto_BufferType_Secure: dest_buffer->buffer.secure.offset += bytes; break; case OEMCrypto_BufferType_Direct: // Nothing to do for this buffer type. break; } } } // namespace namespace wvoec_ref { /***************************************/ class ContentKeysContext : public SessionContextKeys { public: explicit ContentKeysContext() {} ~ContentKeysContext() override {} size_t size() override { return session_keys_.size(); } bool Insert(const KeyId& key_id, const Key& key_data) override; Key* Find(const KeyId& key_id) override; Key* FirstKey() override; void Remove(const KeyId& key_id) override; void UpdateDuration(const KeyControlBlock& control) override; OEMCrypto_LicenseType type() override { return OEMCrypto_ContentLicense; } bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) override; EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override; private: SessionKeyTable session_keys_; CORE_DISALLOW_COPY_AND_ASSIGN(ContentKeysContext); }; bool ContentKeysContext::Insert(const KeyId& key_id, const Key& key_data) { return session_keys_.Insert(key_id, key_data); } Key* ContentKeysContext::Find(const KeyId& key_id) { return session_keys_.Find(key_id); } Key* ContentKeysContext::FirstKey() { return session_keys_.FirstKey(); } void ContentKeysContext::Remove(const KeyId& key_id) { session_keys_.Remove(key_id); } void ContentKeysContext::UpdateDuration(const KeyControlBlock& control) { session_keys_.UpdateDuration(control); } bool ContentKeysContext::SetContentKey( const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) { // Unsupported action for this type. return false; } EntitlementKey* ContentKeysContext::GetEntitlementKey( const KeyId& entitlement_id) { // Unsupported action for this type. return nullptr; } /***************************************/ class EntitlementKeysContext : public SessionContextKeys { public: EntitlementKeysContext() {} ~EntitlementKeysContext() override {} size_t size() override { return session_keys_.size(); } bool Insert(const KeyId& key_id, const Key& key_data) override; Key* Find(const KeyId& key_id) override; Key* FirstKey() override; void Remove(const KeyId& key_id) override; void UpdateDuration(const KeyControlBlock& control) override; bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) override; EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override; OEMCrypto_LicenseType type() override { return OEMCrypto_EntitlementLicense; } private: EntitlementKeyTable session_keys_; CORE_DISALLOW_COPY_AND_ASSIGN(EntitlementKeysContext); }; bool EntitlementKeysContext::Insert(const KeyId& key_id, const Key& key_data) { return session_keys_.Insert(key_id, key_data); } Key* EntitlementKeysContext::Find(const KeyId& key_id) { return session_keys_.Find(key_id); } Key* EntitlementKeysContext::FirstKey() { return session_keys_.FirstKey(); } void EntitlementKeysContext::Remove(const KeyId& key_id) { session_keys_.Remove(key_id); } void EntitlementKeysContext::UpdateDuration(const KeyControlBlock& control) { session_keys_.UpdateDuration(control); } bool EntitlementKeysContext::SetContentKey( const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) { return session_keys_.SetContentKey(entitlement_id, content_key_id, content_key); } EntitlementKey* EntitlementKeysContext::GetEntitlementKey( const KeyId& entitlement_id) { return session_keys_.GetEntitlementKey(entitlement_id); } /***************************************/ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key) : valid_(true), ce_(ce), id_(sid), current_content_key_(nullptr), session_keys_(nullptr), license_request_hash_(), rsa_key_(rsa_key), allowed_schemes_(kSign_RSASSA_PSS), decrypt_started_(false), timer_limits_(), clock_values_(), usage_entry_(nullptr), srm_requirements_status_(NoSRMVersion), usage_entry_status_(kNoUsageEntry), compute_hash_(false), current_hash_(0), bad_frame_number_(0), hash_error_(OEMCrypto_SUCCESS), state_nonce_created_(false), state_request_signed_(false), state_response_loaded_(false) { ODK_InitializeSessionValues(&timer_limits_, &clock_values_, &nonce_values_, CryptoEngine::kApiVersion, sid); } SessionContext::~SessionContext() {} // Internal utility function to derive key using CMAC-128 bool SessionContext::DeriveKey(const std::vector& key, const std::vector& context, int counter, std::vector* out) { if (key.empty() || counter > 4 || context.empty() || out == nullptr) { LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return false; } const EVP_CIPHER* cipher = EVP_aes_128_cbc(); CMAC_CTX* cmac_ctx = CMAC_CTX_new(); if (!cmac_ctx) { LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); return false; } if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); CMAC_CTX_free(cmac_ctx); return false; } std::vector message; message.push_back(counter); message.insert(message.end(), context.begin(), context.end()); if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); CMAC_CTX_free(cmac_ctx); return false; } size_t reslen; uint8_t res[128]; if (!CMAC_Final(cmac_ctx, res, &reslen)) { LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); CMAC_CTX_free(cmac_ctx); return false; } out->assign(res, res + reslen); CMAC_CTX_free(cmac_ctx); return true; } bool SessionContext::DeriveKeys(const std::vector& master_key, const std::vector& mac_key_context, const std::vector& enc_key_context) { // Generate derived key for mac key std::vector mac_key_server; std::vector mac_key_client; std::vector mac_key_part2; if (!DeriveKey(master_key, mac_key_context, 1, &mac_key_server)) { return false; } if (!DeriveKey(master_key, mac_key_context, 2, &mac_key_part2)) { return false; } mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(), mac_key_part2.end()); if (!DeriveKey(master_key, mac_key_context, 3, &mac_key_client)) { return false; } if (!DeriveKey(master_key, mac_key_context, 4, &mac_key_part2)) { return false; } mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(), mac_key_part2.end()); // Generate derived key for encryption key std::vector enc_key; if (!DeriveKey(master_key, enc_key_context, 1, &enc_key)) { return false; } set_mac_key_server(mac_key_server); set_mac_key_client(mac_key_client); set_encryption_key(enc_key); return true; } bool SessionContext::RSADeriveKeys( const std::vector& enc_session_key, const std::vector& mac_key_context, const std::vector& enc_key_context) { if (!rsa_key()) { LOGE("[RSADeriveKeys(): no RSA key set]"); return false; } const size_t actual_key_size = static_cast(RSA_size(rsa_key())); if (enc_session_key.size() != actual_key_size) { LOGE( "[RSADeriveKeys(): encrypted session key wrong size: %zu, expected " "%zu]", enc_session_key.size(), actual_key_size); dump_boringssl_error(); return false; } session_key_.resize(RSA_size(rsa_key())); const int decrypted_size = RSA_private_decrypt(enc_session_key.size(), &enc_session_key[0], &session_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); if (-1 == decrypted_size) { LOGE("[RSADeriveKeys(): error decrypting session key.]"); dump_boringssl_error(); return false; } session_key_.resize(decrypted_size); if (decrypted_size != static_cast(wvoec::KEY_SIZE)) { LOGE("[RSADeriveKeys(): error. Session key is wrong size: %d.]", decrypted_size); dump_boringssl_error(); session_key_.clear(); return false; } return DeriveKeys(session_key_, mac_key_context, enc_key_context); } OEMCryptoResult SessionContext::PrepAndSignLicenseRequest( uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (signature_length == nullptr || core_message_length == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } const size_t required_signature_size = CertSignatureSize(); OEMCryptoResult result = ODK_PrepareCoreLicenseRequest( message, message_length, core_message_length, &nonce_values_); if (*signature_length < required_signature_size || result == OEMCrypto_ERROR_SHORT_BUFFER) { *signature_length = required_signature_size; return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length < *core_message_length || signature == nullptr) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (state_request_signed_) { LOGE("Attempt to sign two license requests"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // For backwards compatibility, we only sign the message body, and we compute // a SHA256 of the core message. SHA256(message, *core_message_length, license_request_hash_); const uint8_t* message_body = message + *core_message_length; const size_t message_body_length = message_length - *core_message_length; result = GenerateCertSignature(message_body, message_body_length, signature, signature_length); if (result == OEMCrypto_SUCCESS) { state_request_signed_ = true; result = ODK_InitializeClockValues(&clock_values_, ce_->SystemTime()); } return result; } OEMCryptoResult SessionContext::PrepAndSignRenewalRequest( uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (signature_length == nullptr || core_message_length == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } // If we have signed a request, but have not loaded it, something is wrong. // On the other hand, we can sign a license release using the mac keys from // the usage table. So it is OK if we have never signed a license request. if (state_request_signed_ && !state_response_loaded_) { LOGE("Attempt to sign renewal before load"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const size_t required_signature_size = SHA256_DIGEST_LENGTH; const uint64_t now = ce_->SystemTime(); const OEMCryptoResult result = ODK_PrepareCoreRenewalRequest( message, message_length, core_message_length, &nonce_values_, &clock_values_, now); if (*signature_length < required_signature_size || result == OEMCrypto_ERROR_SHORT_BUFFER) { *signature_length = required_signature_size; return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length < *core_message_length || signature == nullptr) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } // If we are talking to an old license server, then we only sign the message // body. if (nonce_values_.api_major_version < 16) { const uint8_t* message_body = message + *core_message_length; const size_t message_body_length = message_length - *core_message_length; return GenerateSignature(message_body, message_body_length, signature, signature_length); } else { return GenerateSignature(message, message_length, signature, signature_length); } } OEMCryptoResult SessionContext::PrepAndSignProvisioningRequest( uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (signature_length == nullptr || core_message_length == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } if (state_request_signed_) { LOGE("Attempt to sign prov request after license request"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const size_t required_signature_size = ROTSignatureSize(); if (required_signature_size == 0) return OEMCrypto_ERROR_UNKNOWN_FAILURE; const std::vector device_id = ce_->DeviceRootId(); OEMCryptoResult result = ODK_PrepareCoreProvisioningRequest( message, message_length, core_message_length, &nonce_values_, device_id.data(), device_id.size()); if (*signature_length < required_signature_size || result == OEMCrypto_ERROR_SHORT_BUFFER) { *signature_length = required_signature_size; return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length == 0 || signature == nullptr) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (ce_->config_provisioning_method() == OEMCrypto_Keybox) { result = GenerateSignature(message, message_length, signature, signature_length); } else if (ce_->config_provisioning_method() == OEMCrypto_OEMCertificate) { result = GenerateCertSignature(message, message_length, signature, signature_length); } else { LOGE("Bad prov method = %d", static_cast(ce_->config_provisioning_method())); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; return result; } // Utility function to generate a message signature OEMCryptoResult SessionContext::GenerateSignature(const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length) { if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == nullptr) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (mac_key_client_.size() != wvoec::MAC_KEY_SIZE) { return OEMCrypto_ERROR_INVALID_CONTEXT; } if (*signature_length != SHA256_DIGEST_LENGTH) { *signature_length = SHA256_DIGEST_LENGTH; return OEMCrypto_ERROR_SHORT_BUFFER; } unsigned int md_len = *signature_length; if (HMAC(EVP_sha256(), &mac_key_client_[0], wvoec::MAC_KEY_SIZE, message, message_length, signature, &md_len)) { *signature_length = md_len; return OEMCrypto_SUCCESS; } return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // This is ussd when the device is a cast receiver. size_t SessionContext::RSASignatureSize() { if (!rsa_key()) { LOGE("no RSA key set"); return 0; } return static_cast(RSA_size(rsa_key())); } size_t SessionContext::CertSignatureSize() { // TODO(b/67735947): Add ECC cert support. if (!rsa_key()) { LOGE("No private key set"); return 0; } return static_cast(RSA_size(rsa_key())); } size_t SessionContext::ROTSignatureSize() { if (ce_->config_provisioning_method() == OEMCrypto_Keybox) return SHA256_DIGEST_LENGTH; if (ce_->config_provisioning_method() == OEMCrypto_OEMCertificate) return CertSignatureSize(); LOGE("Bad prov method = %d", static_cast(ce_->config_provisioning_method())); return 0; } OEMCryptoResult SessionContext::GenerateCertSignature( const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length) { // TODO(b/67735947): Add ECC cert support. if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == 0) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!rsa_key()) { LOGE("No RSA key set"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } if (*signature_length < static_cast(RSA_size(rsa_key()))) { *signature_length = CertSignatureSize(); return OEMCrypto_ERROR_SHORT_BUFFER; } if (allowed_schemes_ != kSign_RSASSA_PSS) { LOGE("Message signing not allowed"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } // Hash the message using SHA1. uint8_t hash[SHA_DIGEST_LENGTH]; if (!SHA1(message, message_length, hash)) { LOGE("Error creating signature hash"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // Add PSS padding. std::vector padded_digest(*signature_length); int status = RSA_padding_add_PKCS1_PSS_mgf1( rsa_key(), &padded_digest[0], hash, EVP_sha1(), nullptr, kPssSaltLength); if (status == -1) { LOGE("Error padding hash"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // Encrypt PSS padded digest. status = RSA_private_encrypt(*signature_length, &padded_digest[0], signature, rsa_key(), RSA_NO_PADDING); if (status == -1) { LOGE("Error in private encrypt"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::GenerateRSASignature( const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) { if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == 0) { LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!rsa_key()) { LOGE("No RSA key set"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } if (*signature_length < static_cast(RSA_size(rsa_key()))) { *signature_length = RSA_size(rsa_key()); return OEMCrypto_ERROR_SHORT_BUFFER; } if (((padding_scheme & allowed_schemes_) != padding_scheme) || (padding_scheme != kSign_PKCS1_Block1)) { LOGE("padding_scheme not allowed"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } // This is the maximum digest size possible for PKCS1 block type 1, // as used for a CAST receiver. const size_t max_digest_size = 83u; if (message_length > max_digest_size) { LOGE("RSA digest too large"); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } // Pad the message with PKCS1 padding, and then encrypt. const int status = RSA_private_encrypt(message_length, message, signature, rsa_key(), RSA_PKCS1_PADDING); if (status < 0) { LOGE("Error in RSA private encrypt. status = %d", status); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } *signature_length = static_cast(RSA_size(rsa_key())); return OEMCrypto_SUCCESS; } // Validate message signature bool SessionContext::ValidateMessage(const uint8_t* given_message, size_t message_length, const uint8_t* given_signature, size_t signature_length) { if (signature_length != SHA256_DIGEST_LENGTH) { return false; } uint8_t computed_signature[SHA256_DIGEST_LENGTH]; memset(computed_signature, 0, SHA256_DIGEST_LENGTH); unsigned int md_len = SHA256_DIGEST_LENGTH; if (!HMAC(EVP_sha256(), mac_key_server_.data(), mac_key_server_.size(), given_message, message_length, computed_signature, &md_len)) { LOGE("ValidateMessage: Could not compute signature"); return false; } if (CRYPTO_memcmp(given_signature, computed_signature, signature_length)) { LOGE("Invalid signature given: %s", wvcdm::HexEncode(given_signature, signature_length).c_str()); LOGE("Invalid signature computed: %s", wvcdm::HexEncode(computed_signature, signature_length).c_str()); return false; } return true; } OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, uint32_t control) { if (!(control & wvoec::kControlNonceEnabled)) { LOGE("LoadKeys: Server provided Nonce_Required but Nonce_Enabled = 0"); // Server error. Continue, and assume nonce required. } if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; switch (usage_entry_status_) { case kNoUsageEntry: LOGE("LoadKeys: Session did not create usage entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryLoaded: LOGE("LoadKeys: Session reloaded existing entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryNew: return OEMCrypto_SUCCESS; default: // invalid status. return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, uint32_t control) { if (control & wvoec::kControlNonceEnabled) { LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1"); // Server error. Continue, and assume nonce required. } switch (usage_entry_status_) { case kNoUsageEntry: LOGE("LoadKeys: Session did not create or load usage entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryLoaded: // Repeat load. Calling function will verify pst and keys. return OEMCrypto_SUCCESS; case kUsageEntryNew: // First load. Verify nonce. if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; return OEMCrypto_SUCCESS; default: // invalid status. return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } OEMCryptoResult SessionContext::CheckNonceOrEntry( const KeyControlBlock& key_control_block) { switch (key_control_block.control_bits() & wvoec::kControlReplayMask) { case wvoec::kControlNonceRequired: // Online license. Nonce always // required. return CheckStatusOnline(key_control_block.nonce(), key_control_block.control_bits()); break; case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on // first use. return CheckStatusOffline(key_control_block.nonce(), key_control_block.control_bits()); break; default: if ((key_control_block.control_bits() & wvoec::kControlNonceEnabled) && (!CheckNonce(key_control_block.nonce()))) { LOGE("LoadKeys: BAD Nonce"); return OEMCrypto_ERROR_INVALID_NONCE; } } return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::LoadLicense(const uint8_t* message, size_t message_length, size_t core_message_length, const uint8_t* signature, size_t signature_length) { // Check state before we check signature. State is change in // LoadKeysNoSignature. if (state_response_loaded_) { return OEMCrypto_ERROR_LICENSE_RELOAD; } ODK_ParsedLicense parsed_response; const bool initial_license_load = (usage_entry_status_ != kUsageEntryLoaded); const OEMCryptoResult result = ODK_ParseLicense( message, message_length, core_message_length, initial_license_load, usage_entry_present(), license_request_hash_, &timer_limits_, &clock_values_, &nonce_values_, &parsed_response); if (result != OEMCrypto_SUCCESS) { LOGE("ODK Error %d", static_cast(result)); return result; } // Validate message signature if (!ValidateMessage(message, message_length, signature, signature_length)) { return OEMCrypto_ERROR_SIGNATURE_FAILURE; } const uint8_t* message_body = message + core_message_length; const size_t message_body_length = message_length - core_message_length; return LoadKeysNoSignature( message_body, message_body_length, parsed_response.enc_mac_keys_iv, parsed_response.enc_mac_keys, parsed_response.key_array_length, parsed_response.key_array, parsed_response.pst, parsed_response.srm_restriction_data, static_cast(parsed_response.license_type)); } OEMCryptoResult SessionContext::LoadKeys( const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, size_t num_keys, const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, OEMCrypto_LicenseType license_type) { // Check state before we check signature. State is change in // LoadKeysNoSignature. if (state_response_loaded_) { return OEMCrypto_ERROR_LICENSE_RELOAD; } // Validate message signature if (!ValidateMessage(message, message_length, signature, signature_length)) { return OEMCrypto_ERROR_SIGNATURE_FAILURE; } OEMCryptoResult result = LoadKeysNoSignature( message, message_length, enc_mac_keys_iv, enc_mac_keys, num_keys, key_array, pst, srm_restriction_data, license_type); if (result != OEMCrypto_SUCCESS) return result; Key* key = session_keys_->FirstKey(); uint32_t duration = key ? key->control().duration() : 0; result = ODK_InitializeV15Values(&timer_limits_, &clock_values_, &nonce_values_, duration, ce_->SystemTime()); // TODO(b/140765227): clear session on errors return result; } OEMCryptoResult SessionContext::LoadKeysNoSignature( const uint8_t* message, size_t message_length, OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, size_t num_keys, const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, OEMCrypto_LicenseType license_type) { if (state_response_loaded_) { return OEMCrypto_ERROR_LICENSE_RELOAD; } state_response_loaded_ = true; if (num_keys < 1) return OEMCrypto_ERROR_INVALID_CONTEXT; if (session_keys_ == nullptr) { switch (license_type) { case OEMCrypto_ContentLicense: session_keys_.reset(new ContentKeysContext()); break; case OEMCrypto_EntitlementLicense: session_keys_.reset(new EntitlementKeysContext()); break; default: return OEMCrypto_ERROR_INVALID_CONTEXT; } } else { if (session_keys_->type() != license_type) { return OEMCrypto_ERROR_INVALID_CONTEXT; } } if (srm_restriction_data.length != 0) { const std::string kSRMVerificationString = "HDCPDATA"; if (memcmp(message + srm_restriction_data.offset, kSRMVerificationString.c_str(), kSRMVerificationString.size())) { LOGE("SRM Requirement Data has bad verification string: %8s", message + srm_restriction_data.offset); return OEMCrypto_ERROR_INVALID_CONTEXT; } const uint32_t minimum_version = htonl(*reinterpret_cast( message + srm_restriction_data.offset + 8)); uint16_t current_version = 0; if (OEMCrypto_SUCCESS != ce_->current_srm_version(¤t_version)) { LOGW("[LoadKeys: SRM Version not available"); srm_requirements_status_ = InvalidSRMVersion; } else if (current_version < minimum_version) { LOGW("[LoadKeys: SRM Version is too small %u, required: %u", current_version, minimum_version); srm_requirements_status_ = InvalidSRMVersion; } else if (ce_->srm_forbidden_device_attached()) { LOGW("[LoadKeys: SRM forbidden device attached]"); srm_requirements_status_ = InvalidSRMVersion; } else { LOGI("[LoadKeys: SRM Versions is %u, required: %u]", current_version, minimum_version); srm_requirements_status_ = ValidSRMVersion; } } // Decrypt and install keys in key object // Each key will have a key control block. They will all have the same nonce. OEMCryptoResult status = OEMCrypto_SUCCESS; std::vector key_id; std::vector enc_key_data; std::vector key_data_iv; std::vector key_control; std::vector key_control_iv; for (unsigned int i = 0; i < num_keys; i++) { key_id.assign( message + key_array[i].key_id.offset, message + key_array[i].key_id.offset + key_array[i].key_id.length); enc_key_data.assign( message + key_array[i].key_data.offset, message + key_array[i].key_data.offset + key_array[i].key_data.length); key_data_iv.assign( message + key_array[i].key_data_iv.offset, message + key_array[i].key_data_iv.offset + wvoec::KEY_IV_SIZE); if (key_array[i].key_control.length == 0) { status = OEMCrypto_ERROR_UNKNOWN_FAILURE; break; } key_control.assign( message + key_array[i].key_control.offset, message + key_array[i].key_control.offset + wvoec::KEY_CONTROL_SIZE); key_control_iv.assign( message + key_array[i].key_control_iv.offset, message + key_array[i].key_control_iv.offset + wvoec::KEY_IV_SIZE); OEMCryptoResult result = InstallKey(key_id, enc_key_data, key_data_iv, key_control, key_control_iv); if (result != OEMCrypto_SUCCESS) { status = result; break; } } if (status != OEMCrypto_SUCCESS) return status; // enc_mac_key can be nullptr if license renewal is not supported if (enc_mac_keys.length != 0) { // V2.1 license protocol: update mac keys after processing license response const std::vector enc_mac_keys_str = std::vector( message + enc_mac_keys.offset, message + enc_mac_keys.offset + 2 * wvoec::MAC_KEY_SIZE); const std::vector enc_mac_key_iv_str = std::vector( message + enc_mac_keys_iv.offset, message + enc_mac_keys_iv.offset + wvoec::KEY_IV_SIZE); if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) { LOGE("Failed to update mac keys."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } else { // If the mac keys are not updated, we will not need them again. mac_key_server_.resize(0); mac_key_client_.resize(0); } if (usage_entry_) { OEMCryptoResult result = OEMCrypto_SUCCESS; switch (usage_entry_status_) { case kNoUsageEntry: if (pst.length > 0) { LOGE("LoadKeys: PST specified but no usage entry loaded"); return OEMCrypto_ERROR_INVALID_CONTEXT; } break; // no extra check. case kUsageEntryNew: result = usage_entry_->SetPST(message + pst.offset, pst.length); if (result != OEMCrypto_SUCCESS) { return result; } if (!usage_entry_->SetMacKeys(mac_key_server_, mac_key_client_)) { LOGE("LoadKeys: Usage table can't set keys."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } break; case kUsageEntryLoaded: if (!usage_entry_->VerifyPST(message + pst.offset, pst.length)) { return OEMCrypto_ERROR_WRONG_PST; } if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { LOGE("LoadKeys: Usage table entry mac keys do not match."); return OEMCrypto_ERROR_WRONG_KEYS; } if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; break; } } encryption_key_.clear(); return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::LoadEntitledContentKeys( const uint8_t* message, size_t message_length, size_t key_array_length, const OEMCrypto_EntitledContentKeyObject* key_array) { if (!key_array) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (session_keys_ == nullptr || session_keys_->type() != OEMCrypto_EntitlementLicense) { return OEMCrypto_ERROR_INVALID_CONTEXT; } for (size_t i = 0; i < key_array_length; ++i) { const OEMCrypto_EntitledContentKeyObject* key_data = &key_array[i]; std::vector entitlement_key_id; entitlement_key_id.assign(message + key_data->entitlement_key_id.offset, message + key_data->entitlement_key_id.offset + key_data->entitlement_key_id.length); EntitlementKey* entitlement_key = session_keys_->GetEntitlementKey(entitlement_key_id); if (entitlement_key == nullptr) { return OEMCrypto_KEY_NOT_ENTITLED; } std::vector content_key; std::vector iv; std::vector encrypted_content_key; std::vector content_key_id; iv.assign(message + key_data->content_key_data_iv.offset, message + key_data->content_key_data_iv.offset + 16); encrypted_content_key.assign(message + key_data->content_key_data.offset, message + key_data->content_key_data.offset + key_data->content_key_data.length); content_key_id.assign(message + key_data->content_key_id.offset, message + key_data->content_key_id.offset + key_data->content_key_id.length); if (!DecryptMessage(entitlement_key->entitlement_key(), iv, encrypted_content_key, &content_key, 256 /* key size */)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (!session_keys_->SetContentKey(entitlement_key_id, content_key_id, content_key)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::InstallKey( const KeyId& key_id, const std::vector& key_data, const std::vector& key_data_iv, const std::vector& key_control, const std::vector& key_control_iv) { // Decrypt encrypted key_data using derived encryption key and offered iv std::vector content_key; std::vector key_control_str; if (!DecryptMessage(encryption_key_, key_data_iv, key_data, &content_key, 128 /* key size */)) { LOGE("[Installkey(): Could not decrypt key data]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // Key control must be supplied by license server if (key_control.empty()) { LOGE("[Installkey(): WARNING: No Key Control]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (key_control_iv.empty()) { LOGE("[Installkey(): ERROR: No Key Control IV]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!DecryptMessage(content_key, key_control_iv, key_control, &key_control_str, 128 /* key size */)) { LOGE("[Installkey(): ERROR: Could not decrypt content key]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } KeyControlBlock key_control_block(key_control_str); if (!key_control_block.valid()) { LOGE("Error parsing key control"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if ((key_control_block.control_bits() & wvoec::kControlRequireAntiRollbackHardware) && !ce_->config_is_anti_rollback_hw_present()) { LOGE("Anti-rollback hardware is required but hardware not present"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const uint8_t minimum_patch_level = (key_control_block.control_bits() & wvoec::kControlSecurityPatchLevelMask) >> wvoec::kControlSecurityPatchLevelShift; if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { LOGE("[InstallKey(): security_patch_level = %u, minimum_patch_level = %u]", OEMCrypto_Security_Patch_Level(), minimum_patch_level); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckNonceOrEntry(key_control_block); if (result != OEMCrypto_SUCCESS) { LOGE("LoadKeys: Failed Nonce/PST check"); return result; } if (key_control_block.control_bits() & wvoec::kControlSRMVersionRequired) { if (srm_requirements_status_ == NoSRMVersion) { LOGE("[LoadKeys: control bit says SRM version required]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (srm_requirements_status_ == InvalidSRMVersion) { // If the SRM version is too small, treat this key as local display only. key_control_block.RequireLocalDisplay(); } } Key key(content_key, key_control_block); if (session_keys_ == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } session_keys_->Insert(key_id, key); return OEMCrypto_SUCCESS; } bool SessionContext::InstallRSAEncryptedKey( const uint8_t* encrypted_message_key, size_t encrypted_message_key_length) { encryption_key_.resize(RSA_size(rsa_key())); const int decrypted_size = RSA_private_decrypt( encrypted_message_key_length, encrypted_message_key, &encryption_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); if (-1 == decrypted_size) { LOGE("[RSADeriveKeys(): error decrypting session key.]"); dump_boringssl_error(); return false; } encryption_key_.resize(decrypted_size); if (decrypted_size != static_cast(wvoec::KEY_SIZE)) { LOGE("[RSADeriveKeys(): error. Session key is wrong size: %d.]", decrypted_size); dump_boringssl_error(); encryption_key_.clear(); return false; } return true; } OEMCryptoResult SessionContext::LoadRenewal(const uint8_t* message, size_t message_length, size_t core_message_length, const uint8_t* signature, size_t signature_length) { if (session_keys_ == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!ValidateMessage(message, message_length, signature, signature_length)) { LOGE("Signature was invalid"); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } // The reference implementation does not use a hardware timer. uint64_t* timer_value = nullptr; const OEMCryptoResult result = ODK_ParseRenewal( message, message_length, core_message_length, &nonce_values_, ce_->SystemTime(), &timer_limits_, &clock_values_, timer_value); if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) return OEMCrypto_SUCCESS; if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; // All other errors are returned to the caller. return result; } OEMCryptoResult SessionContext::RefreshKey( const KeyId& key_id, const std::vector& key_control, const std::vector& key_control_iv) { if (session_keys_ == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } if (key_control.empty()) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } std::vector decrypted_key_control; if (key_id.empty()) { // Key control is not encrypted if key id is NULL decrypted_key_control = key_control; } else { Key* content_key = session_keys_->Find(key_id); if (nullptr == content_key) { LOGE("Key ID not found."); return OEMCrypto_ERROR_NO_CONTENT_KEY; } const std::vector content_key_value = content_key->value(); // Decrypt encrypted key control block if (key_control_iv.empty()) { decrypted_key_control = key_control; } else { if (!DecryptMessage(content_key_value, key_control_iv, key_control, &decrypted_key_control, 128 /* key size */)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } } KeyControlBlock key_control_block(decrypted_key_control); if (!key_control_block.valid()) { LOGE("Parse key control error."); return OEMCrypto_ERROR_INVALID_CONTEXT; } uint32_t new_key_duration = key_control_block.duration(); uint64_t* timer_value = nullptr; const OEMCryptoResult result = ODK_RefreshV15Values(&timer_limits_, &clock_values_, &nonce_values_, ce_->SystemTime(), new_key_duration, timer_value); if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) return OEMCrypto_SUCCESS; if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; return result; } bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* pkcs8_rsa_key) { if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) { LOGE("[DecryptRSAKey(): bad buffer size]"); return false; } // Decrypt rsa key with keybox. uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE); AES_KEY aes_key; AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key); AES_cbc_encrypt(enc_rsa_key, pkcs8_rsa_key, enc_rsa_key_length, &aes_key, iv_buffer, AES_DECRYPT); return true; } bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key) { if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) { LOGE("[EncryptRSAKey(): bad buffer size]"); return false; } // Encrypt rsa key with keybox. uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE); AES_KEY aes_key; AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key); AES_cbc_encrypt(pkcs8_rsa_key, enc_rsa_key, enc_rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT); return true; } bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length) { rsa_key_.reset(); if (rsa_key_length < 8) { LOGE("[LoadRSAKey(): Very Short Buffer]"); return false; } if ((memcmp(pkcs8_rsa_key, "SIGN", 4) == 0)) { uint32_t schemes_n; memcpy((uint8_t*)&schemes_n, pkcs8_rsa_key + 4, sizeof(uint32_t)); allowed_schemes_ = htonl(schemes_n); pkcs8_rsa_key += 8; rsa_key_length -= 8; } else { allowed_schemes_ = kSign_RSASSA_PSS; } return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); } OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, uint32_t use_type, OEMCryptoBufferType buffer_type) { const KeyControlBlock& control = current_content_key()->control(); if (use_type && (!(control.control_bits() & use_type))) { LOGE("[%s(): control bit says not allowed", log_string.c_str()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (control.control_bits() & wvoec::kControlDataPathSecure) { if (!ce_->config_closed_platform() && buffer_type == OEMCrypto_BufferType_Clear) { LOGE("[%s(): Secure key with insecure buffer]", log_string.c_str()); return OEMCrypto_ERROR_DECRYPT_FAILED; } } if (!ce_->config_local_display_only()) { // Only look at HDCP restrictions if the display can be non-local. if (control.control_bits() & wvoec::kControlHDCPRequired) { uint8_t required_hdcp = (control.control_bits() & wvoec::kControlHDCPVersionMask) >> wvoec::kControlHDCPVersionShift; if (ce_->srm_forbidden_device_attached()) { required_hdcp = HDCP_NO_DIGITAL_OUTPUT; } // For reference implementation, we pretend we can handle the current // HDCP version. if (required_hdcp > ce_->config_current_hdcp_capability() || ce_->config_current_hdcp_capability() == 0) { return OEMCrypto_ERROR_INSUFFICIENT_HDCP; } } } // Return an error if analog displays should be disabled. if ((control.control_bits() & wvoec::kControlDisableAnalogOutput) && ce_->analog_display_active()) { LOGE("[%s(): control bit says disable analog", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } // Check if CGMS is required. if (control.control_bits() & wvoec::kControlCGMSMask) { if (ce_->analog_display_active() && !ce_->cgms_a_active()) { LOGE("[%s(): control bit says CGMS required", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } } if (!decrypt_started_) { // The reference implementation does not have a hardware timer. uint64_t* timer_expiration = nullptr; const OEMCryptoResult result = ODK_AttemptFirstPlayback( ce_->SystemTime(), &timer_limits_, &clock_values_, timer_expiration); if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; if (usage_entry_ != nullptr) usage_entry_->ForbidReport(); } else { // Continued playback. const OEMCryptoResult result = ODK_UpdateLastPlaybackTime( ce_->SystemTime(), &timer_limits_, &clock_values_); if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; if (usage_entry_ != nullptr) usage_entry_->set_recent_decrypt(true); } decrypt_started_ = true; // First playback for session. return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { // Check there is a content key if (current_content_key() == nullptr) { LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } const std::vector& key = current_content_key()->value(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { LOGE("[Generic_Encrypt(): algorithm bad"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (buffer_length % AES_BLOCK_SIZE != 0) { LOGE("[Generic_Encrypt(): buffers size bad"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const uint8_t* key_u8 = &key[0]; AES_KEY aes_key; if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { LOGE("[Generic_Encrypt(): FAILURE]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, iv, wvoec::KEY_IV_SIZE); AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, AES_ENCRYPT); return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { // Check there is a content key if (current_content_key() == nullptr) { LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } const std::vector& key = current_content_key()->value(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { LOGE("[Generic_Decrypt(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (buffer_length % AES_BLOCK_SIZE != 0) { LOGE("[Generic_Decrypt(): bad buffer size"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const uint8_t* key_u8 = &key[0]; AES_KEY aes_key; if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { LOGE("[Generic_Decrypt(): FAILURE]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, iv, wvoec::KEY_IV_SIZE); AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, AES_DECRYPT); return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, uint8_t* signature, size_t* signature_length) { // Check there is a content key if (current_content_key() == nullptr) { LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } if (*signature_length < SHA256_DIGEST_LENGTH) { *signature_length = SHA256_DIGEST_LENGTH; LOGE("[Generic_Sign(): bad signature length"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const std::vector& key = current_content_key()->value(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { LOGE("[Generic_Sign(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse("Generic_Sign", wvoec::kControlAllowSign, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { LOGE("[Generic_Sign(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } unsigned int md_len = *signature_length; if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, signature, &md_len)) { *signature_length = md_len; return OEMCrypto_SUCCESS; } LOGE("[Generic_Sign(): hmac failed"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length) { // Check there is a content key if (current_content_key() == nullptr) { LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (signature_length < SHA256_DIGEST_LENGTH) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const std::vector& key = current_content_key()->value(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse( "Generic_Verify", wvoec::kControlAllowVerify, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { LOGE("[Generic_Verify(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } unsigned int md_len = signature_length; uint8_t computed_signature[SHA256_DIGEST_LENGTH]; if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, computed_signature, &md_len)) { if (0 == CRYPTO_memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) { return OEMCrypto_SUCCESS; } else { return OEMCrypto_ERROR_SIGNATURE_FAILURE; } } LOGE("[Generic_Verify(): HMAC failed"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, const std::vector& iv) { // Decrypt mac key from enc_mac_key using device_keya std::vector mac_keys; if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys, 128 /* key size */)) { return false; } mac_key_server_ = std::vector( mac_keys.begin(), mac_keys.begin() + wvoec::MAC_KEY_SIZE); mac_key_client_ = std::vector(mac_keys.begin() + wvoec::MAC_KEY_SIZE, mac_keys.end()); return true; } bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { if (session_keys_ == nullptr) { return false; } const Key* content_key = session_keys_->Find(key_id); if (content_key == nullptr) { LOGE("[QueryKeyControlBlock(): No key matches key id]"); return false; } data[0] = 0; // verification optional. data[1] = htonl(content_key->control().duration()); data[2] = 0; // nonce optional. data[3] = htonl(content_key->control().control_bits()); return true; } OEMCryptoResult SessionContext::SelectContentKey( const KeyId& key_id, OEMCryptoCipherMode cipher_mode) { if (session_keys_ == nullptr) { LOGE("Select Key: no session keys"); return OEMCrypto_ERROR_INVALID_CONTEXT; } Key* content_key = session_keys_->Find(key_id); if (content_key == nullptr) { LOGE("No key matches key id"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR); current_content_key_ = content_key; return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::CreateNewUsageEntry( uint32_t* usage_entry_number) { if (usage_entry_) { // Can only load one entry per session. return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; } OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry( this, &usage_entry_, usage_entry_number); if (usage_entry_) { usage_entry_status_ = kUsageEntryNew; } return result; } OEMCryptoResult SessionContext::LoadUsageEntry( uint32_t index, const std::vector& buffer) { if (usage_entry_) { // Can only load one entry per session. return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; } const OEMCryptoResult result = ce_->usage_table().LoadUsageEntry( this, &usage_entry_, index, buffer, &clock_values_); if ((result != OEMCrypto_SUCCESS) && (result != OEMCrypto_WARNING_GENERATION_SKEW)) return result; if (!usage_entry_) return OEMCrypto_ERROR_UNKNOWN_FAILURE; usage_entry_status_ = kUsageEntryLoaded; // Copy the mac keys to the current session. mac_key_server_ = std::vector( usage_entry_->mac_key_server(), usage_entry_->mac_key_server() + wvoec::MAC_KEY_SIZE); mac_key_client_ = std::vector( usage_entry_->mac_key_client(), usage_entry_->mac_key_client() + wvoec::MAC_KEY_SIZE); return result; } OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, size_t* header_buffer_length, uint8_t* entry_buffer, size_t* entry_buffer_length) { if (!usage_entry_) { LOGE("UpdateUsageEntry: Session has no entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; } return ce_->usage_table().UpdateUsageEntry( this, usage_entry_.get(), header_buffer, header_buffer_length, entry_buffer, entry_buffer_length, &clock_values_); } OEMCryptoResult SessionContext::DeactivateUsageEntry( const std::vector& pst) { if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; usage_entry_->ForbidReport(); return ODK_DeactivateUsageEntry(&clock_values_); } OEMCryptoResult SessionContext::ReportUsage(const std::vector& pst, uint8_t* buffer, size_t* buffer_length) { if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; return usage_entry_->ReportUsage(pst, buffer, buffer_length); } OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) { if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; return ce_->usage_table().MoveEntry(usage_entry_.get(), new_index); } // Internal utility function to decrypt the message bool SessionContext::DecryptMessage(const std::vector& key, const std::vector& iv, const std::vector& message, std::vector* decrypted, uint32_t key_size) { if (key.empty() || iv.empty() || message.empty() || !decrypted) { LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return false; } if (message.size() % AES_BLOCK_SIZE != 0) { LOGE("[DecryptMessage(): bad buffer size]"); return false; } decrypted->resize(message.size()); uint8_t iv_buffer[16]; memcpy(iv_buffer, &iv[0], 16); AES_KEY aes_key; AES_set_decrypt_key(&key[0], key_size, &aes_key); AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, iv_buffer, AES_DECRYPT); return true; } OEMCryptoResult SessionContext::DecryptSamples( const OEMCrypto_SampleDescription* samples, size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { // Iterate through all the samples and decrypt each one for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) { const OEMCrypto_SampleDescription& sample = samples[sample_index]; // Iterate through all the subsamples and decrypt each one. A production // implementation may be able to do something more efficient, like // decrypting all the encrypted portions in one pass. const uint8_t* subsample_source = sample.buffers.input_data; OEMCrypto_DestBufferDesc subsample_dest = sample.buffers.output_descriptor; uint8_t subsample_iv[wvoec::KEY_IV_SIZE]; static_assert(sizeof(sample.iv) == wvoec::KEY_IV_SIZE, "The IV in OEMCrypto_SampleDescription is the wrong length."); // Per its type, sizeof(subsample_iv) == wvoec::KEY_IV_SIZE memcpy(subsample_iv, sample.iv, wvoec::KEY_IV_SIZE); for (size_t subsample_index = 0; subsample_index < sample.subsamples_length; ++subsample_index) { const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[subsample_index]; const size_t subsample_length = subsample.num_bytes_clear + subsample.num_bytes_encrypted; OEMCryptoResult result = ce_->SetDestination( subsample_dest, subsample_length, subsample.subsample_flags); if (result != OEMCrypto_SUCCESS) { LOGE("SetDestination status: %d", static_cast(result)); return result; } result = DecryptSubsample(subsample, subsample_source, ce_->destination(), subsample_dest.type, subsample_iv, pattern); if (result != OEMCrypto_SUCCESS) { LOGE("DecryptSubsample status: %d", static_cast(result)); return result; } result = ce_->PushDestination(subsample_dest, subsample.subsample_flags); if (result != OEMCrypto_SUCCESS) { LOGE("PushDestination status: %d", static_cast(result)); return result; } // Advance the source buffer, the dest buffer, and (if necessary) the IV subsample_source += subsample_length; advance_dest_buffer(&subsample_dest, subsample_length); if (subsample.num_bytes_encrypted > 0 && current_content_key()->ctr_mode()) { wvcdm::AdvanceIvCtr(&subsample_iv, subsample.block_offset + subsample.num_bytes_encrypted); } } // Subsample loop } // Sample loop return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::DecryptSubsample( const OEMCrypto_SubSampleDescription& subsample, const uint8_t* cipher_data, uint8_t* clear_data, OEMCryptoBufferType buffer_type, const uint8_t (&iv)[wvoec::KEY_IV_SIZE], const OEMCrypto_CENCEncryptPatternDesc* pattern) { // Handle the clear portion of the subsample. if (subsample.num_bytes_clear > 0) { if (buffer_type != OEMCrypto_BufferType_Direct) { memmove(clear_data, cipher_data, subsample.num_bytes_clear); } // For the reference implementation, we quietly drop the clear direct video. } // Handle the encrypted portion of the subsample. OEMCryptoResult result = OEMCrypto_SUCCESS; if (subsample.num_bytes_encrypted > 0) { const uint8_t* source = cipher_data + subsample.num_bytes_clear; uint8_t* dest = clear_data + subsample.num_bytes_clear; result = ChooseDecrypt(iv, subsample.block_offset, pattern, source, subsample.num_bytes_encrypted, dest, buffer_type); } // Compute hash for FDPT. if (compute_hash_) { if (current_content_key() == nullptr || (current_content_key()->control().control_bits() & wvoec::kControlAllowHashVerification) == 0) { LOGE("[DecryptCENC(): OEMCrypto_ERROR_UNKNOWN_FAILURE]"); hash_error_ = OEMCrypto_ERROR_UNKNOWN_FAILURE; compute_hash_ = false; current_hash_ = 0; current_frame_number_ = 0; } else { if (OEMCrypto_FirstSubsample & subsample.subsample_flags) { current_hash_ = wvcrc32Init(); } current_hash_ = wvcrc32Cont( clear_data, subsample.num_bytes_clear + subsample.num_bytes_encrypted, current_hash_); if (OEMCrypto_LastSubsample & subsample.subsample_flags) { if (current_hash_ != given_hash_) { LOGE("CRC for frame %u is %08x, should be %08x\n", current_frame_number_, current_hash_, given_hash_); // Update bad_frame_number_ only if this is the first bad frame. if (hash_error_ == OEMCrypto_SUCCESS) { bad_frame_number_ = current_frame_number_; hash_error_ = OEMCrypto_ERROR_BAD_HASH; } } compute_hash_ = false; } } } // Return the result of the previous ChooseDecrypt() call after computing the // hash. return result; } OEMCryptoResult SessionContext::ChooseDecrypt( const uint8_t* iv, size_t block_offset, const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, size_t cipher_data_length, uint8_t* clear_data, OEMCryptoBufferType buffer_type) { // Check there is a content key if (current_content_key() == nullptr) { LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); return OEMCrypto_ERROR_DECRYPT_FAILED; } OEMCryptoResult result = CheckKeyUse("DecryptCENC", 0, buffer_type); if (result != OEMCrypto_SUCCESS) return result; const std::vector& content_key = current_content_key()->value(); // Set the AES key. if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %zu", content_key.size()); return OEMCrypto_ERROR_DECRYPT_FAILED; } const uint8_t* key_u8 = &content_key[0]; if (buffer_type == OEMCrypto_BufferType_Direct) { // For reference implementation, we quietly drop the decrypted direct video. return OEMCrypto_SUCCESS; } if (!current_content_key()->ctr_mode()) { if (block_offset > 0 || pattern->encrypt == 0) { return OEMCrypto_ERROR_INVALID_CONTEXT; } return PatternDecryptCBC(key_u8, iv, pattern, cipher_data, cipher_data_length, clear_data); } else { if (pattern->skip != 0 || pattern->encrypt != 0) { return OEMCrypto_ERROR_INVALID_CONTEXT; } return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length, clear_data); } } OEMCryptoResult SessionContext::PatternDecryptCBC( const uint8_t* key, const uint8_t* initial_iv, const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, size_t cipher_data_length, uint8_t* clear_data) { AES_KEY aes_key; AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); uint8_t iv[AES_BLOCK_SIZE]; uint8_t next_iv[AES_BLOCK_SIZE]; memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); const size_t pattern_length = pattern->encrypt + pattern->skip; if (pattern_length <= 0) return OEMCrypto_ERROR_INVALID_CONTEXT; size_t l = 0; size_t pattern_offset = 0; while (l < cipher_data_length) { const size_t size = std::min(cipher_data_length - l, static_cast(AES_BLOCK_SIZE)); const bool skip_block = (pattern_offset >= pattern->encrypt); pattern_offset = (pattern_offset + 1) % pattern_length; if (skip_block || (size < AES_BLOCK_SIZE)) { // If we are decrypting in-place, then this block is already correct and // can be skipped. if (clear_data != cipher_data) { memcpy(&clear_data[l], &cipher_data[l], size); } } else { uint8_t aes_output[AES_BLOCK_SIZE]; // Save the iv for the next block, in case cipher_data is in the same // buffer as clear_data. memcpy(next_iv, &cipher_data[l], AES_BLOCK_SIZE); AES_decrypt(&cipher_data[l], aes_output, &aes_key); for (size_t n = 0; n < AES_BLOCK_SIZE; n++) { clear_data[l + n] = aes_output[n] ^ iv[n]; } memcpy(iv, next_iv, AES_BLOCK_SIZE); } l += size; } return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, const uint8_t* iv, size_t block_offset, const uint8_t* cipher_data, size_t cipher_data_length, uint8_t* clear_data) { if (block_offset >= AES_BLOCK_SIZE) return OEMCrypto_ERROR_INVALID_CONTEXT; // Local copy (will be modified). // Allocated as 64-bit ints to enforce 64-bit alignment for later access as a // 64-bit value. uint64_t aes_iv[2]; assert(sizeof(aes_iv) == AES_BLOCK_SIZE); // The double-cast is needed to comply with strict aliasing rules. uint8_t* aes_iv_u8 = reinterpret_cast(reinterpret_cast(aes_iv)); memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); // The CENC spec specifies we increment only the low 64 bits of the IV // counter, and leave the high 64 bits alone. This is different from the // OpenSSL implementation, which increments the entire 128 bit iv. That is // why we implement the CTR loop ourselves. size_t l = 0; if (block_offset > 0 && l < cipher_data_length) { // Encrypt the IV. uint8_t ecount_buf[AES_BLOCK_SIZE]; AES_KEY aes_key; if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { LOGE("[DecryptCTR(): FAILURE]"); return OEMCrypto_ERROR_DECRYPT_FAILED; } AES_encrypt(aes_iv_u8, ecount_buf, &aes_key); for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; ++n, ++l) { clear_data[l] = cipher_data[l] ^ ecount_buf[n]; } ctr128_inc64(aes_iv_u8); block_offset = 0; } uint64_t remaining = cipher_data_length - l; int out_len = 0; while (remaining) { EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0); if (!EVP_DecryptInit_ex(evp_cipher_ctx, EVP_aes_128_ctr(), nullptr, key_u8, aes_iv_u8)) { LOGE("[DecryptCTR(): EVP_INIT ERROR]"); EVP_CIPHER_CTX_free(evp_cipher_ctx); return OEMCrypto_ERROR_DECRYPT_FAILED; } // Test the MSB of the counter portion of the initialization vector. If the // value is 0xFF the counter is near wrapping. In this case we calculate // the number of bytes we can safely decrypt before the counter wraps. uint64_t decrypt_length = 0; if (aes_iv_u8[8] == 0xFF) { uint64_t bottom_64_bits = wvcdm::ntohll64(aes_iv[1]); uint64_t bytes_before_iv_wrap = (~bottom_64_bits + 1) * AES_BLOCK_SIZE; decrypt_length = bytes_before_iv_wrap < remaining ? bytes_before_iv_wrap : remaining; } else { decrypt_length = remaining; } if (!EVP_DecryptUpdate(evp_cipher_ctx, &clear_data[l], &out_len, &cipher_data[l], decrypt_length)) { LOGE("[DecryptCTR(): EVP_UPDATE_ERROR]"); EVP_CIPHER_CTX_free(evp_cipher_ctx); return OEMCrypto_ERROR_DECRYPT_FAILED; } l += decrypt_length; remaining = cipher_data_length - l; int final; if (!EVP_DecryptFinal_ex(evp_cipher_ctx, &clear_data[cipher_data_length - remaining], &final)) { LOGE("[DecryptCTR(): EVP_FINAL_ERROR]"); EVP_CIPHER_CTX_free(evp_cipher_ctx); return OEMCrypto_ERROR_DECRYPT_FAILED; } EVP_CIPHER_CTX_free(evp_cipher_ctx); // If remaining is not zero, reset the iv before the second pass. if (remaining) { memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); memset(&aes_iv_u8[8], 0, AES_BLOCK_SIZE / 2); } } return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::SetDecryptHash(uint32_t frame_number, const uint8_t* hash, size_t hash_length) { if (hash_length < sizeof(uint32_t)) { LOGE("[SetDecryptHash(): short buffer]"); return OEMCrypto_ERROR_SHORT_BUFFER; } if (hash_length > sizeof(uint32_t)) { LOGE("[SetDecryptHash(): long buffer]"); return OEMCrypto_ERROR_BUFFER_TOO_LARGE; } compute_hash_ = true; current_frame_number_ = frame_number; given_hash_ = *reinterpret_cast(hash); return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::GetHashErrorCode( uint32_t* failed_frame_number) { if (failed_frame_number == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (hash_error_ != OEMCrypto_SUCCESS) *failed_frame_number = bad_frame_number_; return hash_error_; } bool SessionContext::set_nonce(uint32_t nonce) { if (state_nonce_created_) return false; if (nonce == 0) return false; state_nonce_created_ = true; ODK_SetNonceValues(&nonce_values_, nonce); return true; } } // namespace wvoec_ref