diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index a2ed7b10..969b3fa1 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -723,14 +723,26 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( } CryptoWrappedKey private_key; - const CdmResponseType status = crypto_session_->LoadProvisioning( - request_, signed_message, core_message, signature, &private_key.key()); + // TODO(b/316053127): clean this up a bit. + if (cert_type_ == kCertificateX509) { + const std::string dummy_key; + const CdmResponseType status = crypto_session_->LoadProvisioningCast( + dummy_key, request_, signed_message, core_message, signature, + &private_key.key()); - if (status != NO_ERROR) { - LOGE("LoadProvisioning failed: status = %d", static_cast(status)); - return status; + if (status != NO_ERROR) { + LOGE("LoadProvisioning failed: status = %d", static_cast(status)); + return status; + } + } else { + const CdmResponseType status = crypto_session_->LoadProvisioning( + request_, signed_message, core_message, signature, &private_key.key()); + + if (status != NO_ERROR) { + LOGE("LoadProvisioning failed: status = %d", static_cast(status)); + return status; + } } - const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel(); CloseSession(); diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index f34c4272..5772ede1 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -462,9 +462,11 @@ typedef enum OEMCrypto_Clock_Security_Level { } OEMCrypto_Clock_Security_Level; typedef uint8_t RSA_Padding_Scheme; -// RSASSA-PSS with SHA1. +// RSASSA-PSS with SHA1. Scheme used for DRM certificates for signing a license +// request. #define kSign_RSASSA_PSS ((RSA_Padding_Scheme)0x1) -// PKCS1 with block type 1 padding (only). +// PKCS1 with block type 1 padding (only). Keys used with x509 Cast Receiver +// certificates. #define kSign_PKCS1_Block1 ((RSA_Padding_Scheme)0x2) /// @} @@ -3971,22 +3973,10 @@ OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm( * Below, all fields are found in the struct ODK_ParsedLicense parsed_license * returned by ODK_ParsedProvisioning. * - * After decrypting `parsed_response->enc_private_key`, If the first four bytes - * of the buffer are the string "SIGN", then the actual RSA key begins on the - * 9th byte of the buffer. The second four bytes of the buffer is the 32 bit - * field "allowed_schemes" of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature(). The value of allowed_schemes must also be - * wrapped with RSA key. We recommend storing the magic string "SIGN" with - * the key to distinguish keys that have a value for allowed_schemes from - * those that should use the default allowed_schemes. Devices that do not - * support the alternative signing algorithms may refuse to load these keys - * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case - * for these alternative signing algorithms is to support devices that use - * X509 certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * - * If the first four bytes of the buffer `enc_private_key` are not the string - * "SIGN", then this key may not be used with OEMCrypto_GenerateRSASignature(). + * After decrypting `parsed_response->enc_private_key`, OEMCrypto shall verify + * that the first four bytes of the buffer are *not* the string "SIGN". Keys + * loaded with this function are DRM certificate private keys and may not be + * used with OEMCrypto_GenerateRSASignature(). * * Verification and Algorithm: * The following checks should be performed. If any check fails, an error is @@ -4002,34 +3992,21 @@ OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm( * derived encryption key (enc_key). Use enc_private_key_iv as the initial * vector for AES_128-CBC mode, with PKCS#5 padding. The private_key should * be kept in secure memory and protected from the user. - * 6. If the first four bytes of the buffer private_key are the string "SIGN", - * then the actual RSA key begins on the 9th byte of the buffer. The - * second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature(). The value of allowed_schemes must - * also be wrapped with RSA key. We recommend storing the magic string - * "SIGN" with the key to distinguish keys that have a value for - * allowed_schemes from those that should use the default - * allowed_schemes. Devices that do not support the alternative signing - * algorithms may refuse to load these keys and return an error of - * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these - * alternative signing algorithms is to support devices that use X.509 - * certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * 7. If the first four bytes of the buffer private_key are not the string - * "SIGN", this key may not be used with OEMCrypto_GenerateRSASignature(). - * 8. After possibly skipping past the first 8 bytes signifying the allowed + * 6. Verify that the first four bytes of the buffer private_key are not the + * string "SIGN". This key may not be used with + * OEMCrypto_GenerateRSASignature(). + * 7. After possibly skipping past the first 8 bytes signifying the allowed * signing algorithm, the rest of the buffer private_key contains an ECC * private key or an RSA private key in PKCS#8 binary DER encoded * format. The OEMCrypto library shall verify that this private key is * valid. - * 9. Re-encrypt the device private key with an internal key (such as one + * 8. Re-encrypt the device private key with an internal key (such as one * derived by the OEM key or Widevine Keybox key) and the generated IV * using AES-128-CBC with PKCS#5 padding. The data should also be * signed. This algorithm is just a suggestion. The implementer may use any * suitable encrypting and validation algorithm with a key that ties it to * the device. - * 10. Copy the rewrapped key to the buffer specified by wrapped_private_key + * 9. Copy the rewrapped key to the buffer specified by wrapped_private_key * and the size of the wrapped key to wrapped_private_key_length. * * @param[in] session: crypto session identifier. @@ -4087,12 +4064,103 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( * recommend that the OEM use a strong encryption key and signing key algorithm. * * This is the same as OEMCrypto_LoadProvisioning except it is for CAST devices. - * This should return OEMCrypto_ERROR_NOT_IMPLEMENTED for non-CAST devices. + * Devices that do not support the alternative signing algorithms used by Cast + * Receiver certificates may refuse to load these keys and return an error of + * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these alternative + * signing algorithms is to support devices that use X509 certificates for + * authentication when acting as a ChromeCast receiver. This is not needed for + * devices that wish to send data to a ChromeCast. Keys loaded from this + * function may not be used with OEMCrypto_PrepAndSignLicenseRequest(). + * + * First, OEMCrypto should generate three secondary keys, mac_key[server], + * mac_key[client], and encryption_key, for handling signing and content key + * derivation under the license server protocol for CENC. This is the same + * process as used in `OEMCrypto_LoadLicense()`. For devices that support + * Provisioning 2.0, the derivation_key is ignored and the device key from the + * keybox is used to derive the three keys. For devices that support + * Provisioning 4.0, the derivation_key is used to derive the three keys, using + * the same algorithm as in OEMCrypto_LoadLicense(). + * + * Then OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * Refer to the Verification of Messages from a Server section above for more + * details. + * + * After the signature is verified, + * the function ODK_ParseProvisioning is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseProvisioning is described in the document "Widevine Core + * Message Serialization". + * + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returned by ODK_ParsedProvisioning. + * + * After decrypting `parsed_response->enc_private_key`, OEMCryto should verify + * that the first four bytes of the buffer are the string "SIGN". The actual RSA + * key begins on the 9th byte of the buffer. The second four bytes of the buffer + * is the 32 bit field "allowed_schemes" of type RSA_Padding_Scheme, which is + * used in OEMCrypto_GenerateRSASignature(). The only supported scheme for keys + * loaded with this function is kSign_PKCS1_Block1. The value of allowed_schemes + * must also be wrapped with RSA key. We recommend storing the magic string + * "SIGN" with the key to distinguish keys that have a value for allowed_schemes + * from those that should use the default allowed_schemes. + * + * If the first four bytes of the buffer `enc_private_key` are not the string + * "SIGN", then this key may not be loaded. The error + * OEMCrypto_ERROR_INVALID_KEY is returned. + * + * Verification and Algorithm: + * The following checks should be performed. If any check fails, an error is + * returned, and the key is not loaded. + * 1. Check that all the pointer values passed into it are within the + * buffer specified by message and message_length. + * 2. Verify that (in) wrapped_private_key_length is large enough to hold + * the rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. + * 3. Verify the message signature, using the derived signing key + * (mac_key[server]). + * 4. The function ODK_ParseProvisioning is called to parse the message. + * 5. Decrypt enc_private_key in the buffer private_key using the session's + * derived encryption key (enc_key). Use enc_private_key_iv as the initial + * vector for AES_128-CBC mode, with PKCS#5 padding. The private_key should + * be kept in secure memory and protected from the user. + * 6. Verify that the first four bytes of the buffer private_key are the + * string "SIGN". The actual RSA key begins on the 9th byte of the + * buffer. The second four bytes of the buffer is the 32 bit field + * "allowed_schemes", of type RSA_Padding_Scheme, which is used in + * OEMCrypto_GenerateRSASignature(). The value of allowed_schemes must + * also be wrapped with RSA key. We recommend storing the magic string + * "SIGN" with the key to distinguish keys that have a value for + * allowed_schemes from those that should use the default + * allowed_schemes. Devices that do not support the alternative signing + * algorithms may refuse to load these keys and return an error of + * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these + * alternative signing algorithms is to support devices that use X.509 + * certificates for authentication when acting as a ChromeCast receiver. + * This is not needed for devices that wish to send data to a ChromeCast. + * 7. After possibly skipping past the first 8 bytes signifying the allowed + * signing algorithm, the rest of the buffer private_key contains an ECC + * private key or an RSA private key in PKCS#8 binary DER encoded + * format. The OEMCrypto library shall verify that this private key is + * valid. + * 8. Re-encrypt the device private key with an internal key (such as one + * derived by the OEM key or Widevine Keybox key) and the generated IV + * using AES-128-CBC with PKCS#5 padding. The data should also be + * signed. This algorithm is just a suggestion. The implementer may use any + * suitable encrypting and validation algorithm with a key that ties it to + * the device. + * 9. Copy the rewrapped key to the buffer specified by wrapped_private_key + * and the size of the wrapped key to wrapped_private_key_length. * * @param[in] session: crypto session identifier. - * @param[in] derivation_key: session key, encrypted with the public RSA key - * (from the DRM certifcate) using RSA-OAEP. - * @param[in] derivation_key_length: length of derivation_key, in bytes. + * @param[in] derivation_key: pointer to memory containing derivation key. + * @param[in] derivation_key_length: length of the derivation_key, in bytes. * @param[in] provision_request: the initial provisioning request. * @param[in] provision_request_length: length of provision_request, in bytes. * @param[in] message: pointer to memory containing data. diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 4a92297f..36db07a6 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -540,11 +540,24 @@ OEMCryptoResult ProvisioningRoundTrip::LoadResponseNoRetry( Session* session, size_t* wrapped_key_length) { EXPECT_NE(session, nullptr); VerifyEncryptAndSignResponseLengths(); - return OEMCrypto_LoadProvisioning( - session->session_id(), request_.data(), request_.size(), - encrypted_response_.data(), encrypted_response_.size(), - serialized_core_message_.size(), response_signature_.data(), - response_signature_.size(), wrapped_rsa_key_.data(), wrapped_key_length); + if (allowed_schemes_ == kSign_RSASSA_PSS) { + return OEMCrypto_LoadProvisioning( + session->session_id(), request_.data(), request_.size(), + encrypted_response_.data(), encrypted_response_.size(), + serialized_core_message_.size(), response_signature_.data(), + response_signature_.size(), wrapped_rsa_key_.data(), + wrapped_key_length); + } else { + // TODO(b/316053127): Clean this up a lot. + const uint8_t* derivation_key = nullptr; + const size_t derivation_key_length = 0; + return OEMCrypto_LoadProvisioningCast( + session->session_id(), derivation_key, derivation_key_length, + request_.data(), request_.size(), encrypted_response_.data(), + encrypted_response_.size(), serialized_core_message_.size(), + response_signature_.data(), response_signature_.size(), + wrapped_rsa_key_.data(), wrapped_key_length); + } } void ProvisioningRoundTrip::VerifyLoadFailed() {