Files
whitebox/api/license_whitebox.h
Aaron Vaage 5d90e8d89b Benchmarking and Unmasking
In this code drop we introduce the benchmarking tests that allow us to
compare the performance of different implementations. Like the other
tests, any implementation can link with them to create their own
binary.

There are two types of benchmarks:
  1 - Throughput, which measures the speed that a function can process
      information (bits per second). These are used for AEAD decrypt
      and license white-box decrypt functions.
  2 - Samples, which measures the min, 25% percentile, median, 75%
      percentile, and max observed values. These is used for all other
      functions as a way to measure the execute duration of a call.

The other change in this code drop is the update to the unmasking
function to only unmask a subset of the bytes in the masked buffer.
This was added to better align with the decoder behaviour in the CDM.
2020-06-24 15:30:50 -07:00

408 lines
16 KiB
C

// 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.
//
// Extracts and loads content and signing keys for use. Any content keys that
// exceed the security levels permitted by the instance, will be thrown away,
// but the key ids are retained (see WB_RESULT_INSUFFICIENT_SECURITY_LEVEL). All
// non-content keys and non-signing keys will be thrown away.
//
// 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_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, or if |license_request_size| was zero.
//
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature does not match
// |signature|.
//
// WB_RESULT_INVALID_STATE if a license has already been loaded.
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 not loaded a license.
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_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |secret_string_size| (as input) was less than
// the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
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_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
// the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
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 |masked_output_data| was null, or if |masked_output_data_size|
// was null.
//
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
// the license, but the |whitebox| was not allowed to use it.
//
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
// loaded license.
//
// WB_RESULT_BUFFER_TOO_SMALL if |masked_output_data_size| (as input) was less
// than the required size.
//
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
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 a subset of the data in |masked_data| using |secret_string| and
// writes it to |unmasked_data|.
//
// The subset is denoted as |offset| (inclusive) to |offset + size| (exclusive).
// It is assumed that |offset| and |offset + size - 1| are both valid indexes
// into |masked_data|.
//
// It is assumed that indexes between 0 and |size - 1| (inclusive) are all valid
// indexes into |unmasked_data|.
//
// Args:
// masked_data (in) : The masked data to read from.
//
// offset (in) : The index into |masked_data| from where to start reading data.
//
// size (in) : The number of bytes from |masked_data| to unmask and copy into
// |unmasked_data|.
//
// secret_string (in) : The auxiliary data for unmasking |masked_data|.
//
// secret_string_size (in) : The number of bytes in |secret_string|.
//
// unmasked_data (out) : The output buffer to write the unmasked data to.
void WB_License_Unmask(const uint8_t* masked_data,
size_t offset,
size_t size,
const uint8_t* secret_string,
size_t secret_string_size,
uint8_t* unmasked_data);
#ifdef __cplusplus
}
#endif
#endif // WHITEBOX_API_LICENSE_WHITEBOX_H_