Initial Code Drop
This is the initial code drop of the reference implementation and test cases for the Widevine Whitebox API. In this drop, the full reference implementation for the AEAD white-box is provided and all test cases verifying the top-level behave have are enabled. Since the implementations can vary so much the testing is mostly left to verifying the return codes for specific parameter conditions. A full reference implementation for the license white-box is provided, however not all tests are implemented or enabled. A number of tests have been disabled as they required a loaded license and test licenses are still being worked on. The two license white-box API functions that are the further from competition are ProcessLicenseResponse() and MaskedDecryt(). ProcessLicenseResponse() is still being worked on and MaskedDecrypt() is waiting on Decrypt() to be fully functional. Most tests focus on verifying return code for specific parameter conditions, but as test licenses are created, tests looking to test the internal behaviour of license management will be added to ProcessLicenseResponse(), Decrypt(), and MaskedDecrypt().
This commit is contained in:
397
api/license_whitebox.h
Normal file
397
api/license_whitebox.h
Normal file
@@ -0,0 +1,397 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
#define WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/result.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// The opaque type representing a single white-box instance.
|
||||
typedef struct WB_License_Whitebox WB_License_Whitebox;
|
||||
|
||||
typedef enum {
|
||||
WB_CIPHER_MODE_CTR,
|
||||
WB_CIPHER_MODE_CBC,
|
||||
} WB_CipherMode;
|
||||
|
||||
// Creates a new white-box instance using |whitebox_init_data|. A pointer to the
|
||||
// white-box instance will be returned via |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox_init_data (in) : The implementation-specific initialization data
|
||||
// needed to initialize a new white-box instance.
|
||||
//
|
||||
// whitebox_init_data_size (in) : The number of bytes in |whitebox_init_data|.
|
||||
//
|
||||
// whitebox (out) : The output parameter used to return the new white-box
|
||||
// instance.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the white-box instance was successfully created.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null or invalid.
|
||||
//
|
||||
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
|
||||
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
WB_License_Whitebox** whitebox);
|
||||
|
||||
// Releases all resources used by the white-box instance pointed to by
|
||||
// |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to a white-box instance. Passing in null will
|
||||
// result in a no-op.
|
||||
void WB_License_Delete(WB_License_Whitebox* whitebox);
|
||||
|
||||
// Signs a license request using the CDM's private signing key.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to the current white-box instance.
|
||||
//
|
||||
// license_request (in) : The license request in serialized form.
|
||||
//
|
||||
// license_request_size (in) : The number of bytes in the license_request.
|
||||
//
|
||||
// signature (out) : The generated signature for |license_request|.
|
||||
//
|
||||
// signature_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |signature|. As output, |signature_size| is set to
|
||||
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the signature was successfully generated.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |license_request| was
|
||||
// null, if |license_request_size| was zero, if |signature| was null, or if
|
||||
// |signature_size| was null.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
|
||||
// required size.
|
||||
WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies a license response using HMAC and the server signing key, extract
|
||||
// the content keys and signing keys, and load them into the white-box.
|
||||
//
|
||||
// This function can only be called once per white-box instance. To parse a new
|
||||
// license response, a new white-box instance must be used.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in/out) : The white-box instance that will load the keys.
|
||||
//
|
||||
// message (in) : The message field of the license response.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (in) : The signature field of the license response.
|
||||
//
|
||||
// signature_size (in) : The number of bytes in |signature|.
|
||||
//
|
||||
// session_key (in) : The session key field of the license response.
|
||||
//
|
||||
// session_key_size (in) : The number of bytes in |session_key|.
|
||||
//
|
||||
// license_request (in) : The license request that was sent in order to get the
|
||||
// license response.
|
||||
//
|
||||
// license_request_size (in) : The number of bytes in |license_request|.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the response was verified and the keys were loaded into
|
||||
// |whitebox|.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature does not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |message| did not conform to the expected
|
||||
// format, if |signature| was null, if |signature_size| was incorrect, if
|
||||
// |session_key| was null, if |session_key_size| was incorrect, if
|
||||
// |session_key| could not be unwrapped correctly, if |license_request| was
|
||||
// null, if |license_request_size| was zero, or if |license_request| was
|
||||
// malformed.
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size,
|
||||
const uint8_t* session_key,
|
||||
size_t session_key_size,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size);
|
||||
|
||||
// Signs |message| and return the signature via |signature| using HMAC and the
|
||||
// client renewal signing key
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box instance containing the signing key.
|
||||
//
|
||||
// message (in) : The message that should be signed.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (out) : The output parameter used to return the signature.
|
||||
//
|
||||
// signature_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |signature|. As output, |signature_size| is set to
|
||||
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |message| was successfully signed.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |signature| was null, or if |signature_size|
|
||||
// was null.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
|
||||
// required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no signing keys.
|
||||
WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies the renewal response using HMAC and the server signing key.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the server signing key.
|
||||
//
|
||||
// message (in) : The message that needs to be verified.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (in) : The expected signature for |message|.
|
||||
//
|
||||
// signature_size (in) : The number of bytes in |signature|.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |message|'s signature matches |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |signature| was null, of if |signature_size|
|
||||
// was incorrect.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature did not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size);
|
||||
|
||||
// Gets the secret string needed by WB_License_Unmask() in order to unmask the
|
||||
// masked decrypted content returned by WB_License_MaskedDecrypt().
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box instance that will be used for the calls to
|
||||
// WB_License_MaskedDecrypt().
|
||||
//
|
||||
// mode (in) : The AES decryption mode that will be used for the calls to
|
||||
// WB_License_MaskedDecrypt().
|
||||
//
|
||||
// key_id (in) : The key id that will be used for the call to
|
||||
// WB_License_MaskedDecrypt(). The key id must match a key loaded in
|
||||
// |whitebox|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// secret_string (out) : The output parameter used to return the secret string
|
||||
// that can be passed to WB_License_Unmask().
|
||||
//
|
||||
// secret_string_size (in/out) : As input, this contains the max number of
|
||||
// bytes that can be written to |secret_string|. As output,
|
||||
// |secret_string_size| is set to the required size on WB_RESULT_OK and
|
||||
// WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |key_id| matches a key from the license response and the
|
||||
// secret string was written to |secret_string|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |secret_string| was
|
||||
// null, or if |secret_string_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| did not match any keys in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| refers to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |secret_string_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// mode (in) : The decryption algorithm that should be used to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// input_data (in) : The ciphertext.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
|
||||
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
|
||||
// this |input_data_size| must be greater than zero.
|
||||
//
|
||||
// iv (in) : The iv.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|. This must be 16.
|
||||
//
|
||||
// output_data (out) : The output parameter for the plaintext.
|
||||
//
|
||||
// output_data_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |output_data|. As output, |output_data_size| is set
|
||||
// to the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
|
||||
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
|
||||
// invalid, if |output_data| was null, or if |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but
|
||||
// the key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was not SW_SECURE_CRYPTO.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
// Decrypts |input_data| and write the obfuscated plaintext to
|
||||
// |masked_output_data|. The obfuscated plaintext can be deobfuscated using
|
||||
// WB_License_GetSecretString() and WB_License_Unmask().
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// mode (in) : The decryption algorithm that should be used to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// input_data (in) : The ciphertext.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
|
||||
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
|
||||
// this |input_data_size| must be greater than zero.
|
||||
//
|
||||
// iv (in) : The iv.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|. This must be 16.
|
||||
//
|
||||
// masked_output_data (out) : The output parameter for the obfuscated
|
||||
// plaintext.
|
||||
//
|
||||
// masked_output_data_size (in/out) : As input, this contains the max number of
|
||||
// bytes that can be written to |masked_output_data|. As output,
|
||||
// |masked_output_data_size| is set to the required size on WB_RESULT_OK and
|
||||
// WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
|
||||
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
|
||||
// invalid, if |output_data| was null, or if |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |masked_output_data_size| (as input) was less
|
||||
// than the required size.
|
||||
WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* masked_output_data,
|
||||
size_t* masked_output_data_size);
|
||||
|
||||
// Unmasks the data in |buffer| using |secret_string|. |buffer| is operated on
|
||||
// in-place.
|
||||
//
|
||||
// Args:
|
||||
// secret_string (in) : The "key" used to unmask the data in |buffer|.
|
||||
//
|
||||
// secret_string_size (in) : The number of bytes in |secret_string|.
|
||||
//
|
||||
// buffer (in/out) : As input, this is the masked data. As output, this is the
|
||||
// unmasked data. The number of bytes in the masked data will be equal to the
|
||||
// number of bytes in the unmasked data.
|
||||
//
|
||||
// buffer_size (in) : The number of bytes in |buffer|.
|
||||
void WB_License_Unmask(const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
Reference in New Issue
Block a user