diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 19a55f0b..a866a43f 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -382,7 +382,7 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message, OEMCryptoResult sts = OEMCrypto_LoadKeys( oec_session_id_, msg, message.size(), reinterpret_cast(signature.data()), signature.size(), - enc_mac_key_iv, enc_mac_key, num_keys, &load_key_array[0]); + enc_mac_key_iv, enc_mac_key, num_keys, &load_key_array[0], NULL, 0); if (OEMCrypto_SUCCESS == sts) { return KEY_ADDED; @@ -514,7 +514,7 @@ bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa, if (use_rsa) { sts = OEMCrypto_GenerateRSASignature( oec_session_id_, reinterpret_cast(message.data()), - message.size(), NULL, &length); + message.size(), NULL, &length, kSign_RSASSA_PSS); if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts); return false; @@ -537,7 +537,7 @@ bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa, oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), - &length); + &length, kSign_RSASSA_PSS); } else { sts = OEMCrypto_GenerateSignature( oec_session_id_, reinterpret_cast(message.data()), diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 35a2df13..d2e805e2 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -48,7 +48,8 @@ typedef OEMCryptoResult (*L1_LoadKeys_t)( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, - const OEMCrypto_KeyObject* key_array); + const OEMCrypto_KeyObject* key_array, + const uint8_t* pst, size_t pst_length); typedef OEMCryptoResult (*L1_RefreshKeys_t)( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, size_t num_keys, @@ -88,7 +89,8 @@ typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, - size_t* signature_length); + size_t* signature_length, + RSA_Padding_Scheme algorithm); typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)( OEMCrypto_SESSION session, const uint8_t* enc_session_key, size_t enc_session_key_length, const uint8_t* mac_key_context, @@ -239,9 +241,10 @@ class Adapter { return false; } uint32_t level1_version = level1_.APIVersion(); - if (level1_version != oec_latest_version) { + uint32_t minimum_version = 8; // TODO(fredgc): allow version 8 and 9? + if (level1_version < minimum_version) { LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", - level1_version, oec_latest_version); + level1_version, minimum_version); return false; } if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) { @@ -439,13 +442,14 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, - const OEMCrypto_KeyObject* key_array) { + const OEMCrypto_KeyObject* key_array, + const uint8_t* pst, size_t pst_length) { if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; LevelSession pair = kAdapter->get(session); if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; return pair.fcn->LoadKeys(pair.session, message, message_length, signature, signature_length, enc_mac_key_iv, enc_mac_key, - num_keys, key_array); + num_keys, key_array, pst, pst_length); } extern "C" OEMCryptoResult OEMCrypto_RefreshKeys( @@ -579,12 +583,12 @@ extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - uint8_t* signature, size_t* signature_length) { + uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme algorithm) { if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; LevelSession pair = kAdapter->get(session); if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; return pair.fcn->GenerateRSASignature(pair.session, message, message_length, - signature, signature_length); + signature, signature_length, algorithm); } extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( diff --git a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf index c32cfda1..47512aee 100644 Binary files a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf and b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf differ diff --git a/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf b/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf index 0b49eb1f..61f2b624 100644 Binary files a/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf and b/libwvdrmengine/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement.pdf differ diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 720f7c20..a979daa6 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -9,6 +9,7 @@ #ifndef OEMCRYPTO_CENC_H_ #define OEMCRYPTO_CENC_H_ +#include #include #include @@ -16,10 +17,6 @@ extern "C" { #endif -#define OEMCRYPTO_VERSION "8.0" -static const char oec_version[] = OEMCRYPTO_VERSION; -static const uint32_t oec_latest_version = 8; - typedef uint32_t OEMCrypto_SESSION; typedef enum OEMCryptoResult { @@ -195,6 +192,45 @@ typedef enum OEMCrypto_Algorithm { #define OEMCrypto_FirstSubsample 1 #define OEMCrypto_LastSubsample 2 +/* OEMCrypto_Usage_Entry_Status. + * Valid values for status in the usage table. + */ +typedef enum OEMCrypto_Usage_Entry_Status { + kUnused = 0, + kActive = 1, + kInactive = 2 +} OEMCrypto_Usage_Entry_Status; + +/* + * OEMCrypto_PST_Report is used to report an entry from the Usage Table. + */ +typedef struct { + uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. + int64_t seconds_since_license_received; // now - time_of_license_received + int64_t seconds_since_first_decrypt; // now - time_of_first_decrypt + int64_t seconds_since_last_decrypt; // now - time_of_last_decrypt + uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) + uint8_t clock_security_level; + uint8_t pst_length; + uint8_t pst[]; +} OEMCrypto_PST_Report; + +/* OEMCrypto_Clock_Security_Level. + * Valid values for clock_security_level in OEMCrypto_PST_Report. + */ +typedef enum OEMCrypto_Clock_Security_Level { + kInsecureClock = 0, + kSecureTimer = 1, + kSecureClock = 2, + kHardwareSecureClock = 3 +} OEMCrypto_Clock_Security_Level; + +typedef enum RSA_Padding_Scheme { + kSign_RSASSA_PSS = 0x1, // RSASSA-PSS with SHA1. + kSign_PKCS1_Block1 = 0x2, // PKCS1 with block type 1 padding (only). +} RSA_Padding_Scheme; + + /* Obfuscation Renames. */ #define OEMCrypto_Initialize _oecc01 #define OEMCrypto_Terminate _oecc02 @@ -223,26 +259,34 @@ typedef enum OEMCrypto_Algorithm { #define OEMCrypto_Generic_Decrypt _oecc25 #define OEMCrypto_Generic_Sign _oecc26 #define OEMCrypto_Generic_Verify _oecc27 +#define OEMCrypto_GetHDCPCapability _oecc28 +#define OEMCrypto_SupportsUsageTable _oecc29 +#define OEMCrypto_UpdateUsageTable _oecc30 +#define OEMCrypto_DeactivateUsageEntry _oecc31 +#define OEMCrypto_ReportUsage _oecc32 +#define OEMCrypto_DeleteUsageEntry _oecc33 +#define OEMCrypto_DeleteUsageTable _oecc34 /* * OEMCrypto_Initialize * - * Description: - * Initialize the crypto firmware/hardware. - * - * Parameters: - * N/A - * - * Threading: - * No other function calls will be made while this function is running. This - * function will not be called again before OEMCrypto_Terminate. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - * - * Version: - * This method is supported by all API versions. + + Initializes the crypto hardware. + + Parameters: + None + + Returns: + OEMCrypto_SUCCESS success + OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + + Threading: + No other function calls will be made while this function is running. This + function will not be called again before OEMCrypto_Terminate(). + + Version: + This method is supported by all API versions. + */ OEMCryptoResult OEMCrypto_Initialize(void); @@ -250,7 +294,7 @@ OEMCryptoResult OEMCrypto_Initialize(void); * OEMCrypto_Terminate * * Description: - * The API closes the crypto operation and releases all resources used. + * Closes the crypto operation and releases all related resources. * * Parameters: * N/A @@ -265,7 +309,7 @@ OEMCryptoResult OEMCrypto_Initialize(void); * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware * * Version: - * This method is all API versions. + * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Terminate(void); @@ -273,10 +317,13 @@ OEMCryptoResult OEMCrypto_Terminate(void); * OEMCrypto_OpenSession * * Description: - * The API provides for session based crypto initialization for AES CTR mode. + * Open a new crypto security engine context. The security engine hardware and + * firmware shall acquire resources that are needed to support the session, and + * return a session handle that identifies that session in future calls. * * Parameters: - * session (out) - pointer to crypto session identifier. + * session (out) - an opaque handle that the crypto firmware uses to identify the + * session. * * Threading: * No other Open/Close session calls will be made while this function is @@ -297,10 +344,10 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); * OEMCrypto_CloseSession * * Description: - * The API provides for session based crypto termination for AES CTR mode. + * Closes the crypto security engine session and frees any associated resources. * * Parameters: - * session (in) - crypto session identifier. + * session (in) - handle for the session to be closed. * * Threading: * No other Open/Close session calls will be made while this function is @@ -334,13 +381,15 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * These three keys will be stored until the next call to LoadKeys. * * Parameters: - * session (in) - crypto session identifier. + * session (in) - handle for the session to be used. * mac_key_context (in) - pointer to memory containing context data for * computing the HMAC generation key. - * mac_key_context_length (in) - length of the HMAC key context data. + * mac_key_context_length (in) - length of the HMAC key context data, in bytes. * enc_key_context (in) - pointer to memory containing context data for * computing the encryption key. - * enc_key_context_length (in) - length of the encryption key context data. + * enc_key_context_length (in) - length of the encryption key context data, in + * bytes. + * * * Results: * mac_key_server: the 256 bit mac key is generated and stored in secure memory. @@ -377,15 +426,18 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * control block. The nonce is stored in secure memory and will be used * for the next call to LoadKeys. * - * Refer to documents "Widevine Modular DRM Security Integration Guide for - * CENC". + * Because the nonce will be used to prevent replay attacks, it is desirable + * that a rogue application cannot rapidly call this function until a + * repeated nonce is created randomly. With this in mind, we require that + * creation of more than 20 nonces will take at least one full second. * * Parameters: * session (in) - crypto session identifier. * nonce (out) - pointer to memory to received the computed nonce. * * Results: - * nonce: the nonce is also stored in secure memory. + * nonce: the nonce is also stored in secure memory. At least 4 nonces should be + * stored for each session. * * Threading: * This function may be called simultaneously with functions on other sessions, @@ -420,10 +472,11 @@ OEMCryptoResult OEMCrypto_GenerateNonce( * Parameters: * session (in) - crypto session identifier. * message (in) - pointer to memory containing message to be signed. - * message_length (in) - length of the message. - * signature (out) - pointer to memory to received the computed signature. - * signature_length (in/out) - (in) length of the signature buffer. - * (out) actual length of the signature + * message_length (in) - length of the message, in bytes. + * signature (out) - pointer to memory to received the computed signature. May be + * null on the first call in order to find required buffer size. + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * (out) actual length of the signature, in bytes. * * Threading: * This function may be called simultaneously with functions on other sessions, @@ -431,10 +484,9 @@ OEMCryptoResult OEMCrypto_GenerateNonce( * * Returns: * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to hold + * buffer. * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_UNKNOWN_FAILURE * @@ -463,43 +515,123 @@ OEMCryptoResult OEMCrypto_GenerateSignature( * * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and * the IV given in the KeyObject. Each key control block will be decrypted - * using the corresponding content key (AES-128-CBC) and the IV given in the - * KeyObject. + * using the first 128 bits of the corresponding content key (AES-128-CBC) and + * the IV given in the KeyObject. * - * If any key's control block does not have valid verification fields, return - * OEMCrypto_ERROR_INVALID_CONTEXT and do not install any keys. + * If it is not null, enc_mac_keys will be used to create new mac_keys. After + * all keys have been decrypted and validated, the new mac_keys are decrypted + * with the current encrypt_key and the offered IV. The new mac_keys replaces + * the current mac_keys for future calls to OEMCrypto_RefreshKeys(). The first + * 256 bits of the mac_keys become the mac_key[server] and the following 256 + * bits of the mac_keys become the mac_key[client]. If enc_mac_keys is null, + * then there will not be a call to OEMCrypto_RefreshKeys for this session and + * the current mac_keys should remain unchanged. * - * If any key's control block requires a nonce, and the nonce in the control - * block is different from the current nonce, return - * OEMCrypto_ERROR_INVALID_NONCE. In that case, do not install any keys. - * - * The new mac_keys are decrypted with the current encrypt_key and the offered - * IV. They replace the current mac_keys. - * - * The mac_keys and encrypt_key were generated and stored by the previous call + * The mac_key and encrypt_key were generated and stored by the previous call * to OEMCrypto_GenerateDerivedKeys(). The nonce was generated and stored by * the previous call to OEMCrypto_GenerateNonce(). * * This session’s elapsed time clock is started at 0. The clock will be used - * in OEMCrypto_DecryptCTR. + * in OEMCrypto_DecryptCTR(). * - * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish - * the mac_keys and encrypt_key. + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key and encrypt_key. * * Refer to document "Widevine Modular DRM Security Integration Guide for * CENC" for details. * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * + * 1. The signature of the message shall be computed, and the API shall verify + * the computed signature matches the signature passed in. If not, return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature verification shall use a + * constant-time algorithm (a signature mismatch will always take the same time + * as a successful comparison). + * + * 2. The enc_mac_keys pointer must be either null, or point inside the + * message. If the pointer enc_mac_keys is not null, [e]the API shall verify + * that the two pointers enc_mac_keys_iv and enc_mac_keys point to locations in + * the message. I.e. (message <= p && p < message+message_length)for p in + * each of enc_mac_keys_iv, enc_mac_keys. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 3. The API shall verify that each pointer in each KeyObject points to a + * location in the message. I.e. (message <= p && p < message+message_length) + * for p in each of key_id, key_data_iv, key_data, key_control_iv, + * key_control. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 4. Each key’s control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 5. If any key control block has the Nonce_Enabled bit set, that key’s Nonce + * field shall match the nonce generated by the current nonce. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that nonce from + * the cache. Note that all the key control blocks in a particular call shall + * have the same nonce value. + * + * 6. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * + * Usage Table and Provider Session Token (pst): + * + * If a key control block has a nonzero value for Replay_Control, then all keys + * in this license will have the same value. In this case, the following + * additional checks are performed. + * + * The pointer pst must not be null, and must point to a location in the + * message. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the table does + * not already have an entry for the value of pst passed in as a parameter --- + * if an entry already exists, an error OEMCrypto_ERROR_INVALID_CONTEXT is + * returned and no keys are loaded. OEMCrypto will then create a new entry in + * the table, and mark this session as using this new entry. This prevents the + * license from being loaded more than once, and will be used for online + * streaming. + * + * - If Replay_Control is 2 = “Require existing Session Usage table entry or + * Nonce”, then OEMCrypto will check the Session Usage table for an existing + * entry with the same pst. + * + * --- If the pst is not in the table yet, a new entry will be created in the + * table and this session shall use the new entry. In that case, the nonce + * will be verified for each key. + * + * --- If an existing usage table entry is found, then this session will use + * that entry. In that case, the nonce will not be verified for each key. + * Also, the entry’s mac keys will be verified against the current + * session’s mac keys. This allows an offline license to be reloaded but + * maintain continuity of the playback times from one session to the next. + * + * - If the nonce is not valid and an existing entry is not found, the return + * error is OEMCrypto_ERROR_INVALID_NONCE. + * + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used in the Usage Table. If LoadKeys does not update the mac keys, the + * existing session mac keys are stored in the usage table. + * + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. + * + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * * Parameters: * session (in) - crypto session identifier. * message (in) - pointer to memory containing message to be verified. - * message_length (in) - length of the message. + * message_length (in) - length of the message, in bytes. * signature (in) - pointer to memory containing the signature. - * signature_length (in) - length of the signature. + * signature_length (in) - length of the signature, in bytes. * enc_mac_keys_iv (in) - IV for decrypting new mac_key. Size is 128 bits. * enc_mac_keys (in) - encrypted mac_keys for generating new mac_keys. Size is * 512 bits. * num_keys (in) - number of keys present. * key_array (in) - set of keys to be installed. + * pst (in) - the Provider Session Token. + * pst_length (in) - the length of pst. * * Threading: * This function may be called simultaneously with functions on other sessions, @@ -516,7 +648,7 @@ OEMCryptoResult OEMCrypto_GenerateSignature( * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 8. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, const uint8_t* message, @@ -526,7 +658,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, size_t num_keys, - const OEMCrypto_KeyObject* key_array); + const OEMCrypto_KeyObject* key_array, + const uint8_t* pst, + size_t pst_length); /* * OEMCrypto_RefreshKeys @@ -536,26 +670,66 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, * current session. * * The relevant fields have been extracted from the Renewal Response protocol - * message, but the entire message and associated signature are provided so - * the message can be verified (using HMAC-SHA256 with the current - * mac_key_server). If the signature verification fails, ignore all other - * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add - * the keys to the session context. + * message, but the entire message and associated signature are provided so the + * message can be verified (using HMAC-SHA256 with the current + * mac_key[server]). If any verification step fails, an error is returned. + * Otherwise, the key table in trusted memory is updated using the key_control + * block. When updating an entry in the table, only the duration, nonce, and + * nonce_enabled fields are used. All other key control bits are not modified. * - * NOTE: OEMCrypto_GenerateDerivedKeys() or OEMCrypto_LoadKeys() must be - * called first to establish the mac_keys. + * NOTE: OEMCrypto_GenerateDerivedKeys() or OEMCrypto_LoadKeys() must be called + * first to establish the mac_key[server]. * - * Refer to document "Widevine Modular DRM Security Integration Guide for - * CENC" for details. + * This session’s elapsed time clock is reset to 0 when this function is called. + * The elapsed time clock is used in OEMCrypto_DecryptCTR(). + * + * This function does not add keys to the key table. It is only used to update a + * key control block license duration. Refer to the License Signing and + * Verification section above for more details. This function is used to update + * the duration of a key, only. It is not used to update key control bits. + * + * If the KeyRefreshObject’s key_control_iv is null, then the key_control is not + * encrypted. If the key_control_iv is specified, then key_control is encrypted + * with the first 128 bits of the corresponding content key. + * + * If the KeyRefreshObject’s key_id is null, then this refresh object should be + * used to update the duration of all keys for the current session. In this + * case, key_control_iv will also be null and the control block will not be + * encrypted. + * + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * + * 1. The signature of the message shall be computed, and the API shall verify + * the computed signature matches the signature passed in. If not, return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature verification shall use a + * constant-time algorithm (a signature mismatch will always take the same time + * as a successful comparison). + * + * 2. The API shall verify that each pointer in each KeyObject points to a + * location in the message, or is null. I.e. (message <= p && p < + * message+message_length) for p in each of key_id,key_control_iv, + * key_control. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 3. Each key’s control block shall have a valid verification field. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 4. If the key control block has the Nonce_Enabled bit set, the Nonce field + * shall match one of the nonces in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that nonce from the + * cache. Note that all the key control blocks in a particular call shall have + * the same nonce value. * * Parameters: * session (in) - crypto session identifier. * message (in) - pointer to memory containing message to be verified. - * message_length (in) - length of the message. + * message_length (in) - length of the message, in bytes. * signature (in) - pointer to memory containing the signature. - * signature_length (in) - length of the signature. + * signature_length (in) - length of the signature, in bytes. * num_keys (in) - number of keys present. - * key_array (in) - set of keys to be installed. + * key_array (in) - set of key updates. * * Threading: * This function may be called simultaneously with functions on other sessions, @@ -592,24 +766,22 @@ OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, * The specified key must have been previously "installed" via * OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys(). * - * This session’s elapsed time clock is started at 0. The clock will be used - * in OEMCrypto_DecryptCTR. - * * A key control block is associated with the key and the session, and is used * to configure the session context. The Key Control data is documented in * "Key Control Block Definition". * * Step 1: Lookup the content key data via the offered key_id. The key data - * includes the key value, the content key IV, the key control - * block, and the key control block IV. + * includes the key value, and the key control block. * * Step 2: Latch the content key into the hardware key ladder. Set * permission flags and timers based on the key's control block. * - * Step 3: use the latched content key to decrypt (AES-128-CTR) - * to decrypt buffers passed in via OEMCrypto_DecryptCTR(). Continue - * to use this key until OEMCrypto_SelectKey() is called again, or - * until OEMCrypto_CloseSession() is called. + * Step 3: use the latched content key to decrypt (AES-128-CTR) buffers passed in + * via OEMCrypto_DecryptCTR(). If the key is 256 bits it will be used for + * OEMCrypto_Generic_Sign or OEMCrypto_Generic_Verify as specified in the key + * control block. Continue to use this key until OEMCrypto_SelectKey() is called + * again, or until OEMCrypto_CloseSession() is called. + * * * Parameters: * session (in) - crypto session identifier @@ -642,54 +814,24 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, * OEMCrypto_DecryptCTR * * Description: + * Decrypts (AES-128-CTR) or copies the payload in the buffer referenced by the + * data_addr parameter into the buffer referenced by the out_buffer parameter, + * using the session context indicated by the session parameter. If is_encrypted + * is true, the content key associated with the session is latched in the active + * hardware key ladder and is used for the decryption operation. If is_encrypted + * is false, the data is simply copied. * - * The API decrypts (AES-CTR) or copies the payload in the buffer referenced by - * the data_addr parameter to the buffer determined by out_buffer, using the key - * previously set by a call to OEMCrypto_SelectKey for the specified session. + * After decryption, the data_length bytes are copied to the location described + * by out_buffer. This could be one of * - * Parameters: - * session (in) - crypto session identifier. - * data_addr (in) - An unaligned pointer to this segment of the stream. - * data_length (in) - The length of this segment of the stream. - * is_encrypted (in) - True if the buffer described by data_addr, - * data_length is encrypted. If is_encrypted is false, only the - * data_addr and data_length parameters are used. The iv and offset - * arguments are ignored. - * iv (in) - The initial value block to be used for content decryption. - * This is discussed further below. - * block_offset (in) - If non-zero, the decryption block boundary is - * different from the start of the data. offset should be subtracted from - * data_addr to compute the starting address of the first decrypted - * block. The bytes between the decryption block start address and - * data_addr are discarded after decryption. This is only used to adjust - * the start of decryption block. It does not adjust the beginning of the - * source or destination data. 0 <= block_offset < 16. - * out_buffer (in) - A caller-owned descriptor that specifies the - * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc - * for details. - * subsample_flags (in) - bitwise flags indicating if this is the first, - * middle, or last subsample in a chunk of data. 1 = first subsample, - * 2 = last subsample, 3 = both first and last subsample, 0 = neither - * first nor last subsample. + * 1. The structure out_buffer contains a pointer to a clear text buffer. The + * OEMCrypto library shall verify that key control allows data to be returned in + * clear text. If it is not authorized, this method should return an error. * - * AES CTR is a stream cipher. The stream may be composed of arbitrary- - * length clear and encrypted segments. The encrypted portions of a sample - * are collectively treated as a continuous sequence of decryption - * block-sized blocks even though the sequence is interrupted by clear blocks. - * This means a given encrypted segment may not start or end on a decryption - * block boundary. + * 2. The structure out_buffer contains a handle to a secure buffer. * - * If data_addr is not aligned with a decryption block boundary (offset != 0), - * the additional offset bytes before data_addr (pre-padding) are included in - * the decrypt operation, and they are dropped after decryption. If - * data_length + offset is not a multiple of the decryption block size, the - * extra bytes in the final decryption block (post-padding) are also dropped - * after decryption. The caller is responsible for guaranteeing that all - * memory addresses from (data-addr - pre-padding) to (data-addr + - * data-length + post-padding) are valid memory addresses. - * - * After decrypting the entire buffer including any pre-padding and - * post-padding, send data_length bytes starting at data_addr to the decoder. + * 3. The structure out_buffer indicates that the data should be sent directly to + * the decoder and rendered. * * NOTES: * IV points to the counter value to be used for the initial @@ -709,6 +851,70 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, * OEMCrypto_LastSubsample has been set. If an implementation decrypts data * immediately, it may ignore subsample_flags. * + * If the destination buffer is secure, an offset may be specified. DecryptCTR + * begins storing data out_buffer->secure.offset bytes after the beginning of the + * secure buffer. + * + * If the session has an entry in the Usage Table, then OEMCrypto will update the + * time_of_last_decrypt. If the status of the entry is “unused”, then change the + * status to “active” and set the time_of_first_decrypt.[f] + * + * + * Verification: + * The following checks should be performed if is_encrypted is true. If any + * check fails, an error is returned, and no decryption is performed. + * + * 1. If the current key’s control block has a nonzero Duration field, then the + * API shall verify that the duration is greater than the session’s elapsed time + * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED[g]. + * + * 2. If the current key’s control block has the Data_Path_Type bit set, then the + * API shall verify that the output buffer is secure or direct. If not, return + * OEMCrypto_ERROR_DECRYPT_FAILED. + * + * 3. If the current key’s control block has the HDCP bit set, then the API shall + * verify that the buffer will be output using HDCP only. If not, return + * OEMCrypto_ERROR_DECRYPT_FAILED. + * + * 4. If the current key’s control block has a nonzero value for HDCP_Version, + * then the current version of HDCP for the device and the display combined will + * be compared against the version specified in the control block. If the + * current version is not at least as high as that in the control block, then + * return OEMCrypto_ERROR_DECRYPT_FAILED. + * + * 1. If the current session has an entry in the Usage Table, and the status of + * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION.[h] + * + * If the flag is_encrypted is false, then no verification is performed. This + * call shall copy clear data even when there are no keys loaded, or there is no + * selected key. + * + * Parameters: + * session (in) - crypto session identifier. + * data_addr (in) - An unaligned pointer to this segment of the stream. + * data_length (in) - The length of this segment of the stream, in bytes. + * is_encrypted (in) - True if the buffer described by data_addr, data_length is + * encrypted. If is_encrypted is false, only the data_addr + * and data_length parameters are used. The iv and offset + * arguments are ignored. + * iv (in) - The initial value block to be used for content decryption. + * block_offset (in) - If non-zero, the decryption block boundary is different + * from the start of the data. block_offset should be + * subtracted from data_addr to compute the starting address + * of the first decrypted block. The bytes between the + * decryption block start address and data_addr are discarded + * after decryption. It does not adjust the beginning of the + * source or destination data. This parameter satisfies + * 0 <= block_offset < 16. + * out_buffer (in) - A caller-owned descriptor that specifies the handling of the + * decrypted byte stream. See OEMCrypto_DestbufferDesc for details. + * subsample_flags (in) - bitwise flags indicating if this is the first, middle, + * or last subsample in a chunk of data. + * 1 = first subsample, + * 2 = last subsample, + * 3 = both first and last subsample, + * 0 = neither first nor last subsample. + * * Threading: * This function may be called simultaneously with functions on other sessions, * but not with other functions on this session. @@ -724,7 +930,7 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 5. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, const uint8_t *data_addr, @@ -739,26 +945,41 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, * OEMCrypto_WrapKeybox * * Description: - * Wrap the Keybox with a key derived from the device key. If transportKey - * is not NULL, the input keybox is encrypted with transportKey. If so, - * decrypt the input keybox before wrapping it, using transportKey in AES-CBC - * mode with an IV of all zeroes. This function is only needed if the - * provisioning method involves saving the keybox to the file system. + * During manufacturing, the keybox should be encrypted with the OEM root key and + * stored on the file system in a region that will not be erased during factory + * reset. As described in section 5.5.4, the keybox may be directly encrypted + * and stored on the device in a single step, or it may use the two-step + * WrapKeybox/InstallKeybox approach. When the Widevine DRM plugin initializes, + * it will look for a wrapped keybox in the file /factory/wv.keys and install it + * into the security processor by calling OEMCrypto_InstallKeybox(). + * + * OEMCrypto_WrapKeybox() is used to generate an OEM-encrypted keybox that may be + * passed to OEMCrypto_InstallKeybox() for provisioning. The keybox may be + * either passed in the clear or previously encrypted with a transport key. If a + * transport key is supplied, the keybox is first decrypted with the transport + * key before being wrapped with the OEM root key. This function is only needed + * if the provisioning method involves saving the keybox to the file system. * * Parameters: - * keybox (in) - Pointer to keybox data. - * keyboxLength - Length of the Keybox data in bytes - * wrappedKeybox (out) - Pointer to wrapped keybox - * wrappedKeyboxLength (out) - Pointer to the length of the wrapped keybox in - * bytes - * transportKey (in) - An optional AES transport key. If provided, the input - * keybox is encrypted with this transport key with AES-CBC - * and a null IV. - * transportKeyLength - number of bytes in the transportKey + * keybox (in) - pointer to Keybox data to encrypt. May be NULL on the first + * call to test size of wrapped keybox. The keybox may either be clear or + * previously encrypted. + * keyboxLength (in) - length the keybox data in bytes + * wrappedKeybox (out) – Pointer to wrapped keybox + * wrappedKeyboxLength (out) – Pointer to the length of the wrapped keybox in bytes + * transportKey (in) – Optional. AES transport key. If provided, the keybox + * parameter was previously encrypted with this key. The keybox will be + * decrypted with the transport key using AES-CBC and a null IV. + * transportKeyLength (in) – Optional. Number of bytes in the transportKey, if used. + * + * Threading: + * This function is not called simultaneously with any other functions * * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox + * OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to determine the + * size of the wrapped keybox * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_NOT_IMPLEMENTED * @@ -777,27 +998,29 @@ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox, * OEMCrypto_InstallKeybox * * Description: - * Unwrap and store the keybox to persistent memory. - * The device key must be stored securely. - * - * This function is used once to load the keybox onto the device at - * provisioning time. + * Decrypts a wrapped keybox and installs it in the security processor. The + * keybox is unwrapped then encrypted with the OEM root key. This function is + * called from the Widevine DRM plugin at initialization time if there is no + * valid keybox installed. It looks for a wrapped keybox in the file + * /factory/wv.keys and if it is present, will read the file and call + * OEMCrypto_InstallKeybox() with the contents of the file. * * Parameters: - * keybox (in) - Pointer to clear keybox data. Must have been originally - * wrapped with OEMCrypto_WrapKeybox. - * keyboxLength (in) - Length of the keybox data in bytes. + * keybox (in) - pointer to encrypted Keybox data as input + * keyboxLength (in) - length of the keybox data in bytes * * Threading: * This function is not called simultaneously with any other functions. * * Returns: * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox * * Version: - * This method is all API versions. + * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, size_t keyBoxLength); @@ -805,52 +1028,52 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, /* * OEMCrypto_IsKeyboxValid * - * Description: - * Validate the Widevine Keybox stored on the device. - * - * The API performs two verification steps on the Keybox. It first verifies - * the MAGIC field contains a valid signature (must be 'kbox'). The API then - * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the - * checksum to the CRC stored in the Keybox. The CRC is computed over the - * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]). - * - * Parameters: - * none - * - * Threading: - * This function may be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_BAD_MAGIC - * OEMCrypto_ERROR_BAD_CRC - * - * Version: - * This method is supported by all API versions. + Validates the Widevine Keybox loaded into the security processor device. This + method verifies two fields in the keybox: + + Verify the MAGIC field contains a valid signature (such as, ‘k’’b’’o’’x’). + + Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the checksum + to the CRC stored in the Keybox. The CRC is computed over the entire Keybox + excluding the 4 bytes of the CRC (for example, Keybox[0..123]). For a + description of the fields stored in the keybox, see Keybox Definition. + + Parameters: + none + + Returns: + OEMCrypto_SUCCESS + OEMCrypto_ERROR_BAD_MAGIC + OEMCrypto_ERROR_BAD_CRC + + Threading: + This function may be called simultaneously with any session functions. + + Version: + This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_IsKeyboxValid(void); /* * OEMCrypto_GetDeviceID * - * Description: - * Retrieve the device's unique identifier from the Keybox. - * - * Parameters: - * deviceId (out) - pointer to the buffer that receives the Device ID - * idLength (in/out) - on input, size of the caller's device ID buffer. - * On output, the number of bytes written into the buffer. - * - * Threading: - * This function may be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID - * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id - * - * Version: - * This method is supported by all API versions. + Retrieve DeviceID from the Keybox. + + Parameters: + [out] deviceId - pointer to the buffer that receives the Device ID + [in/out] idLength – on input, size of the caller’s device ID buffer. On + output, the number of bytes written into the buffer. + + Returns: + OEMCrypto_SUCCESS success + OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID + OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + + Threading: + This function may be called simultaneously with any session functions. + + Version: + This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t *idLength); @@ -858,31 +1081,24 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, /* * OEMCrypto_GetKeyData * - * Description: - * Returns the Key Data field from the Keybox. The Key Data field does not - * need to be encrypted by an OEM root key, but may be if desired. - * - * If the Key Data field was encrypted with an OEM root key when the Keybox - * was stored on the device, then this function should decrypt it and return - * the clear Key Data. If the Key Data was not encrypted, then this function - * should just access and return the clear Key data. - * - * Parameters: - * keyData (out) - pointer to a caller-managed buffer to hold the Key Data - * field from the Keybox - * dataLength (in/out) - on input, the allocated buffer size. On output, - * the number of bytes in KeyData. - * - * Threading: - * This function may be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData - * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData - * - * Version: - * This method is supported by all API versions. + Return the Key Data field from the Keybox. + + Parameters: + [out] keyData - pointer to the buffer to hold the Key Data field from the + Keybox + [in/out] keyDataLength – on input, the allocated buffer size. On output, the + number of bytes in Key Data + + Returns: + OEMCrypto_SUCCESS success + OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return KeyData + OEMCrypto_ERROR_NO_KEYDATA + + Threading: + This function may be called simultaneously with any session functions. + + Version: + This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t *keyDataLength); @@ -890,25 +1106,23 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, /* * OEMCrypto_GetRandom * - * Description: - * Return a buffer filled with hardware-generated random bytes. If the - * hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED. - * - * Parameters: - * randomData (out) - Pointer to caller-manager buffer that will receive the - * random data. - * dataLength (in) - Length of the random data buffer in bytes. - * - * Threading: - * This function may be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_RNG_FAILED failed to generate random number - * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported - * - * Version: - * This method is supported by all API versions. + Returns a buffer filled with hardware-generated random bytes, if supported by + the hardware. + + Parameters: + [out] randomData - pointer to the buffer that receives random data + [in] dataLength - length of the random data buffer in bytes + + Returns: + OEMCrypto_SUCCESS success + OEMCrypto_ERROR_RNG_FAILED failed to generate random number + OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + + Threading: + This function may be called simultaneously with any session functions. + + Version: + This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength); @@ -917,13 +1131,53 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, * OEMCrypto_RewrapDeviceRSAKey * * Description: - * Verifies an RSA provisioning response is valid and corresponds - * to the previous provisioning request by checking the nonce. The RSA - * private key is decrypted and stored in secure memory. The RSA key is then - * re-encrypted for storage on the filesystem. The OEM may either encrypt it - * with the private key from the Widevine Keybox, or with an OEM specific - * device key. The signature of the message is verified with the - * mac_key_server. + * Verifies an RSA provisioning response is valid and corresponds to the previous + * provisioning request by checking the nonce. The RSA private key is decrypted + * and stored in secure memory. The RSA key is then re-encrypted and signed for + * storage on the filesystem. We recommend that the OEM use an encryption key + * and signing key generated using an algorithm at least as strong as that in + * GenerateDerivedKeys. + * + * After decrypting enc_rsa_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_rsa_key are not the string “SIGN”, + * then the default value of allowed_schemes = 1 will be used. + * + * Verification: + * 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_rsa_key_length is large enough to hold the rewrapped + * key, returning OEMCRYPTO_ERROR_BUFFER_TOO_SMALL otherwise. + * 3. Verify that the nonce matches one generated by a previous call to + * OEMCrypto_GenerateNonce(). The matching nonce shall be removed from the nonce + * table. If there is no matching nonce, return OEMCRYPTO_ERROR_INVALID_NONCE. + * 4. Verify the message signature, using the derived signing key + * (mac_key[server]). + * 5. Decrypt enc_rsa_key using the derived encryption key (enc_key), and + * enc_rsa_key_iv. + * 6. Validate the decrypted RSA device key by verifying that it can be loaded by + * the RSA implementation. + * 7. Generate a random initialization vector and store it in + * wrapped_rsa_key_iv. + * 8. Re-encrypt the device RSA key with an internal key (such as the OEM key or + * Widevine Keybox key) and the generated IV using AES-128-CBC with PKCS#5 + * padding. + * 9. Copy the rewrapped key to the buffer specified by wrapped_rsa_key and the + * size of the wrapped key to wrapped_rsa_key_length. * * Parameters: * session (in) - crypto session identifier. @@ -937,7 +1191,7 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, * nonce (in) - The nonce provided in the provisioning response. * enc_rsa_key (in) - Encrypted device private RSA key received from * - the provisioning server. Format is PKCS#8 - * - PrivateKeyInfo, encrypted with the derived + * - binary DER encoded, encrypted with the derived * - encryption key, using AES-128-CBC with PKCS#5 * - padding. * enc_rsa_key_length (in) - length of the encrypted RSA key, in bytes. @@ -947,6 +1201,10 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, * - in order to find required buffer size. * wrapped_rsa_key_length (in/out) - length of the encrypted RSA key, in bytes. * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_NO_DEVICE_KEY @@ -959,7 +1217,7 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API versions 8. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, @@ -978,15 +1236,28 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_LoadDeviceRSAKey * * Description: - * Loads a wrapped RSA private key to secure memory for use by this session - * in future calls to OEMCrypto_GenerateRSASignature. The wrapped RSA key - * will be one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA - * private key should be stored in secure memory. + * Loads a wrapped RSA private key to secure memory for use by this session in + * future calls to OEMCrypto_GenerateRSASignature. The wrapped RSA key will be + * the one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA private + * key should be stored in secure memory. + * + * If the bit field “allowed_schemes” was wrapped with this RSA key, its value + * will be loaded and stored with the RSA key. If there was not bit field + * wrapped with the RSA key, the key will use a default value of 1 = RSASSA-PSS + * with SHA1. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the RSA key is not loaded. + * 1. The wrapped key has a valid signature, as described in RewrapDeviceRSAKey. + * 2. The decrypted key is a valid private RSA key. + * 3. If a value for allowed_schemes is included with the key, it is a valid + * value. * * Parameters: * session (in) - crypto session identifier. * wrapped_rsa_key (in) - wrapped device RSA key stored on the device. - * - Format is PKCS#8 PrivateKeyInfo, and + * - Format is PKCS#8, binary DER encoded, and * - encrypted with a key internal to the OEMCrypto * - instance, using AES-128-CBC with PKCS#5 * - padding. This is the wrapped key generated @@ -995,6 +1266,10 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, * wrapped_rsa_key_iv (in) - The initialization vector used to encrypt * - wrapped_rsa_key. * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_NO_DEVICE_KEY @@ -1004,7 +1279,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 6. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, @@ -1014,12 +1289,36 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_GenerateRSASignature * * Description: - * The OEMCrypto_GenerateRSASignature method is used to sign messages using - * the device private RSA key, specifically, it is used to sign the initial - * license request. + * The OEMCrypto_GenerateRSASignature method is used to sign messages using the + * device private RSA key, specifically, it is used to sign the initial license + * request. Refer to the License Request Signed by RSA Certificate section above + * for more details. * - * Refer to the document "Widevine Security Integration Guide for DASH" for - * more details. + * For devices that wish to be CAST receivers, there is a new RSA padding scheme. + * The padding_scheme parameter indicates which hashing and padding is to be + * applied to the message so as to generate the encoded message (the + * modulus-sized block to which the integer conversion and RSA decryption is + * applied). The following values are defined: + * 0x1 - RSASSA-PSS with SHA1. + * 0x2 - PKCS1 with block type 1 padding (only). + * + * In the first case, a hash algorithm (SHA1) is first applied to the message, + * whose length is not otherwise restricted. In the second case, the "message" is + * already a digest, so no further hashing is applied, and the message_length can + * be no longer than 83 bytes. If the message_length is greater than 83 bytes + * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. + * + * The second padding scheme is for devices that use x509 certificates for + * authentication. The main example is devices that work as a Cast receiver, like + * a ChromeCast, not for devices that wish to send to the Cast device, such as + * almost all Android devices. OEMs that do not support x509 certificate + * authentication need not implement the second scheme and can return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Verification: + * The bitwise AND of the parameter padding_scheme and the RSA key’s + * allowed_schemes is computed. If this value is 0, then the signature is not + * computed and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. * * Parameters: * session (in) - crypto session identifier. @@ -1032,6 +1331,11 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, * - RSASSA-PSS. * signature_length (in/out) - (in) length of the signature buffer, in bytes. * - (out) actual length of the signature + * padding_scheme (in) - specify which scheme to use for the signature. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. * * Returns: * OEMCrypto_SUCCESS success @@ -1040,15 +1344,18 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_INVALID_RSA_KEY * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_NOT_IMPLEMENTED - if algorithm > 0, and the device does not + * support that algorithm. * * Version: - * This method changed in API version 6. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, - size_t *signature_length); + size_t *signature_length, + RSA_Padding_Scheme padding_scheme); /* * OEMCrypto_DeriveKeysFromSessionKey @@ -1067,6 +1374,11 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, * to LoadKeys and RefreshKeys proceed in the same manner for license * requests using RSA or using a Widevine keybox token. * + * Verification: + * If the RSA key’s allowed_schemes is not kSign_RSASSA_PSS, then no keys are + * derived and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. An RSA key + * cannot be used for both deriving session keys and also for PKCS1 signatures. + * * Parameters: * session (in) - crypto session identifier. * enc_session_key (in) - session key, encrypted with the device RSA key @@ -1080,6 +1392,10 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, * enc_key_context_length (in) - length of the encryption key context data, in * - bytes. * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED @@ -1089,7 +1405,7 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 8. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, const uint8_t* enc_session_key, @@ -1111,11 +1427,20 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, * There is a possibility that some API methods will be backwards compatible, * or backwards compatible at a reduced security level. * - * There is no plan to introduce forward-compatibility. I.e. applications - * will reject a library with a newer version of the API. + * There is no plan to introduce forward-compatibility. Applications will reject + * a library with a newer version of the API. + * + * The version specified in this document is 9. Any OEM that returns this + * version number guarantees it passes all unit tests associated this version. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. * * Returns: - * The current version number. + * The supported API, as specified in the header file OEMCryptoCENC.h. * * Version: * This method should change in all API versions. @@ -1134,46 +1459,131 @@ uint32_t OEMCrypto_APIVersion(); * Returns: * A null terminated string. Useful values are "L1", "L2" or "L3". * + * Threading: + * This function may be called simultaneously with any other functions. + * * Version: * This method changed in API version 6. */ const char* OEMCrypto_SecurityLevel(); /* - * OEMCryptoResult OEMCrypto_Generic_Encrypt + * OEMCrypto_GetHDCPCapability() * - * This function encrypts a generic buffer of data using the current key. + * Description: + * Returns the maximum HDCP version supported by the device, and the HDCP version + * supported by the device and any connected display. * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and the data is not encrypted. - * - * The control bit for the current key shall have the Allow_Encrypt set. If - * not, return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * Valid values for HDCP_Capability are: + * 0x0 - No HDCP supported, no secure data path. + * 0x1 - HDCP version 1.0 + * 0x2 - HDCP version 2.0 + * 0x3 - HDCP version 2.1 + * 0x4 - HDCP version 2.2 + * 0xFF - No HDCP device attached/using local display with secure path. * * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] iv: IV for encrypting data. Size is specified by the algorithm. - * [in] algorithm: Specifies which encryption algorithm to use. See - * OEMCrypto_Algorithm for valid values. - * [out] out_buffer: pointer to buffer in which encrypted data should be stored. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE + * current (out) - this is the current HDCP version, based on the device itself, + * and the display to which it is connected. + * maximum (out) - this is the maximum supported HDCP version for the device, + * ignoring any attached device. * * Threading: - * This function may be called simultaneously with functions on other sessions, - * but not with other functions on this session. + * This function may be called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 7. + * This method changed in API version 9. + */ +typedef uint8_t OEMCrypto_HDCP_Capability; +OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, + OEMCrypto_HDCP_Capability *maximum); + +/* + * OEMCrypto_SupportsUsageTable() + * + * Description: + * This is used to determine if the device can support a usage table. Since this + * function is spoofable, it is not relied on for security purposes. It is for + * information only. The usage table is described in the section above. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns true if the device can maintain a usage table. Returns false otherwise. + * + * Version: + * This method changed in API version 9. + */ +bool OEMCrypto_SupportsUsageTable(); + +/* + * OEMCryptoResult OEMCrypto_Generic_Encrypt + * + * Description: + * This function encrypts a generic buffer of data using the current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will update the + * time_of_last_decrypt. If the status of the entry is “unused”, then change the + * status to “active” and set the time_of_first_decrypt. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not encrypted. + * 1. The control bit for the current key shall have the Allow_Encrypt set. If + * not, return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 2. If the current key’s control block has a nonzero Duration field, then the + * API shall verify that the duration is greater than the session’s elapsed time + * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 3. If the current session has an entry in the Usage Table, and the status of + * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * current (out) - this is the current HDCP version, based on the device itself, + * and the display to which it is connected. + * maximum (out) - this is the maximum supported HDCP version for the device, + * ignoring any attached device. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +typedef uint8_t OEMCrypto_HDCP_Capability; +OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, + OEMCrypto_HDCP_Capability *maximum); + +/* + * OEMCrypto_SupportsUsageTable() + * + * Description: + * This is used to determine if the device can support a usage table. Since this + * function is spoofable, it is not relied on for security purposes. It is for + * information only. The usage table is described in the section above. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns true if the device can maintain a usage table. Returns false otherwise. + * + * Version: + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, @@ -1185,42 +1595,52 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, /* * OEMCrypto_Generic_Decrypt * - * This function decrypts a generic buffer of data using the current key. + * Description: + * This function decrypts a generic buffer of data using the current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will update the + * time_of_last_decrypt. If the status of the entry is “unused”, then change the + * status to “active” and set the time_of_first_decrypt. * * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and the data is not decrypted. - * - * The control bit for the current key shall have the Allow_Decrypt set. If - * not, return OEMCrypto_ERROR_DECRYPT_FAILED. - * If the current key’s control block has the Data_Path_Type bit set, then - * return OEMCrypto_ERROR_DECRYPT_FAILED. - * If the current key’s control block has the HDCP bit set, then return - * OEMCrypto_ERROR_DECRYPT_FAILED. + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not decrypted. + * 1. The control bit for the current key shall have the Allow_Decrypt set. If + * not, return OEMCrypto_ERROR_DECRYPT_FAILED. + * 2. If the current key’s control block has the Data_Path_Type bit set, then + * return OEMCrypto_ERROR_DECRYPT_FAILED. + * 1. If the current key’s control block has the HDCP bit set, then return + * OEMCrypto_ERROR_DECRYPT_FAILED. + * 2. If the current key’s control block has a nonzero Duration field, then the + * API shall verify that the duration is greater than the session’s elapsed time + * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 3. If the current session has an entry in the Usage Table, and the status of + * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. * * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] iv: IV for encrypting data. Size depends on the algorithm. - * [in] algorithm: Specifies which encryption algorithm to use. See - * OEMCrypto_Algorithm for valid values. - * [out] out_buffer: pointer to buffer in which decrypted data should be stored. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. The algorithm may + * restrict buffer_length to be a multiple of block size. + * iv (in) - IV for encrypting data. Size is 128 bits. + * algorithm (in) - Specifies which encryption algorithm to use. + * out_buffer (out) - pointer to buffer in which decrypted data should be stored. * * Threading: - * This function may be called simultaneously with functions on other sessions, - * but not with other functions on this session. + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_DECRYPT_FAILED + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 7. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, @@ -1232,41 +1652,51 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, /* * OEMCrypto_Generic_Sign * - * This function signs a generic buffer of data using the current key. + * Description: + * This function signs a generic buffer of data using the current key. * - * Verification - * The following checks should be performed. If any check fails, - * an error is returned, and the signature is not generated. + * If the session has an entry in the Usage Table, then OEMCrypto will update the + * time_of_last_decrypt. If the status of the entry is “unused”, then change the + * status to “active” and set the time_of_first_decrypt. * - * The control bit for the current key shall have the Allow_Sign set. + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not signed. + * 1. The control bit for the current key shall have the Allow_Sign set. + * 2. If the current key’s control block has a nonzero Duration field, then the + * API shall verify that the duration is greater than the session’s elapsed time + * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 3. If the current session has an entry in the Usage Table, and the status of + * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. * - * Parameters - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. See - * OEMCrypto_Algorithm for valid values. - * [out] signature: pointer to buffer in which signature should be stored. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * algorithm (in) - Specifies which algorithm to use. + * signature (out) - pointer to buffer in which signature should be stored. May + * be null on the first call in order to find required buffer size. + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * (out) actual length of the signature * - * Returns - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to hold - * signature. - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_DECRYPT_FAILED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE * - * Threading - * This function may be called simultaneously with functions on other sessions, - * but not with other functions on this session. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to hold + * the output signature. + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 7. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, const uint8_t* in_buffer, @@ -1277,42 +1707,53 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, /* * OEMCrypto_Generic_Verify - * This function verfies the signature of a generic buffer of data using the - * current key. * - * Verification - * The following checks should be performed. If any check fails, an error is - * returned, and the data is not signed. + * Description: + * This function verifies the signature of a generic buffer of data using the + * current key. * - * The control bit for the current key shall have the Allow_Verify set. - * The signature of the message shall be computed, and the API shall verify the - * computed signature matches the signature passed in. If not, return - * OEMCrypto_ERROR_SIGNATURE_FAILURE + * If the session has an entry in the Usage Table, then OEMCrypto will update the + * time_of_last_decrypt. If the status of the entry is “unused”, then change the + * status to “active” and set the time_of_first_decrypt. * - * Parameters - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. Current valid value is - * HMAC_SHA256. - * [in] signature: pointer to signature buffer. - * [in] signature_length: length of the signature buffer, in bytes. + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned. + * 1. The control bit for the current key shall have the Allow_Verify set. + * 2. The signature of the message shall be computed, and the API shall verify + * the computed signature matches the signature passed in. If not, return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. + * 3. The signature verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * 4. If the current key’s control block has a nonzero Duration field, then the + * API shall verify that the duration is greater than the session’s elapsed time + * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 5. If the current session has an entry in the Usage Table, and the status of + * that entry is “inactive”, then return OEMCrypto_ERROR_INVALID_SESSION. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * algorithm (in) - Specifies which algorithm to use. + * signature (in) - pointer to buffer in which signature resides. + * signature_length (in) - length of the signature buffer, in bytes. * * Threading: - * This function may be called simultaneously with functions on other sessions, - * but not with other functions on this session. + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 7. + * This method changed in API version 9. */ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, const uint8_t* in_buffer, @@ -1321,6 +1762,236 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); + +/* + * OEMCrypto_UpdateUsageTable + * + * Description: + * OEMCrypto should propagate values from all open sessions to the Session Usage + * Table. If any values have changed, increment the generation number, sign, and + * save the table. During playback, this function will be called approximately + * once per minute. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_UpdateUsageTable(); + +/* + * OEMCrypto_DeactivateUsageEntry + * + * Description: + * Find the entry in the Usage Table with a matching PST. Mark the status of + * that entry as “inactive”. If it corresponds to an open session, the status of + * that session will also be marked as “inactive”. Then OEMCrypto will increment + * Usage Table’s generation number, sign, encrypt, and save the Usage Table. + * + * If no entry in the Usage Table has a matching PST, return the error + * OEMCrypto_ERROR_INVALID_CONTEXT. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * pst (in) - pointer to memory containing Provider Session Token. + * pst_length (in) - length of the pst, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_CONTEXT - no entry has matching PST. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst, + size_t pst_length); + +/* + * OEMCrypto_ReportUsage + * + * Description: + * If the buffer_length is not sufficient to hold a report structure, set + * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * + * If no entry in the Usage Table has a matching PST, return the error + * OEMCrypto_ERROR_INVALID_CONTEXT. + * + * OEMCrypto will increment Usage Table’s generation number, sign, encrypt, and + * save the Usage Table. This is done, even though the table has not changed, so + * that a single rollback cannot undo a call to DeactivateUsageEntry and still + * report that license as inactive. + * + * The pst_report is filled out by subtracting the times un the Usage Table from + * the current time on the secure clock. This is done in case the secure clock + * is not using UTC time, but is instead using something like seconds since clock + * installed. + * + * Valid values for status are: + * 0 = kUnused -- the keys have not been used to decrypt. + * 1 = kActive -- the keys have been used, and have not been deactivated. + * 2 = kInactive -- the keys have been marked inactive. + * + * The clock_security_level is reported as follows: + * 0 = Insecure Clock - clock just uses system time. + * 1 = Secure Timer - clock uses secure timer, which cannot be modified by user + * software, when OEMCrypto is active and the system time when OEMCrypto is + * inactive. + * 2 = Software Secure Clock - clock cannot be modified by user software when + * OEMCrypto is active or inactive. + * 3 = Hardware Secure Clock - clock cannot be modified by user software and + * there are security features that prevent the user from modifying the clock + * in hardware, such as a tamper proof battery. + * + * After pst_report has been filled in, the HMAC SHA1 signature is computed for + * the buffer from bytes 20 to the end of the pst field. The signature is + * computed using the client_mac_key which is stored in the usage table. The + * HMAC SHA1 signature is used to prevent a rogue application from using + * OMECrypto_GenerateSignature to forge a Usage Report. + * + * This function also copies the client_mac_key and server_mac_key from the Usage + * Table entry to the session. They will be used to verify a signature in + * OEMCrypto_DeleteUsageEntry below. This session will be associated with the + * entry in the Usage Table. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * session (in) - handle for the session to be used. + * pst (in) - pointer to memory containing Provider Session Token. + * pst_length (in) - length of the pst, in bytes. + * buffer (out) - pointer to buffer in which usage report should be stored. May + * be null on the first call in order to find required buffer size. + * buffer_length (in/out) - (in) length of the report buffer, in bytes. + * (out) actual length of the report + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER if report buffer is not large enough to hold the + * output signature. + * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * OEMCrypto_ERROR_INVALID_CONTEXT - no entry has matching PST. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t *pst, + size_t pst_length, + OEMCrypto_PST_Report *buffer, + size_t *buffer_length); + +/* + * OEMCrypto_DeleteUsageEntry + * + * Description: + * This function verifies the signature of the given message using the sessions + * mac_key[server] and the algorithm HMAC-SHA256, and then deletes an entry from + * the session table. The session should already be associated with the given + * entry, from a previous call to OEMCrypto_ReportUsage. + * + * After performing all verification listed below, and deleting the entry from + * the Usage Table, OEMCrypto will increment Usage Table’s generation number, and + * then sign, encrypt, and save the Usage Table. + * + * The signature verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned. + * 1. The pointer pst is not null, and points inside the message. If not, return + * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 2. The signature of the message shall be computed, and the API shall verify + * the computed signature matches the signature passed in. The signature will be + * computed using HMAC-SHA256 and the mac_key_server. If they do not match, + * return OEMCrypto_ERROR_SIGNATURE_FAILURE. + * 3. If the session is not associated with an entry in the Usage Table, return + * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 4. If the pst passed in as a parameter does not match that in the Usage Table, + * return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * + * Parameters: + * session (in) - handle for the session to be used. + * pst (in) - pointer to memory containing Provider Session Token. + * pst_length (in) - length of the pst, in bytes. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message, in bytes. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length, + const uint8_t *message, + size_t message_length, + const uint8_t *signature, + size_t signature_length); + +/* + * OEMCrypto_DeleteUsageTable + * + * Description: + * This is called when the CDM system believes there are major problems or + * resource issues. The entire table should be cleaned and a new table should be + * created. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_DeleteUsageTable(); + #ifdef __cplusplus } #endif diff --git a/libwvdrmengine/oemcrypto/include/level3.h b/libwvdrmengine/oemcrypto/include/level3.h index d8cb3a7f..616ec01f 100644 --- a/libwvdrmengine/oemcrypto/include/level3.h +++ b/libwvdrmengine/oemcrypto/include/level3.h @@ -70,7 +70,9 @@ OEMCryptoResult Level3_LoadKeys(OEMCrypto_SESSION session, const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys, - const OEMCrypto_KeyObject* key_array); + const OEMCrypto_KeyObject* key_array, + const uint8_t* pst, + size_t pst_length); OEMCryptoResult Level3_RefreshKeys(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, @@ -122,7 +124,8 @@ OEMCryptoResult Level3_GenerateRSASignature(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, - size_t *signature_length); + size_t *signature_length, + RSA_Padding_Scheme algorithm); OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, const uint8_t* enc_session_key, size_t enc_session_key_length, diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 0c36afc3..b6464733 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -263,7 +263,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_keys, size_t num_keys, - const OEMCrypto_KeyObject* key_array) { + const OEMCrypto_KeyObject* key_array, + const uint8_t* pst, + size_t pst_length) { if (trace_all_calls) { printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n"); dump_hex("message", message, message_length); @@ -892,7 +894,8 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, - size_t* signature_length) { + size_t* signature_length, + RSA_Padding_Scheme algorithm) { if (trace_all_calls) { printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); dump_hex("message", message, message_length); @@ -925,6 +928,11 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, return OEMCrypto_ERROR_INVALID_CONTEXT; } + if (algorithm != kSign_RSASSA_PSS) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_NOT_IMPLEMENTED]"); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (session_ctx->GenerateRSASignature(message, message_length, signature, @@ -987,7 +995,7 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( extern "C" uint32_t OEMCrypto_APIVersion() { - return oec_latest_version; + return 9; } extern "C" @@ -995,6 +1003,16 @@ const char* OEMCrypto_SecurityLevel() { return "L3"; } +extern "C" +OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, + OEMCrypto_HDCP_Capability *maximum) { + if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + *current = 0; + *maximum = 0; + return OEMCrypto_SUCCESS; +} + extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, @@ -1114,4 +1132,44 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, return OEMCrypto_SUCCESS; } +extern "C" +bool OEMCrypto_SupportsUsageTable() { + return false; +} +extern "C" +OEMCryptoResult OEMCrypto_UpdateUsageTable() { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst, + size_t pst_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t *pst, + size_t pst_length, + OEMCrypto_PST_Report *buffer, + size_t *buffer_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length, + const uint8_t *message, + size_t message_length, + const uint8_t *signature, + size_t signature_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" +OEMCryptoResult OEMCrypto_DeleteUsageTable() { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + }; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 50db642c..de64eda9 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -951,7 +951,7 @@ class Session { OEMCrypto_LoadKeys(session_id(), message_ptr, sizeof(encrypted), &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array)); + kNumKeys, key_array, NULL, 0)); // Update new generated keys. memcpy(&mac_key_server_[0], data.mac_keys, wvcdm::MAC_KEY_SIZE); memcpy(&mac_key_client_[0], data.mac_keys+wvcdm::MAC_KEY_SIZE, @@ -1496,7 +1496,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { cout << " OEMCrypto Security Level is "<< level << endl; uint32_t version = OEMCrypto_APIVersion(); cout << " OEMCrypto API version is " << version << endl; - ASSERT_EQ(oec_latest_version, version); + ASSERT_EQ(9, version); testTearDown(); } @@ -1952,7 +1952,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithNoMAC) { message_ptr, sizeof(encrypted), &signature[0], signature.size(), NULL, NULL, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_EQ(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -1987,7 +1987,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) { &signature[0], signature.size(), encrypted.mac_key_iv, &mac_keys[0], // Not pointing into buffer. - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2019,7 +2019,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) { &signature[0], signature.size(), &mac_key_iv[0], // bad. encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2053,7 +2053,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2087,7 +2087,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2121,7 +2121,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2155,7 +2155,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2189,7 +2189,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); s.close(); testTearDown(); @@ -2220,7 +2220,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -2254,7 +2254,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -2289,7 +2289,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -2322,7 +2322,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_NE(OEMCrypto_SUCCESS, sts); @@ -3131,7 +3131,7 @@ TEST_F(DISABLED_TestKeybox, RSASignature) { sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), NULL, - &signature_length); + &signature_length, kSign_RSASSA_PSS); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); @@ -3140,7 +3140,7 @@ TEST_F(DISABLED_TestKeybox, RSASignature) { sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), signature, - &signature_length); + &signature_length, kSign_RSASSA_PSS); ASSERT_EQ(OEMCrypto_SUCCESS, sts); // In the real world, the signature above would just have been used to contact @@ -3270,7 +3270,7 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox { &signature[0], signature.size(), encrypted.mac_key_iv, encrypted.mac_keys, - kNumKeys, key_array); + kNumKeys, key_array, NULL, 0); ASSERT_EQ(OEMCrypto_SUCCESS, sts); }