Automated update of OPK code

Included changes:

  - 676ac7be8548d80c420591fc0b4fb9a11723ef34 Backwards compatibility script for CDM v18 and OPK v19 by Vicky Min <vickymin@google.com>
  - 3cd4f71fda91245ac0b61c4c847950952f3021c0 Change BuildInformation ree fields to optional by Matt Feddersen <mattfedd@google.com>
  - a2259e95dea40c27a4be02ad479aec8f1fc84737 Created a DICE CBOR Cert parser/serializer. by Alex Dale <sigquit@google.com>
  - b8f2c364afeb6279e5aee6488d4527e189ac42ff Don't create invalid enum value by John "Juce" Bruce <juce@google.com>
  - b0aed212a3b2dd8f752d8fc43982848c1aa6c152 Created an HLS Key type. by Alex Dale <sigquit@google.com>
  - f8cfc54b41f124ba849596dbe6438b7f271a72b7 Specify C/C++ standard when running clang-tidy on OPK by John "Juce" Bruce <juce@google.com>

GitOrigin-RevId: 676ac7be8548d80c420591fc0b4fb9a11723ef34
This commit is contained in:
Googler
2025-05-13 21:44:08 +00:00
committed by mattfedd
parent a2b9e085e9
commit 5387878a5b
25 changed files with 3139 additions and 937 deletions

View File

@@ -33,8 +33,8 @@ void InitLogging() {
void Log(const char* file, const char* function, int line, LogPriority level, void Log(const char* file, const char* function, int line, LogPriority level,
const char* fmt, ...) { const char* fmt, ...) {
const char* severities[] = {"ERROR", "WARN", "INFO", "DEBUG", "VERBOSE"}; const char* severities[] = {"ERROR", "WARN", "INFO", "DEBUG", "VERBOSE"};
if (level < 0 || level >= static_cast<LogPriority>(sizeof(severities) / if (level < 0 || static_cast<size_t>(level) >=
sizeof(severities[0]))) { sizeof(severities) / sizeof(severities[0])) {
fprintf(kOutputFile, "[FATAL:%s(%d):%s] Invalid log priority level: %d\n", fprintf(kOutputFile, "[FATAL:%s(%d):%s] Invalid log priority level: %d\n",
file, line, function, level); file, line, function, level);
return; return;

View File

@@ -1,159 +0,0 @@
#!/usr/bin/python3
# Copyright 2017 Google LLC. All Rights Reserved.
"""Common test utility functions for OEM certificate generation."""
import datetime
import io
from cryptography import x509
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509 import oid
import oem_certificate
_COUNTRY_NAME = 'US'
_STATE_OR_PROVINCE_NAME = 'WA'
_LOCALITY_NAME = 'Kirkland'
_ORGANIZATION_NAME = 'CompanyXYZ'
_ORGANIZATIONAL_UNIT_NAME = 'ContentProtection'
_NOT_VALID_BEFORE = datetime.datetime(2001, 8, 9)
_VALID_DURATION = 100
_LEAF_CERT_VALID_DURATION = 8000
_SYSTEM_ID = 2001
_ROOT_PRIVATE_KEY_PASSPHRASE = b'root_passphrase'
class ArgParseObject(object):
"""A convenient object to allow adding arbitrary attribute to it."""
def create_root_certificate_and_key():
"""Creates a root certificate and key."""
key = rsa.generate_private_key(
public_exponent=65537,
key_size=3072,
backend=backends.default_backend())
subject_name = x509.Name(
[x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')])
certificate = oem_certificate.build_certificate(
subject_name, subject_name, None,
datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True)
return (key, certificate)
def setup_csr_args(country_name=_COUNTRY_NAME,
state_or_province_name=_STATE_OR_PROVINCE_NAME,
locality_name=_LOCALITY_NAME,
organization_name=_ORGANIZATION_NAME,
organizational_unit_name=_ORGANIZATIONAL_UNIT_NAME,
key_size=4096,
output_csr_file=None,
output_private_key_file=None,
passphrase=None,
common_name=None):
"""Sets up arguments to OEM Certificate generator for generating csr."""
args = ArgParseObject()
args.key_size = key_size
args.country_name = country_name
args.state_or_province_name = state_or_province_name
args.locality_name = locality_name
args.organization_name = organization_name
args.organizational_unit_name = organizational_unit_name
args.common_name = common_name
if output_csr_file:
args.output_csr_file = output_csr_file
else:
args.output_csr_file = io.BytesIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = io.BytesIO()
args.passphrase = passphrase
return args
def setup_intermediate_cert_args(
csr_bytes, root_key, root_certificate, not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_VALID_DURATION, system_id=_SYSTEM_ID,
root_private_key_passphrase=_ROOT_PRIVATE_KEY_PASSPHRASE,
output_certificate_file=None):
"""Sets up args to OEM Cert generator for generating intermediate cert."""
args = ArgParseObject()
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.system_id = system_id
args.csr_file = io.BytesIO(csr_bytes)
args.root_private_key_passphrase = root_private_key_passphrase
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = io.BytesIO()
serialized_private_key = root_key.private_bytes(
serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(
args.root_private_key_passphrase))
serialized_certificate = root_certificate.public_bytes(
serialization.Encoding.DER)
args.root_certificate_file = io.BytesIO(serialized_certificate)
args.root_private_key_file = io.BytesIO(serialized_private_key)
return args
def setup_leaf_cert_args(intermediate_key_bytes,
intermediate_certificate_bytes,
key_size=1024,
passphrase=None,
not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_LEAF_CERT_VALID_DURATION,
output_certificate_file=None,
output_private_key_file=None):
"""Sets up args to OEM Certificate generator for generating leaf cert."""
args = ArgParseObject()
args.key_size = key_size
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.intermediate_private_key_passphrase = None
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = io.BytesIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = io.BytesIO()
args.passphrase = passphrase
args.intermediate_private_key_file = io.BytesIO(
intermediate_key_bytes)
args.intermediate_certificate_file = io.BytesIO(
intermediate_certificate_bytes)
return args
def create_intermediate_certificate_and_key_bytes(key_size=4096,
passphrase=None,
pem_format=True):
"""Creates an intermediate certificate and key."""
csr_args = setup_csr_args(key_size=key_size, passphrase=passphrase)
oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue()
root_key, root_certificate = create_root_certificate_and_key()
args = setup_intermediate_cert_args(csr_bytes, root_key, root_certificate)
oem_certificate.generate_intermediate_certificate(args)
cert_bytes = args.output_certificate_file.getvalue()
if pem_format:
cert = x509.load_der_x509_certificate(cert_bytes,
backends.default_backend())
cert_bytes = cert.public_bytes(serialization.Encoding.PEM)
return (csr_args.output_private_key_file.getvalue(), cert_bytes)

View File

@@ -3629,7 +3629,9 @@ uint32_t OEMCrypto_MinorAPIVersion(void);
* defined * defined
* *
* While not required, another optional top level struct can be added to the * While not required, another optional top level struct can be added to the
* build information string to provide information about liboemcrypto.so: * build information string to provide information about liboemcrypto.so. The
* fields within this struct are not required, but if they are included they
* must match the listed data type:
* - "ree" { * - "ree" {
* - "liboemcrypto_ver" [string]: liboemcrypto.so version in string format * - "liboemcrypto_ver" [string]: liboemcrypto.so version in string format
* eg "2.15.0+tag". Note that this is separate from the "ta_ver" field * eg "2.15.0+tag". Note that this is separate from the "ta_ver" field

View File

@@ -1,700 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
/*********************************************************************
* level3.h
*
* Reference APIs needed to support Widevine's crypto algorithms.
*********************************************************************/
#ifndef LEVEL3_OEMCRYPTO_H_
#define LEVEL3_OEMCRYPTO_H_
#include <stddef.h>
#include <stdint.h>
#include "OEMCryptoCENC.h"
#include "level3_file_system.h"
// clang-format off
#ifdef DYNAMIC_ADAPTER
#define Level3_IsInApp _lcc00
#define Level3_Initialize _lcc01
#define Level3_Terminate _lcc02
#define Level3_InstallKeyboxOrOEMCert _lcc03
#define Level3_GetKeyData _lcc04
#define Level3_IsKeyboxOrOEMCertValid _lcc05
#define Level3_GetRandom _lcc06
#define Level3_GetDeviceID _lcc07
#define Level3_WrapKeyboxOrOEMCert _lcc08
#define Level3_OpenSession _lcc09
#define Level3_CloseSession _lcc10
#define Level3_GenerateSignature _lcc13
#define Level3_GenerateNonce _lcc14
#define Level3_RewrapDeviceRSAKey _lcc18
#define Level3_LoadDeviceRSAKey _lcc19
#define Level3_DeriveKeysFromSessionKey _lcc21
#define Level3_APIVersion _lcc22
#define Level3_Generic_Encrypt_V17 _lcc24
#define Level3_Generic_Decrypt_V17 _lcc25
#define Level3_Generic_Sign_V17 _lcc26
#define Level3_Generic_Verify_V17 _lcc27
#define Level3_SupportsUsageTable _lcc29
#define Level3_ReportUsage _lcc32
#define Level3_GetMaxNumberOfSessions _lcc37
#define Level3_GetNumberOfOpenSessions _lcc38
#define Level3_IsAntiRollbackHwPresent _lcc39
#define Level3_QueryKeyControl _lcc41
#define Level3_GetHDCPCapability _lcc44
#define Level3_LoadTestRSAKey _lcc45
#define Level3_SecurityPatchLevel _lcc46
#define Level3_GetProvisioningMethod _lcc49
#define Level3_RewrapDeviceRSAKey30 _lcc51
#define Level3_SupportedCertificates _lcc52
#define Level3_IsSRMUpdateSupported _lcc53
#define Level3_GetCurrentSRMVersion _lcc54
#define Level3_LoadSRM _lcc55
#define Level3_RemoveSRM _lcc57
#define Level3_CreateUsageTableHeader _lcc61
#define Level3_LoadUsageTableHeader _lcc62
#define Level3_CreateNewUsageEntry _lcc63
#define Level3_LoadUsageEntry _lcc64
#define Level3_UpdateUsageEntry _lcc65
#define Level3_ShrinkUsageTableHeader _lcc67
#define Level3_MoveEntry _lcc68
#define Level3_GetAnalogOutputFlags _lcc71
#define Level3_LoadTestKeybox _lcc78
#define Level3_SelectKey _lcc81
#define Level3_LoadKeys _lcc83
#define Level3_SetSandbox _lcc84
#define Level3_ResourceRatingTier _lcc85
#define Level3_SupportsDecryptHash _lcc86
#define Level3_SetDecryptHash_V18 _lcc88
#define Level3_GetHashErrorCode _lcc89
#define Level3_RefreshKeys _lcc91
#define Level3_LoadEntitledContentKeys_V16 _lcc92
#define Level3_CopyBuffer _lcc93
#define Level3_MaximumUsageTableHeaderSize _lcc94
#define Level3_GenerateDerivedKeys _lcc95
#define Level3_PrepAndSignLicenseRequest _lcc96
#define Level3_PrepAndSignRenewalRequest _lcc97
#define Level3_PrepAndSignProvisioningRequest _lcc98
#define Level3_LoadLicense_V18 _lcc99
#define Level3_LoadRenewal _lcc101
#define Level3_LoadProvisioning_V18 _lcc102
#define Level3_LoadOEMPrivateKey _lcc103
#define Level3_GetOEMPublicCertificate _lcc104
#define Level3_DecryptCENC_V17 _lcc105
#define Level3_LoadDRMPrivateKey _lcc107
#define Level3_MinorAPIVersion _lcc108
#define Level3_AllocateSecureBuffer _lcc109
#define Level3_FreeSecureBuffer _lcc110
#define Level3_CreateEntitledKeySession _lcc111
#define Level3_RemoveEntitledKeySession _lcc112
#define Level3_GetBootCertificateChain _lcc116
#define Level3_GenerateCertificateKeyPair _lcc117
#define Level3_InstallOemPrivateKey _lcc118
#define Level3_ReassociateEntitledKeySession _lcc119
#define Level3_LoadCasECMKeys _lcc120
#define Level3_LoadEntitledContentKeys _lcc121 // place holder for v17.
#define Level3_ProductionReady _lcc122
#define Level3_Idle _lcc123
#define Level3_Wake _lcc124
#define Level3_BuildInformation _lcc125
#define Level3_SecurityLevel _lcc126
#define Level3_ReuseUsageEntry _lcc127
#define Level3_GetDTCP2Capability _lcc128
#define Level3_GetWatermarkingSupport _lcc129
#define Level3_GetOEMKeyToken _lcc130
#define Level3_GetDeviceInformation _lcc131
#define Level3_SetMaxAPIVersion _lcc132
#define Level3_GetKeyHandle _lcc133
#define Level3_DecryptCENC _lcc134
#define Level3_Generic_Encrypt _lcc135
#define Level3_Generic_Decrypt _lcc136
#define Level3_Generic_Sign _lcc137
#define Level3_Generic_Verify _lcc138
#define Level3_GetSignatureHashAlgorithm _lcc139
#define Level3_EnterTestMode _lcc140
#define Level3_GetDeviceSignedCsrPayload _lcc141
#define Level3_SetDecryptHash _lcc143
#define Level3_LoadLicense _lcc144
#define Level3_LoadProvisioning _lcc145
#define Level3_LoadProvisioningCast _lcc146
#define Level3_PrepAndSignReleaseRequest _lcc147
#define Level3_GetUsageEntryInfo _lcc148
#define Level3_GetBCCType _lcc149
#define Level3_LoadRelease _lcc150
#define Level3_GetEmbeddedDrmCertificate _lcc151
#define Level3_UseSecondaryKey _lcc152
#define Level3_MarkOfflineSession _lcc153
#define Level3_SetSessionUsage _lcc155
#define Level3_GetBCCSignatureType _lcc156
#define Level3_GetPVRKey _lcc157
#define Level3_LoadPVRKey _lcc158
#define Level3_LoadLicenseData _lcc159
#define Level3_SaveLicenseData _lcc160
#else
#define Level3_Initialize _oecc01
#define Level3_Terminate _oecc02
#define Level3_InstallKeyboxOrOEMCert _oecc03
#define Level3_GetKeyData _oecc04
#define Level3_IsKeyboxOrOEMCertValid _oecc05
#define Level3_GetRandom _oecc06
#define Level3_GetDeviceID _oecc07
#define Level3_WrapKeyboxOrOEMCert _oecc08
#define Level3_OpenSession _oecc09
#define Level3_CloseSession _oecc10
#define Level3_GenerateSignature _oecc13
#define Level3_GenerateNonce _oecc14
#define Level3_RewrapDeviceRSAKey _oecc18
#define Level3_LoadDeviceRSAKey _oecc19
#define Level3_DeriveKeysFromSessionKey _oecc21
#define Level3_APIVersion _oecc22
#define Level3_Generic_Encrypt_V17 _oecc24
#define Level3_Generic_Decrypt_V17 _oecc25
#define Level3_Generic_Sign_V17 _oecc26
#define Level3_Generic_Verify_V17 _oecc27
#define Level3_SupportsUsageTable _oecc29
#define Level3_ReportUsage _oecc32
#define Level3_GenerateRSASignature _oecc36
#define Level3_GetMaxNumberOfSessions _oecc37
#define Level3_GetNumberOfOpenSessions _oecc38
#define Level3_IsAntiRollbackHwPresent _oecc39
#define Level3_QueryKeyControl _oecc41
#define Level3_GetHDCPCapability _oecc44
#define Level3_LoadTestRSAKey _oecc45
#define Level3_SecurityPatchLevel _oecc46
#define Level3_GetProvisioningMethod _oecc49
#define Level3_RewrapDeviceRSAKey30 _oecc51
#define Level3_SupportedCertificates _oecc52
#define Level3_IsSRMUpdateSupported _oecc53
#define Level3_GetCurrentSRMVersion _oecc54
#define Level3_LoadSRM _oecc55
#define Level3_RemoveSRM _oecc57
#define Level3_CreateUsageTableHeader _oecc61
#define Level3_LoadUsageTableHeader _oecc62
#define Level3_CreateNewUsageEntry _oecc63
#define Level3_LoadUsageEntry _oecc64
#define Level3_UpdateUsageEntry _oecc65
#define Level3_DeactivateUsageEntry _oecc66
#define Level3_ShrinkUsageTableHeader _oecc67
#define Level3_MoveEntry _oecc68
#define Level3_GetAnalogOutputFlags _oecc71
#define Level3_LoadTestKeybox _oecc78
#define Level3_SelectKey _oecc81
#define Level3_LoadKeys _oecc83
#define Level3_SetSandbox _oecc84
#define Level3_ResourceRatingTier _oecc85
#define Level3_SupportsDecryptHash _oecc86
#define Level3_SetDecryptHash_V18 _oecc88
#define Level3_GetHashErrorCode _oecc89
#define Level3_RefreshKeys _oecc91
#define Level3_LoadEntitledContentKeys_V16 _oecc92
#define Level3_CopyBuffer _oecc93
#define Level3_MaximumUsageTableHeaderSize _oecc94
#define Level3_GenerateDerivedKeys _oecc95
#define Level3_PrepAndSignLicenseRequest _oecc96
#define Level3_PrepAndSignRenewalRequest _oecc97
#define Level3_PrepAndSignProvisioningRequest _oecc98
#define Level3_LoadLicense_V18 _oecc99
#define Level3_LoadRenewal _oecc101
#define Level3_LoadProvisioning_V18 _oecc102
#define Level3_LoadOEMPrivateKey _oecc103
#define Level3_GetOEMPublicCertificate _oecc104
#define Level3_DecryptCENC_V17 _oecc105
#define Level3_LoadDRMPrivateKey _oecc107
#define Level3_MinorAPIVersion _oecc108
#define Level3_AllocateSecureBuffer _oecc109
#define Level3_FreeSecureBuffer _oecc110
#define Level3_CreateEntitledKeySession _oecc111
#define Level3_RemoveEntitledKeySession _oecc112
#define Level3_GetBootCertificateChain _oecc116
#define Level3_GenerateCertificateKeyPair _oecc117
#define Level3_InstallOemPrivateKey _oecc118
#define Level3_ReassociateEntitledKeySession _oecc119
#define Level3_LoadCasECMKeys _oecc120
#define Level3_LoadEntitledContentKeys _oecc121 // place holder for v17.
#define Level3_ProductionReady _oecc122
#define Level3_Idle _oecc123
#define Level3_Wake _oecc124
#define Level3_BuildInformation _oecc125
#define Level3_SecurityLevel _oecc126
#define Level3_ReuseUsageEntry _oecc127
#define Level3_GetDTCP2Capability _oecc128
#define Level3_GetWatermarkingSupport _oecc129
#define Level3_GetOEMKeyToken _oecc130
#define Level3_GetDeviceInformation _oecc131
#define Level3_SetMaxAPIVersion _oecc132
#define Level3_GetKeyHandle _oecc133
#define Level3_DecryptCENC _oecc134
#define Level3_Generic_Encrypt _oecc135
#define Level3_Generic_Decrypt _oecc136
#define Level3_Generic_Sign _oecc137
#define Level3_Generic_Verify _oecc138
#define Level3_GetSignatureHashAlgorithm _oecc139
#define Level3_EnterTestMode _oecc140
#define Level3_GetDeviceSignedCsrPayload _oecc141
#define Level3_SetDecryptHash _oecc143
#define Level3_LoadLicense _oecc144
#define Level3_LoadProvisioning _oecc145
#define Level3_LoadProvisioningCast _oecc146
#define Level3_PrepAndSignReleaseRequest _oecc147
#define Level3_GetUsageEntryInfo _oecc148
#define Level3_GetBCCType _oecc149
#define Level3_LoadRelease _oecc150
// Internal-only.
#define Level3_GetEmbeddedDrmCertificate _oecc151
#define Level3_UseSecondaryKey _oecc152
#define Level3_MarkOfflineSession _oecc153
#define Level3_SetSessionUsage _oecc155
#define Level3_GetBCCSignatureType _oecc156
#define Level3_GetPVRKey _oecc157
#define Level3_LoadPVRKey _oecc158
#define Level3_LoadLicenseData _oecc159
#define Level3_SaveLicenseData _oecc160
#endif
#define Level3_GetInitializationState _oecl3o01
// clang-format on
extern "C" {
bool Level3_IsInApp();
OEMCryptoResult Level3_Initialize(void);
OEMCryptoResult Level3_Terminate(void);
OEMCryptoResult Level3_OpenSession(OEMCrypto_SESSION* session);
OEMCryptoResult Level3_CloseSession(OEMCrypto_SESSION session);
OEMCryptoResult Level3_GenerateDerivedKeys(OEMCrypto_SESSION session,
const uint8_t* mac_key_context,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
size_t enc_key_context_length);
OEMCryptoResult Level3_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce);
OEMCryptoResult Level3_QueryKeyControl(OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length,
uint8_t* key_control_block,
size_t* key_control_block_length);
OEMCryptoResult Level3_DecryptCENC_V17(
OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples,
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult Level3_InstallKeyboxOrOEMCert(const uint8_t* rot,
size_t rotLength);
OEMCryptoResult Level3_IsKeyboxOrOEMCertValid(void);
OEMCryptoResult Level3_WrapKeyboxOrOEMCert(const uint8_t* rot, size_t rotLength,
uint8_t* wrappedRot,
size_t* wrappedRotLength,
const uint8_t* transportKey,
size_t transportKeyLength);
OEMCrypto_ProvisioningMethod Level3_GetProvisioningMethod();
OEMCryptoResult Level3_GetOEMPublicCertificate(uint8_t* public_cert,
size_t* public_cert_length);
OEMCryptoResult Level3_GetDeviceID(uint8_t* deviceID, size_t* idLength);
OEMCryptoResult Level3_GetKeyData(uint8_t* keyData, size_t* keyDataLength);
OEMCryptoResult Level3_GetRandom(OEMCrypto_SharedMemory* randomData,
size_t randomDataLength);
OEMCryptoResult Level3_LoadOEMPrivateKey(OEMCrypto_SESSION session);
OEMCryptoResult Level3_LoadDRMPrivateKey(OEMCrypto_SESSION session,
OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
OEMCryptoResult Level3_LoadProvisioning_V18(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t core_message_length, const uint8_t* signature,
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length);
OEMCryptoResult Level3_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, const uint32_t* nonce,
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
OEMCryptoResult Level3_LoadTestRSAKey();
OEMCryptoResult Level3_GenerateRSASignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length,
RSA_Padding_Scheme padding_scheme);
OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
const uint8_t* enc_session_key,
size_t enc_session_key_length,
const uint8_t* mac_key_context,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
size_t enc_key_context_length);
uint32_t Level3_APIVersion();
uint32_t Level3_MinorAPIVersion();
uint8_t Level3_SecurityPatchLevel();
OEMCrypto_Security_Level Level3_SecurityLevel();
OEMCryptoResult Level3_GetHDCPCapability(OEMCrypto_HDCP_Capability* current,
OEMCrypto_HDCP_Capability* maximum);
bool Level3_SupportsUsageTable();
bool Level3_IsAntiRollbackHwPresent();
OEMCryptoResult Level3_GetNumberOfOpenSessions(size_t* count);
OEMCryptoResult Level3_GetMaxNumberOfSessions(size_t* maximum);
uint32_t Level3_SupportedCertificates();
OEMCryptoResult Level3_Generic_Encrypt_V17(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer);
OEMCryptoResult Level3_Generic_Decrypt_V17(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer);
OEMCryptoResult Level3_Generic_Sign_V17(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length);
OEMCryptoResult Level3_Generic_Verify_V17(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult Level3_DeactivateUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
OEMCryptoResult Level3_ReportUsage(OEMCrypto_SESSION session,
const uint8_t* pst, size_t pst_length,
uint8_t* buffer, size_t* buffer_length);
OEMCryptoResult Level3_GetUsageEntryInfo(
OEMCrypto_SESSION session, OEMCrypto_UsageEntryStatus* status,
int64_t* seconds_since_license_received,
int64_t* seconds_since_first_decrypt);
bool Level3_IsSRMUpdateSupported();
OEMCryptoResult Level3_GetCurrentSRMVersion(uint16_t* version);
OEMCryptoResult Level3_LoadSRM(const uint8_t* buffer, size_t buffer_length);
OEMCryptoResult Level3_RemoveSRM();
OEMCryptoResult Level3_CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length);
OEMCryptoResult Level3_LoadUsageTableHeader(const uint8_t* buffer,
size_t buffer_length);
OEMCryptoResult Level3_CreateNewUsageEntry(OEMCrypto_SESSION session,
uint32_t* usage_entry_number);
OEMCryptoResult Level3_LoadUsageEntry(OEMCrypto_SESSION session, uint32_t index,
const uint8_t* buffer,
size_t buffer_size);
OEMCryptoResult Level3_UpdateUsageEntry(OEMCrypto_SESSION session,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
OEMCryptoResult Level3_ShrinkUsageTableHeader(uint32_t new_table_size,
uint8_t* header_buffer,
size_t* header_buffer_length);
OEMCryptoResult Level3_MoveEntry(OEMCrypto_SESSION session, uint32_t new_index);
uint32_t Level3_GetAnalogOutputFlags();
OEMCryptoResult Level3_LoadTestKeybox(const uint8_t* buffer, size_t length);
OEMCryptoResult Level3_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id, size_t key_id_length,
OEMCryptoCipherMode cipher_mode);
OEMCryptoResult Level3_LoadLicense_V18(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult Level3_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
OEMCryptoResult Level3_SetSandbox(const uint8_t* sandbox_id,
size_t sandbox_id_length);
uint32_t Level3_ResourceRatingTier();
uint32_t Level3_SupportsDecryptHash();
OEMCryptoResult Level3_SetDecryptHash(OEMCrypto_SESSION session,
uint32_t frame_number, uint32_t crc32);
OEMCryptoResult Level3_SetDecryptHash_V18(OEMCrypto_SESSION session,
uint32_t frame_number,
const uint8_t* hash,
size_t hash_length);
OEMCryptoResult Level3_GetHashErrorCode(OEMCrypto_SESSION session,
uint32_t* failed_frame_number);
OEMCryptoResult Level3_BuildInformation(char* buffer, size_t* buffer_length);
OEMCryptoResult Level3_LoadRenewal(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult Level3_LoadRelease(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult Level3_RefreshKeys(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length, size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
OEMCryptoResult Level3_LoadEntitledContentKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array);
OEMCryptoResult Level3_CopyBuffer(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
const OEMCrypto_DestBufferDesc* out_buffer_descriptor,
uint8_t subsample_flags);
OEMCryptoResult Level3_PrepAndSignProvisioningRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
OEMCryptoResult Level3_PrepAndSignLicenseRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
OEMCryptoResult Level3_PrepAndSignRenewalRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
OEMCryptoResult Level3_PrepAndSignReleaseRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_size, uint8_t* signature, size_t* signature_length);
size_t Level3_MaximumUsageTableHeaderSize();
OEMCryptoResult Level3_AllocateSecureBuffer(
OEMCrypto_SESSION session, size_t buffer_size,
OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd);
OEMCryptoResult Level3_FreeSecureBuffer(
OEMCrypto_SESSION session, OEMCrypto_DestBufferDesc* output_descriptor,
int secure_fd);
OEMCryptoResult Level3_CreateEntitledKeySession(OEMCrypto_SESSION oec_session,
OEMCrypto_SESSION* key_session);
OEMCryptoResult Level3_RemoveEntitledKeySession(OEMCrypto_SESSION key_session);
OEMCryptoResult Level3_GetBootCertificateChain(
uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature,
size_t* additional_signature_size);
OEMCryptoResult Level3_GenerateCertificateKeyPair(
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_size,
uint8_t* public_key_signature, size_t* public_key_signature_size,
uint8_t* wrapped_private_key, size_t* wrapped_private_key_size,
OEMCrypto_PrivateKeyType* key_type);
OEMCryptoResult Level3_InstallOemPrivateKey(OEMCrypto_SESSION session,
OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_private_key,
size_t wrapped_private_key_length);
OEMCryptoResult Level3_ReassociateEntitledKeySession(
OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session);
OEMCryptoResult Level3_LoadCasECMKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledContentKeyObject* even_key,
const OEMCrypto_EntitledContentKeyObject* odd_key);
OEMCryptoResult Level3_ProductionReady();
OEMCryptoResult Level3_Idle(OEMCrypto_IdleState state,
uint32_t os_specific_code);
OEMCryptoResult Level3_Wake();
OEMCryptoResult Level3_ReuseUsageEntry(OEMCrypto_SESSION session,
uint32_t usage_entry_number);
OEMCryptoResult Level3_GetDTCP2Capability(
OEMCrypto_DTCP2_Capability* capability);
OEMCrypto_WatermarkingSupport Level3_GetWatermarkingSupport();
OEMCryptoResult Level3_GetOEMKeyToken(OEMCrypto_SESSION key_session,
uint8_t* key_token,
size_t* key_token_length);
OEMCryptoResult Level3_SetSessionUsage(OEMCrypto_SESSION session,
uint32_t intent, uint32_t mode);
OEMCryptoResult Level3_GetPVRKey(OEMCrypto_SESSION session,
uint8_t* wrapped_pvr_key,
size_t* wrapped_pvr_key_length);
OEMCryptoResult Level3_LoadPVRKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_pvr_key,
size_t wrapped_pvr_key_length);
OEMCryptoResult Level3_GetDeviceInformation(uint8_t* device_info,
size_t* device_info_length);
OEMCryptoResult Level3_GetDeviceSignedCsrPayload(
const uint8_t* challenge, size_t challenge_length,
const uint8_t* encoded_device_info, size_t encoded_device_info_length,
uint8_t* signed_csr_payload, size_t* signed_csr_payload_length);
OEMCryptoResult Level3_SetMaxAPIVersion(uint32_t max_version);
OEMCryptoResult Level3_GetKeyHandle(OEMCrypto_SESSION session,
const uint8_t* content_key_id,
size_t content_key_id_length,
OEMCryptoCipherMode cipher_mode,
uint8_t* key_handle,
size_t* key_handle_length);
OEMCryptoResult Level3_DecryptCENC(
const uint8_t* key_handle, size_t key_handle_length,
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc* pattern);
OEMCryptoResult Level3_Generic_Encrypt(const uint8_t* key_handle,
size_t key_handle_length,
const OEMCrypto_SharedMemory* in_buffer,
size_t in_buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* out_buffer);
OEMCryptoResult Level3_Generic_Decrypt(const uint8_t* key_handle,
size_t key_handle_length,
const OEMCrypto_SharedMemory* in_buffer,
size_t in_buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* out_buffer);
OEMCryptoResult Level3_Generic_Sign(const uint8_t* key_handle,
size_t key_handle_length,
const OEMCrypto_SharedMemory* buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
OEMCrypto_SharedMemory* signature,
size_t* signature_length);
OEMCryptoResult Level3_Generic_Verify(const uint8_t* key_handle,
size_t key_handle_length,
const OEMCrypto_SharedMemory* buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const OEMCrypto_SharedMemory* signature,
size_t signature_length);
OEMCryptoResult Level3_GetSignatureHashAlgorithm(
OEMCrypto_SESSION session, OEMCrypto_SignatureHashAlgorithm* algorithm);
OEMCryptoResult Level3_EnterTestMode(void);
OEMCryptoResult Level3_LoadLicense(
OEMCrypto_SESSION session, const uint8_t* context, size_t context_length,
const uint8_t* derivation_key, size_t derivation_key_length,
const uint8_t* message, size_t message_length, size_t core_message_length,
const uint8_t* signature, size_t signature_length);
OEMCryptoResult Level3_LoadProvisioning(
OEMCrypto_SESSION session, const uint8_t* provision_request,
size_t provision_request_length, const uint8_t* message,
size_t message_length, size_t core_message_length, const uint8_t* signature,
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length);
OEMCryptoResult Level3_LoadProvisioningCast(
OEMCrypto_SESSION session, const uint8_t* derivation_key,
size_t derivation_key_length, const uint8_t* provision_request,
size_t provision_request_length, const uint8_t* message,
size_t message_length, size_t core_message_length, const uint8_t* signature,
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length);
OEMCryptoResult Level3_GetBCCType(OEMCrypto_BCCType* bcc_type);
OEMCryptoResult Level3_GetEmbeddedDrmCertificate(uint8_t* public_cert,
size_t* public_cert_length);
OEMCryptoResult Level3_UseSecondaryKey(OEMCrypto_SESSION session_id,
bool dual_key);
OEMCryptoResult Level3_MarkOfflineSession(OEMCrypto_SESSION session_id);
OEMCryptoResult Level3_LoadLicenseData(OEMCrypto_SESSION session,
const uint8_t* data, size_t data_length);
OEMCryptoResult Level3_SaveLicenseData(OEMCrypto_SESSION session, uint8_t* data,
size_t* data_length);
OEMCryptoResult Level3_GetBCCSignatureType(
OEMCrypto_BCCSignatureType* bcc_signature_type);
// The following are specific to Google's Level 3 implementation and are not
// required.
enum Level3InitializationState {
LEVEL3_INITIALIZATION_SUCCESS = 0,
LEVEL3_INITIALIZATION_UNKNOWN_FAILURE = 1,
LEVEL3_SEED_FAILURE = 2,
LEVEL3_SAVE_DEVICE_KEYS_FAILURE = 3,
LEVEL3_READ_DEVICE_KEYS_FAILURE = 4,
LEVEL3_VERIFY_DEVICE_KEYS_FAILURE = 5,
};
enum Level3RunningMode {
LEVEL3_MODE_HAYSTACK_ONLY = 0,
LEVEL3_MODE_RIKERS_DEFAULT = 1,
LEVEL3_MODE_RIKERS_ONLY = 2,
};
/*
* Level3_GetRunningMode
*
* Description:
* Returns the current mode the Level3 is running in. This shouldn't change
* while the processes is running.
*
* Parameters:
* N/A
*
* Threading:
* No other function calls will be made while this function is running.
*
* Version:
* This method is new in API version 19.
*/
Level3RunningMode Level3_GetRunningMode(void);
/*
* Level3_GetInitializationState
*
* Description:
* Return any warning or error condition which occurred during
* initialization. On some platforms, this value will be logged and metrics
* will be gathered on production devices. This is an optional feature, and
* OEMCrypto may always return 0, even if Level3_Initialize failed. This
* function may be called whether Level3_Initialize succeeded or not.
*
* Parameters:
* N/A
*
* Threading:
* No other function calls will be made while this function is running.
*
* Returns:
* LEVEL3_INITIALIZATION_SUCCESS - no warnings or errors during initialization
* LEVEL3_SEED_FAILURE - error in seeding the software RNG
* LEVEL3_SAVE_DEVICE_KEYS_FAILURE - failed to save device keys to file system
* LEVEL3_READ_DEVICE_KEYS_FAILURE - failed to read device keys from file
* system
* LEVEL3_VERIFY_DEVICE_KEYS_FAILURE - failed to verify decrypted device keys
*
* Version:
* This method is new in API version 14.
*/
Level3InitializationState Level3_GetInitializationState(void);
/*
* Level3_OutputErrorLogs
*
* Description:
* Call to output any errors in the Level 3 execution if the Level 3 has
* failed. This method should only be called if the Level 3 has failed in
* an unrecoverable state, and needs to be reinitialized.
*
* Parameters:
* N/A
*
* Threading:
* No other function calls will be made while this function is running.
*
* Returns:
* N/A
*
* Version:
* This method is new in API version 15.
*/
void Level3_OutputErrorLogs();
} // extern "C"
namespace wvoec3 {
// The following are interfaces needed for Google's Level 3 OEMCrypto
// specifically, which partners are expected to implement.
// Returns a stable, unique identifier for the device. This could be a
// serial number or any other character sequence representing that device.
// The parameter |len| needs to be changed to reflect the length of the
// unique identifier.
const char* getUniqueID(size_t* len);
// Returns a 64-bit unsigned integer to be used as a random seed for RNG.
// If the operation is unsuccessful, this function returns 0.
// We provide a sample implementation under the name generate_entropy_linux.cpp
// which partners should use if they can.
uint64_t generate_entropy();
// Creates and returns an OEMCrypto_Level3FileSystem implementation.
OEMCrypto_Level3FileSystem* createLevel3FileSystem();
// Deletes the pointer retrieved by the function above.
void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system);
} // namespace wvoec3
#endif // LEVEL3_OEMCRYPTO_H_

View File

@@ -1,32 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
/*********************************************************************
* level3_file_system.h
*
* File system for OEMCrypto Level3 file operations.
*********************************************************************/
#ifndef LEVEL3_FILE_SYSTEM_H_
#define LEVEL3_FILE_SYSTEM_H_
#include <stdlib.h>
#include "platform.h"
namespace wvoec3 {
class OEMCrypto_Level3FileSystem {
public:
virtual ~OEMCrypto_Level3FileSystem() {}
virtual ssize_t Read(const char *filename, void *buffer, size_t size) = 0;
virtual ssize_t Write(const char *filename, const void *buffer,
size_t size) = 0;
virtual bool Exists(const char *filename) = 0;
virtual ssize_t FileSize(const char *filename) = 0;
virtual bool Remove(const char *filename) = 0;
};
} // namespace wvoec3
#endif

View File

@@ -36,6 +36,6 @@
#define API_MAJOR_VERSION 20 #define API_MAJOR_VERSION 20
#define API_MINOR_VERSION 0 #define API_MINOR_VERSION 0
#define OPK_PATCH_VERSION 0 #define OPK_PATCH_VERSION 0
#define OPK_BUILD_ID "MAIN" #define OPK_BUILD_ID "2025-05-30"
#endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */ #endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */

View File

@@ -52,6 +52,7 @@ cflags_c += \
cppflags += \ cppflags += \
$(cflags) \ $(cflags) \
-std=c++17 \
$(CPPFLAGS) $(CPPFLAGS)
# Filter out files and directories in third_party. # Filter out files and directories in third_party.
@@ -105,7 +106,9 @@ define clang-tidy-rule
.PHONY: clang-tidy-$(1) .PHONY: clang-tidy-$(1)
clang-tidy-$(1): clang-tidy-$(1):
@$(cmd-echo-silent) ' CLANG-TIDY $(1)' @$(cmd-echo-silent) ' CLANG-TIDY $(1)'
${q}clang-tidy $(clang-tidy-flags) $(1) -- $(cflags) ${q}clang-tidy $(clang-tidy-flags) $(1) -- $(cflags) \
$(if $(filter .c,$(suffix $(1))),-std=c11 -D_POSIX_C_SOURCE=200809L) \
$(if $(filter .cpp,$(suffix $(1))),-std=c++17)
endef endef
define clang-tidy-rule-cpp define clang-tidy-rule-cpp

View File

@@ -8,7 +8,7 @@
#define OPK_CONFIG_H_ #define OPK_CONFIG_H_
#ifndef OPK_CONFIG_SECURITY_LEVEL #ifndef OPK_CONFIG_SECURITY_LEVEL
# define OPK_CONFIG_SECURITY_LEVEL OEMCrypto_Level3 # define OPK_CONFIG_SECURITY_LEVEL OEMCrypto_Level1
#endif #endif
#ifndef OPK_CONFIG_RESOURCE_RATING_TIER #ifndef OPK_CONFIG_RESOURCE_RATING_TIER

View File

@@ -106,7 +106,9 @@ define clang-tidy-rule
.PHONY: clang-tidy-$(1) .PHONY: clang-tidy-$(1)
clang-tidy-$(1): clang-tidy-$(1):
@$(cmd-echo-silent) ' CLANG-TIDY $(1)' @$(cmd-echo-silent) ' CLANG-TIDY $(1)'
${q}clang-tidy $(clang-tidy-flags) $(1) -- $(cflags) ${q}clang-tidy $(clang-tidy-flags) $(1) -- $(cflags) \
$(if $(filter .c,$(suffix $(1))),-std=c11 -D_POSIX_C_SOURCE=200809L) \
$(if $(filter .cpp,$(suffix $(1))),-std=c++17)
endef endef
define clang-tidy-rule-cpp define clang-tidy-rule-cpp

View File

@@ -28,6 +28,8 @@ clang-tidy-flags = \
clang-tidy-cflags = \ clang-tidy-cflags = \
-m$(ARCH) \ -m$(ARCH) \
$(if $(filter .c,$(suffix $(1))),-std=c11 -D_POSIX_C_SOURCE=200809L) \
$(if $(filter .cpp,$(suffix $(1))),-std=c++17) \
$(comp-cppflags-$(call oname,$(1))) $(comp-cppflags-$(call oname,$(1)))
# Define a rule template to run clang-tidy with a single source file. # Define a rule template to run clang-tidy with a single source file.

View File

@@ -26,6 +26,8 @@ clang-tidy-flags := \
clang-tidy-cflags := \ clang-tidy-cflags := \
$(MODULE_CFLAGS) \ $(MODULE_CFLAGS) \
$(if $(filter .c,$(suffix $(1))),-std=c11 -D_POSIX_C_SOURCE=200809L) \
$(if $(filter .cpp,$(suffix $(1))),-std=c++17) \
$(addprefix -D,$(MODULE_DEFINES) TRUSTY_USERSPACE) \ $(addprefix -D,$(MODULE_DEFINES) TRUSTY_USERSPACE) \
$(addprefix -I,\ $(addprefix -I,\
$(MODULE_INCLUDES) \ $(MODULE_INCLUDES) \
@@ -37,7 +39,7 @@ clang-tidy-cflags := \
define clang-tidy-rule define clang-tidy-rule
.PHONY: clang-tidy-$(1) .PHONY: clang-tidy-$(1)
clang-tidy-$(1): clang-tidy-flags := $(clang-tidy-flags) clang-tidy-$(1): clang-tidy-flags := $(clang-tidy-flags)
clang-tidy-$(1): clang-tidy-cflags := $(clang-tidy-cflags) clang-tidy-$(1): clang-tidy-cflags := $(call clang-tidy-cflags,$(1))
clang-tidy-$(1): clang-tidy-$(1):
@echo running clang-tidy: $(1) @echo running clang-tidy: $(1)
$(NOECHO)clang-tidy $(clang-tidy-flags) $(1) -- $(clang-tidy-cflags) $(NOECHO)clang-tidy $(clang-tidy-flags) $(1) -- $(clang-tidy-cflags)

View File

@@ -1,5 +0,0 @@
# Android Format Style
edition = "2021"
use_small_heuristics = "Max"
newline_style = "Unix"

View File

@@ -0,0 +1,75 @@
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
WV_TA_TOP_LEVEL := $(LOCAL_DIR)/../../../../
SHARED_DIR := $(WV_TA_TOP_LEVEL)/shared
IMPL_DIR := $(WV_TA_TOP_LEVEL)/interface_impls
CDM_DIR := trusty/vendor/widevine/cdm
APP_DIR := $(CDM_DIR)/oemcrypto/opk/oemcrypto_ta
SERIALIZATION_DIR := $(CDM_DIR)/oemcrypto/opk/serialization
ODK_DIR := $(CDM_DIR)/oemcrypto/odk
ifndef WIDEVINE_PROVISION_METHOD
$(error WIDEVINE_PROVISION_METHOD is not set. \
Please set it in the [target_name]-inc.mk file of your project)
endif
MODULE_DEFINES += \
WIDEVINE_PROVISION_METHOD=$(WIDEVINE_PROVISION_METHOD) \
# Default value if WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE is not set by *-inc.mk.
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE ?= 0
MODULE_DEFINES += \
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE=$(WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE)
MODULE_DEFINES += \
OPK_CONFIG_SOC_VENDOR_NAME=Google \
OPK_CONFIG_SOC_MODEL_NAME=$(PLATFORM) \
OPK_CONFIG_TEE_OS_NAME=Trusty \
OPK_CONFIG_TEE_OS_VERSION=0.0 \
OPK_CONFIG_DEVICE_FORM_FACTOR=phone+tablet \
OPK_CONFIG_IMPLEMENTER_NAME=Widevine \
WTPI_BUILD_INFO=TRUSTY \
# Widevine vendor code assumes trusty defines __linux__. We need to get this
# fixed upstream. TODO(b/232255239)
MODULE_CFLAGS += -D__linux__
MODULE_SRCS += \
$(APP_DIR)/wtpi_reference/wtpi_decrypt_sample.c \
MODULE_INCLUDES += \
$(LOCAL_DIR)/include \
$(SHARED_DIR)/include \
MODULE_INCLUDES += \
$(CDM_DIR)/oemcrypto/include \
$(ODK_DIR)/include \
$(CDM_DIR)/oemcrypto/odk/src \
$(IMPL_DIR) \
$(APP_DIR) \
$(WV_TA_TOP_LEVEL)/include \
$(APP_DIR)/wtpi \
$(APP_DIR)/wtpi_reference \
MODULE_LIBRARY_DEPS += \
trusty/user/base/lib/libc-trusty \
include make/library.mk

View File

@@ -0,0 +1,71 @@
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
TOP_LEVEL := $(LOCAL_DIR)/../../../../
SHARED_DIR := $(TOP_LEVEL)/shared
IMPL_DIR := $(TOP_LEVEL)/interface_impls
CDM_DIR := trusty/vendor/widevine/cdm
APP_DIR := $(CDM_DIR)/oemcrypto/opk/oemcrypto_ta
SERIALIZATION_DIR := $(CDM_DIR)/oemcrypto/opk/serialization
ODK_DIR := $(CDM_DIR)/oemcrypto/odk
ifndef WIDEVINE_PROVISION_METHOD
$(error WIDEVINE_PROVISION_METHOD is not set. \
Please set it in the [target_name]-inc.mk file of your project)
endif
MODULE_DEFINES += \
WIDEVINE_PROVISION_METHOD=$(WIDEVINE_PROVISION_METHOD) \
# Default value if WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE is not set by *-inc.mk.
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE ?= 0
MODULE_DEFINES += \
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE=$(WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE)
MODULE_DEFINES += \
OPK_CONFIG_SOC_VENDOR_NAME=Google \
OPK_CONFIG_SOC_MODEL_NAME=$(PLATFORM) \
OPK_CONFIG_TEE_OS_NAME=Trusty \
OPK_CONFIG_TEE_OS_VERSION=0.0 \
OPK_CONFIG_DEVICE_FORM_FACTOR=phone+tablet \
OPK_CONFIG_IMPLEMENTER_NAME=Widevine \
WTPI_BUILD_INFO=TRUSTY \
MODULE_SRCS += \
$(IMPL_DIR)/derive_key.c \
MODULE_INCLUDES += \
$(LOCAL_DIR)/include \
$(SHARED_DIR)/include \
MODULE_INCLUDES += \
$(CDM_DIR)/oemcrypto/include \
$(ODK_DIR)/include \
$(CDM_DIR)/oemcrypto/odk/src \
$(IMPL_DIR) \
$(APP_DIR) \
$(TOP_LEVEL)/include \
$(APP_DIR)/wtpi \
$(APP_DIR)/wtpi_reference \
MODULE_LIBRARY_DEPS += \
trusty/user/base/lib/hwkey \
include make/library.mk

View File

@@ -0,0 +1,95 @@
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
WV_TA_TOP_LEVEL := $(LOCAL_DIR)/../../../../
SHARED_DIR := $(WV_TA_TOP_LEVEL)/shared
IMPL_DIR := $(WV_TA_TOP_LEVEL)/interface_impls
CDM_DIR := trusty/vendor/widevine/cdm
APP_DIR := $(CDM_DIR)/oemcrypto/opk/oemcrypto_ta
SERIALIZATION_DIR := $(CDM_DIR)/oemcrypto/opk/serialization
ODK_DIR := $(CDM_DIR)/oemcrypto/odk
ifndef WIDEVINE_PROVISION_METHOD
$(error WIDEVINE_PROVISION_METHOD is not set. \
Please set it in the [target_name]-inc.mk file of your project)
endif
# oemcrypto.h will declare constants as C++-style statics.
# C sources do not like this, so change the behavior.
MODULE_CFLAGS += -DNO_OEMCRYPTO_VARIABLE_DEFINITIONS
# Ensure API entry points are renamed in a predictable manner.
# TODO: can we remove this?
MODULE_CFLAGS += -DOEMCRYPTO_TA_TEST_ONLY
# TODO: remove when the prototypes in oemcrypto.h are strict.
MODULE_CFLAGS += -Wno-strict-prototypes
# TODO: remove when unused variable in dump_ssl_error() is fixed
MODULE_CFLAGS += -Wno-error=unused-but-set-variable
MODULE_DEFINES += \
WIDEVINE_PROVISION_METHOD=$(WIDEVINE_PROVISION_METHOD) \
# Default value if WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE is not set by *-inc.mk.
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE ?= 0
MODULE_DEFINES += \
WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE=$(WIDEVINE_PROVISION_BCC_SIGNATURE_TYPE)
MODULE_DEFINES += \
OPK_CONFIG_SOC_VENDOR_NAME=Google \
OPK_CONFIG_SOC_MODEL_NAME=$(PLATFORM) \
OPK_CONFIG_TEE_OS_NAME=Trusty \
OPK_CONFIG_TEE_OS_VERSION=0.0 \
OPK_CONFIG_DEVICE_FORM_FACTOR=phone+tablet \
OPK_CONFIG_IMPLEMENTER_NAME=Widevine \
WTPI_BUILD_INFO=TRUSTY \
# Widevine vendor code assumes trusty defines __linux__. We need to get this
# fixed upstream. TODO(b/232255239)
MODULE_CFLAGS += -D__linux__
# The base Trusty app.
MODULE_SRCS += \
$(IMPL_DIR)/wtpi_persistent_storage_layer1.c \
$(IMPL_DIR)/wtpi_initialize_terminate.c \
$(IMPL_DIR)/wtpi_root_of_trust_layer2.c \
$(WV_TA_TOP_LEVEL)/tee_context.c \
MODULE_INCLUDES += \
$(WV_TA_TOP_LEVEL)/include \
$(CDM_DIR)/oemcrypto/include \
$(ODK_DIR)/include \
$(SERIALIZATION_DIR)/common \
$(SERIALIZATION_DIR)/common/include \
$(CDM_DIR)/oemcrypto/odk/src \
$(SERIALIZATION_DIR)/os_interfaces \
$(IMPL_DIR) \
MODULE_INCLUDES += \
$(APP_DIR) \
$(APP_DIR)/wtpi \
$(APP_DIR)/wtpi_reference \
MODULE_LIBRARY_DEPS += \
trusty/user/base/lib/libc-trusty \
trusty/user/base/lib/storage \
include make/library.mk

View File

@@ -450,11 +450,3 @@ OEMCryptoResult _oecc157(OEMCrypto_SESSION session, uint8_t* wrapped_pvr_key,
OEMCryptoResult _oecc158(OEMCrypto_SESSION session, OEMCryptoResult _oecc158(OEMCrypto_SESSION session,
const uint8_t* wrapped_pvr_key, const uint8_t* wrapped_pvr_key,
size_t wrapped_pvr_key_length); size_t wrapped_pvr_key_length);
// OEMCrypto_LoadLicenseData defined in v20.0
OEMCryptoResult _oecc159(OEMCrypto_SESSION session, const uint8_t* data,
size_t data_length);
// OEMCrypto_SaveLicenseData defined in v20.0
OEMCryptoResult _oecc160(OEMCrypto_SESSION session, uint8_t* data,
size_t* data_length);

View File

@@ -693,7 +693,7 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
// Whether this was built with FACTORY_MODE_ONLY defined // Whether this was built with FACTORY_MODE_ONLY defined
{"is_factory_mode", JSMN_PRIMITIVE}, {"is_factory_mode", JSMN_PRIMITIVE},
// ... provide information about liboemcrypto.so // ... provide information about liboemcrypto.so
// Special case, see kOptionalReeFields for details. // Special case, see kReeOptionalFields for details.
{kSpecialCaseReeKey, JSMN_OBJECT}, {kSpecialCaseReeKey, JSMN_OBJECT},
// Technically required, but several implementations // Technically required, but several implementations
// do not implement this fields. // do not implement this fields.
@@ -791,7 +791,7 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
// The optional field "ree", if present, must follow the required // The optional field "ree", if present, must follow the required
// format. // format.
const std::map<std::string, jsmntype_t> kReeRequiredFields = { const std::map<std::string, jsmntype_t> kReeOptionalFields = {
// liboemcrypto.so version in string format eg "2.15.0+tag" // liboemcrypto.so version in string format eg "2.15.0+tag"
{"liboemcrypto_ver", JSMN_STRING}, {"liboemcrypto_ver", JSMN_STRING},
// git hash of code that compiled liboemcrypto.so // git hash of code that compiled liboemcrypto.so
@@ -799,7 +799,6 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
// ISO 8601 timestamp for when liboemcrypto.so was built // ISO 8601 timestamp for when liboemcrypto.so was built
{"build_timestamp", JSMN_STRING}}; {"build_timestamp", JSMN_STRING}};
found_required_fields.clear();
for (int32_t i = 0; (i + 1) < static_cast<int32_t>(ree_tokens.size()); for (int32_t i = 0; (i + 1) < static_cast<int32_t>(ree_tokens.size());
i += 2) { i += 2) {
const jsmntok_t& key_token = ree_tokens[i]; const jsmntok_t& key_token = ree_tokens[i];
@@ -809,11 +808,10 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
const std::string key = const std::string key =
build_info.substr(key_token.start, key_token.end - key_token.start); build_info.substr(key_token.start, key_token.end - key_token.start);
if (kReeRequiredFields.find(key) != kReeRequiredFields.end()) { if (kReeOptionalFields.find(key) != kReeOptionalFields.end()) {
ASSERT_EQ(value_token.type, kReeRequiredFields.at(key)) ASSERT_EQ(value_token.type, kReeOptionalFields.at(key))
<< "Unexpected optional REE field type: ree_field = " << key << "Unexpected optional REE field type: ree_field = " << key
<< ", build_info = " << build_info; << ", build_info = " << build_info;
found_required_fields.insert(key);
RecordWvProperty(kReeBuildInfoRecordPrefix + key, RecordWvProperty(kReeBuildInfoRecordPrefix + key,
build_info.substr(value_token.start, build_info.substr(value_token.start,
value_token.end - value_token.start)); value_token.end - value_token.start));
@@ -823,25 +821,6 @@ TEST_F(OEMCryptoClientTest, CheckJsonBuildInformationAPI18) {
i += JsmnAncestorCount(ree_tokens, i + 1); i += JsmnAncestorCount(ree_tokens, i + 1);
} }
// Step 4b: Ensure all required fields of the "ree" object were found.
if (found_required_fields.size() == kReeRequiredFields.size()) return;
// Generate a list of all the missing REE fields.
std::string missing_ree_fields;
for (const auto& required_field : kReeRequiredFields) {
if (found_required_fields.find(required_field.first) !=
found_required_fields.end())
continue;
if (!missing_ree_fields.empty()) {
missing_ree_fields.append(", ");
}
missing_ree_fields.push_back('"');
missing_ree_fields.append(required_field.first);
missing_ree_fields.push_back('"');
}
FAIL() << "REE info JSON object does not contain all required keys; "
<< "missing_ree_fields = [" << missing_ree_fields
<< "], build_info = " << build_info;
} }
TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {

View File

@@ -39,6 +39,7 @@
'sources': [ 'sources': [
'<(wvcrypto_dir)/src/cmac.cpp', '<(wvcrypto_dir)/src/cmac.cpp',
'<(wvcrypto_dir)/src/cose_utils.cpp', '<(wvcrypto_dir)/src/cose_utils.cpp',
'<(wvcrypto_dir)/src/dice_cert_utils.cpp',
'<(wvcrypto_dir)/src/hmac.cpp', '<(wvcrypto_dir)/src/hmac.cpp',
'<(wvcrypto_dir)/src/oemcrypto_cose_key.cpp', '<(wvcrypto_dir)/src/oemcrypto_cose_key.cpp',
'<(wvcrypto_dir)/src/oemcrypto_drm_key.cpp', '<(wvcrypto_dir)/src/oemcrypto_drm_key.cpp',

View File

@@ -0,0 +1,346 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#ifndef WVOEC_UTIL_DICE_CERT_UTILS_H_
#define WVOEC_UTIL_DICE_CERT_UTILS_H_
#include <inttypes.h>
#include <optional>
#include <string>
#include <vector>
#include "wv_class_utils.h"
namespace wvoec {
namespace util {
// ==== DICE UDS/CDI CBOR Certificates ====
// DICE UDS/CDI CBOR Certificates are based on CBOR Web Token (CWT).
// For CWT, RFC 8392
// Token issuer ("iss") => tstr
// Identifies the issuer of the CWT/Cert.
// For DICE, this is a lower-hex encoded UDS_ID/CDI_ID.
constexpr int64_t kDiceIssuerLabel = 1;
// Token subject ("sub") => tstr
// Identifies the subject of the CWT/Cert.
// For DICE, this is a lower-hex encoded CDI_ID.
constexpr int64_t kDiceSubjectLabel = 2;
// Token audience ("aud") => tstr
// Identifies the audience of the CWT/Cert.
// Not used for DICE.
constexpr int64_t kDiceAudienceLabel = 3;
// Expiration time ("exp") => int / float (CWT NumericDate)
// Time of expiry of certificate.
// For DICE, this is only available if a reliable time source is available.
constexpr int64_t kDiceExpirationTimeLabel = 4;
// Not before time ("nbf") => int / float (CWT NumericDate)
// Time after which the certificate may be used.
// For DICE, this is only available if a reliable time source is available.
constexpr int64_t kDiceNotBeforeLabel = 5;
// Issued at time ("iat") => int / float (CWT NumericDate)
// Time which the certificate was issued.
// For DICE, this is only available if a reliable time source is available.
constexpr int64_t kDiceIssuedAtLabel = 6;
// CWT ID ("cti") => bstr
// Used to identify the token claim.
// Not used for DICE.
constexpr int64_t kDiceCwtIdLabel = 7;
// From Open Profile for DICE
// Code hash ("codeHash") => bstr
// Exact code input value used to compute CDI values.
// For Android/Widevine DICE, this is unpadded
// For Open DICE, this is always 64 bytes.
constexpr int64_t kDiceCodeHashLabel = -4670545;
// Code Descriptor ("codeDescriptor") => bstr
// Contains additional information about the code input value.
// Exact format is implementation specific.
constexpr int64_t kDiceCodeDescriptorLabel = -4670546;
// Config Input Hash ("configurationHash") => bstr
// If the configation input is hashed, this is that hash; if
// config input is not hashed, than this field is omitted.
// For Android/Widevine DICE, this is unpadded and required.
// For Open DICE, this is always 64 bytes.
constexpr int64_t kDiceConfigHashLabel = -4670547;
// Configuration Descriptor ("configurationDescriptor") => bstr
// If configuration input is hashed, this field contains the original
// configuration data that was hash, if not hash, this field contains
// the exact 64-byte configuration input value used to compute
// the CDI values.
// For Android/Widevine DICE, this is a CBOR encoded map.
constexpr int64_t kDiceConfigDescriptorLabel = -4670548;
// Authority Input Hash ("authorityHash") => bstr
// Exact authority input used to compute the CDI values.
// For Android/Widevine DICE, this is unpadded and required.
// For Open DICE, this is always 64 bytes.
constexpr int64_t kDiceAuthorityHashLabel = -4670549;
// Authority Descriptor ("authorityDescriptor") => bstr
// If configuration input is hashed, this field contains the original
// configuration data that was hash, if not hash, this field is
// omitted.
constexpr int64_t kDiceAuthorityDescriptorLabel = -4670550;
// DICE Mode ("mode") => bstr .size 1
// DICE config mode. Encoded as a bstr of size 1; which represents
// one of the enumerated DICE modes (0-3).
constexpr int64_t kDiceModeLabel = -4670551;
// Subject Public Key ("subjectPublicKey") => bstr
// Contains the public key of this certificate's subject. Should
// be a CBOR encoded COSE_Key (public key only).
constexpr int64_t kDiceSubjectPublicKeyLabel = -4670552;
// Key usage ("keyUsage") => bstr
// Contains the key usage of the subject public key. Contains
// a little-endian encoded integer representing an X.509 KeyUsage
// bit field. Should only be set to "keyCertSign" bit.
constexpr int64_t kDiceKeyUsageLabel = -4670553;
// Profile Name ("profileName") => tstr
// This is the name of the DICE profile that defines the contents
// of this certificate.
// For Android DICE, required and format "android.<Android Version>"
// For Widevine DICE, required and format "widevine.<OEMCrypto major version>"
// For Open DICE, optional.
constexpr int64_t kDiceProfileNameLabel = -4670554;
constexpr uint8_t kDiceModeNotConfigured = 0;
constexpr uint8_t kDiceModeNormal = 1;
constexpr uint8_t kDiceModeDebug = 2;
constexpr uint8_t kDiceModeRecovery = 3;
// Decoded form of the X.509 key usage bits.
// DICE only recognizes "keyCertSign".
// See RFC 5280 section 4.2.1.3 for details of defined bits.
using DiceKeyUsageFlags = uint16_t;
// Subject public key is used for certificate signature
// verification.
constexpr DiceKeyUsageFlags kKeyCertSignBit = (1 << 5);
class DiceCertData {
public:
// Decodes the bstr format "mode" field into a raw DICE mode byte.
// Returns:
// - Raw DICE mode byte on success
// - nullopt if |mode_bstr| does not represent a valid bstr encoded
// mode.
static std::optional<uint8_t> DecodeModeBytes(
const std::vector<uint8_t>& mode_bstr);
// Encodes the raw DICE mode into the bstr format of "mode".
static std::vector<uint8_t> EncodeModeBytes(uint8_t mode) {
return std::vector<uint8_t>(1, mode);
}
// Decodes the bstr format "keyUsage" field into a raw X.509
// key usage bit field.
// Returns:
// - Raw X.506 bit field.
// - nullopt if |key_usage_bstr| cannot represent a valid bstr
// encoded key usage bit field.
static std::optional<DiceKeyUsageFlags> DecodeKeyUsageBytes(
const std::vector<uint8_t>& key_usage_bstr);
static std::vector<uint8_t> EncodeKeyUsageBytes(
const DiceKeyUsageFlags& key_usage);
DiceCertData() = default;
WVCDM_DEFAULT_COPY_AND_MOVE(DiceCertData);
// == CWT Fields ==
const std::optional<std::string>& issuer() const { return issuer_; }
bool has_issuer() const { return issuer_.has_value(); }
void set_issuer(const std::string& issuer) { issuer_ = issuer; }
void clear_issuer() { issuer_.reset(); }
const std::optional<std::string>& subject() const { return subject_; }
bool has_subject() const { return subject_.has_value(); }
void set_subject(const std::string& subject) { subject_ = subject; }
void clear_subject() { subject_.reset(); }
const std::optional<std::string>& audience() const { return audience_; }
bool has_audience() const { return audience_.has_value(); }
void set_audience(const std::string& audience) { audience_ = audience; }
void clear_audience() { audience_.reset(); }
const std::optional<int64_t>& expiration_time() const {
return expiration_time_;
}
bool has_expiration_time() const { return expiration_time_.has_value(); }
void set_expiration_time(int64_t time) { expiration_time_ = time; }
void clear_expiration_time() { expiration_time_.reset(); }
const std::optional<int64_t>& not_before_time() const {
return not_before_time_;
}
bool has_not_before_time() const { return not_before_time_.has_value(); }
void set_not_before_time(int64_t time) { not_before_time_ = time; }
void clear_not_before_time() { not_before_time_.reset(); }
const std::optional<int64_t>& issued_time() const { return issued_time_; }
bool has_issued_time() const { return issued_time_.has_value(); }
void set_issued_time(int64_t time) { issued_time_ = time; }
void clear_issued_time() { issued_time_.reset(); }
const std::optional<std::vector<uint8_t>>& cwt_id() const { return cwt_id_; }
bool has_cwt_id() const { return cwt_id_.has_value(); }
void set_cwt_id(const std::vector<uint8_t>& id) { cwt_id_ = id; }
void clear_cwt_id() { cwt_id_.reset(); }
// == DICE CDI Fields ==
const std::optional<std::vector<uint8_t>>& code_hash() const {
return code_hash_;
}
bool has_code_hash() const { return code_hash_.has_value(); }
void set_code_hash(const std::vector<uint8_t>& hash) { code_hash_ = hash; }
void clear_code_hash() { code_hash_.reset(); }
const std::optional<std::vector<uint8_t>>& code_descriptor() const {
return code_descriptor_;
}
bool has_code_descriptor() const { return code_descriptor_.has_value(); }
void set_code_descriptor(const std::vector<uint8_t>& descriptor) {
code_descriptor_ = descriptor;
}
void clear_code_descriptor() { code_descriptor_.reset(); }
const std::optional<std::vector<uint8_t>>& config_hash() const {
return config_hash_;
}
bool has_config_hash() const { return config_hash_.has_value(); }
void set_config_hash(const std::vector<uint8_t>& hash) {
config_hash_ = hash;
}
void clear_config_hash() { config_hash_.reset(); }
const std::optional<std::vector<uint8_t>>& config_descriptor() const {
return config_descriptor_;
}
bool has_config_descriptor() const { return config_descriptor_.has_value(); }
void set_config_descriptor(const std::vector<uint8_t>& descriptor) {
config_descriptor_ = descriptor;
}
void clear_config_descriptor() { config_descriptor_.reset(); }
const std::optional<std::vector<uint8_t>>& authority_hash() const {
return authority_hash_;
}
bool has_authority_hash() const { return authority_hash_.has_value(); }
void set_authority_hash(const std::vector<uint8_t>& hash) {
authority_hash_ = hash;
}
void clear_authority_hash() { authority_hash_.reset(); }
const std::optional<std::vector<uint8_t>>& authority_descriptor() const {
return authority_descriptor_;
}
bool has_authority_descriptor() const {
return authority_descriptor_.has_value();
}
void set_authority_descriptor(const std::vector<uint8_t>& descriptor) {
authority_descriptor_ = descriptor;
}
void clear_authority_descriptor() { authority_descriptor_.reset(); }
const std::optional<uint8_t>& mode() const { return mode_; }
bool has_mode() const { return mode_.has_value(); }
void set_mode(uint8_t mode) { mode_ = mode; }
void clear_mode() { mode_.reset(); }
const std::optional<std::vector<uint8_t>>& subject_public_key() const {
return subject_public_key_;
}
bool has_subject_public_key() const {
return subject_public_key_.has_value();
}
void set_subject_public_key(const std::vector<uint8_t>& key) {
subject_public_key_ = key;
}
void clear_subject_public_key() { subject_public_key_.reset(); }
const std::optional<DiceKeyUsageFlags>& key_usage() const {
return key_usage_;
}
bool has_key_usage() const { return key_usage_.has_value(); }
void set_key_usage(DiceKeyUsageFlags key_usage) { key_usage_ = key_usage; }
void clear_key_usage() { key_usage_.reset(); }
const std::optional<std::string>& profile_name() const {
return profile_name_;
}
bool has_profile_name() const { return profile_name_.has_value(); }
void set_profile_name(const std::string& name) { profile_name_ = name; }
void clear_profile_name() { profile_name_.reset(); }
void Clear() {
// CWT fields.
clear_issuer();
clear_subject();
clear_audience();
clear_expiration_time();
clear_not_before_time();
clear_issued_time();
clear_cwt_id();
// DICE CDI fields.
clear_code_hash();
clear_code_descriptor();
clear_config_hash();
clear_config_descriptor();
clear_authority_hash();
clear_authority_descriptor();
clear_mode();
clear_subject_public_key();
clear_key_usage();
clear_profile_name();
}
// Parses the CBOR CWT/DICE Cert.
// Input should be a CBOR serializes map.
// Limited validation:
// - All defined fields are treated as optional (even
// if standard says they are required)
// - Defined fields, if present, must be the expected type.
// - "mode", if present must be a bstr containing a single byte,
// raw value will be stored, even if out of range of defined
// modes.
// - "keyUsage", if present, must decode as a little-endian
// integer that can fit into a uint16_t
// - Additional fields are dropped
// Returns true if successfully parsed, false otherwise.
bool ParseCbor(const uint8_t* buffer, size_t buffer_size);
bool ParseCbor(const std::vector<uint8_t>& buffer) {
return ParseCbor(buffer.data(), buffer.size());
}
// Serializes the DICE Cert.
// - Any set field will be encoded as the type defined.
// - "mode" will be encoded as a bstr containing a single byte
// - "keyUsage" will be encoded as a little-endian integer,
// using the 1 or 2 bytes (0 is encoded as 1 byte).
std::vector<uint8_t> SerializeCbor() const;
private:
// CWT fields.
std::optional<std::string> issuer_ = std::nullopt;
std::optional<std::string> subject_ = std::nullopt;
std::optional<std::string> audience_ = std::nullopt;
std::optional<int64_t> expiration_time_ = std::nullopt;
std::optional<int64_t> not_before_time_ = std::nullopt;
std::optional<int64_t> issued_time_ = std::nullopt;
std::optional<std::vector<uint8_t>> cwt_id_ = std::nullopt;
// Open DICE fields.
std::optional<std::vector<uint8_t>> code_hash_ = std::nullopt;
std::optional<std::vector<uint8_t>> code_descriptor_ = std::nullopt;
std::optional<std::vector<uint8_t>> config_hash_ = std::nullopt;
std::optional<std::vector<uint8_t>> config_descriptor_ = std::nullopt;
std::optional<std::vector<uint8_t>> authority_hash_ = std::nullopt;
std::optional<std::vector<uint8_t>> authority_descriptor_ = std::nullopt;
std::optional<uint8_t> mode_ = std::nullopt;
std::optional<std::vector<uint8_t>> subject_public_key_ = std::nullopt;
std::optional<DiceKeyUsageFlags> key_usage_ = std::nullopt;
std::optional<std::string> profile_name_ = std::nullopt;
}; // class DiceCertData
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_DICE_CERT_UTILS_H_

View File

@@ -19,6 +19,7 @@
'<(oemcrypto_dir)/util/test/cmac_unittest.cpp', '<(oemcrypto_dir)/util/test/cmac_unittest.cpp',
'<(oemcrypto_dir)/util/test/cose_utils_unittest.cpp', '<(oemcrypto_dir)/util/test/cose_utils_unittest.cpp',
'<(oemcrypto_dir)/util/test/device_info_validator_unittest.cpp', '<(oemcrypto_dir)/util/test/device_info_validator_unittest.cpp',
'<(oemcrypto_dir)/util/test/dice_cert_utils_unittest.cpp',
'<(oemcrypto_dir)/util/test/hmac_unittest.cpp', '<(oemcrypto_dir)/util/test/hmac_unittest.cpp',
'<(oemcrypto_dir)/util/test/oem_cert_test.cpp', '<(oemcrypto_dir)/util/test/oem_cert_test.cpp',
'<(oemcrypto_dir)/util/test/oemcrypto_cose_key_unittest.cpp', '<(oemcrypto_dir)/util/test/oemcrypto_cose_key_unittest.cpp',

View File

@@ -0,0 +1,421 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "dice_cert_utils.h"
#include <inttypes.h>
#include <optional>
#include <string>
#include <vector>
#include <cppbor.h>
#include <cppbor_parse.h>
#include "log.h"
#include "string_conversions.h"
namespace wvoec {
namespace util {
namespace {
// == cppbor Utils ==
bool IsItemMapType(const cppbor::Item& item) {
return item.type() == cppbor::MAP;
}
bool IsItemIntType(const cppbor::Item& item) {
return item.type() == cppbor::UINT || item.type() == cppbor::NINT;
}
bool IsItemBstrType(const cppbor::Item& item) {
return item.type() == cppbor::BSTR;
}
bool IsItemTstrType(const cppbor::Item& item) {
return item.type() == cppbor::TSTR;
}
const char* SimpleItemTypeToString(const cppbor::Simple& item) {
switch (item.simpleType()) {
case cppbor::BOOLEAN:
return "bool";
case cppbor::NULL_T:
return "nil";
// case cppbor::FLOAT:
// case cppbor::DOUBLE:
// return "float";
// CDM's version of cppbor is out of date and does not support
// the simple types FLOAT and DOUBLE; however, Android's version does.
// Adding "default" to prevent compilation errors if/when CDM's cppbor
// is updated.
default:
break;
}
return "unknownSimple";
}
const char* ItemTypeToString(const cppbor::Item& item) {
switch (item.type()) {
case cppbor::UINT:
case cppbor::NINT:
return "int";
case cppbor::BSTR:
return "bstr";
case cppbor::TSTR:
return "tstr";
case cppbor::ARRAY:
return "array";
case cppbor::MAP:
return "map";
case cppbor::SEMANTIC:
// Could be other things, but most common is tag.
return "tag";
case cppbor::SIMPLE:
return SimpleItemTypeToString(*item.asSimple());
}
return "unknown";
}
} // namespace
// static
std::optional<uint8_t> DiceCertData::DecodeModeBytes(
const std::vector<uint8_t>& mode_bstr) {
if (mode_bstr.size() != 1) {
LOGE("Invalid length of mode bstr: length = %zu, expected = 1",
mode_bstr.size());
return std::nullopt;
}
return mode_bstr.front();
}
// static
std::optional<DiceKeyUsageFlags> DiceCertData::DecodeKeyUsageBytes(
const std::vector<uint8_t>& key_usage_bstr) {
if (key_usage_bstr.empty() || key_usage_bstr.size() > 2) {
LOGE("Invalid length of key usage bstr: length = %zu, min = 1, max = 2",
key_usage_bstr.size());
return std::nullopt;
}
// Decode as little-endian (reverse iterator).
DiceKeyUsageFlags key_usage = 0;
for (auto it = key_usage_bstr.rbegin(); it != key_usage_bstr.rend(); ++it) {
key_usage = (key_usage << 8) | static_cast<DiceKeyUsageFlags>(*it);
}
return key_usage;
}
// static
std::vector<uint8_t> DiceCertData::EncodeKeyUsageBytes(
const DiceKeyUsageFlags& key_usage) {
if (key_usage == 0) {
// Special case, always encode at least 1 byte.
return std::vector<uint8_t>(1, 0);
}
std::vector<uint8_t> key_usage_bstr;
key_usage_bstr.reserve(sizeof(DiceKeyUsageFlags));
DiceKeyUsageFlags temp = key_usage;
// Encode as little-endian.
// Note: Must be encoded using the fewest number of bytes
// necessary with a minimum of 1.
while (temp > 0) {
key_usage_bstr.push_back(static_cast<uint8_t>(temp & 0xff));
temp >>= 8;
}
return key_usage_bstr;
}
bool DiceCertData::ParseCbor(const uint8_t* buffer, size_t buffer_size) {
if (buffer == nullptr) {
LOGE("Input |buffer| is null");
return false;
}
if (buffer_size == 0) {
LOGE("Input |buffer| is empty");
return false;
}
Clear(); // Clear existing data.
const auto [cert_item, end_pos, error_message] =
cppbor::parse(buffer, buffer_size);
if (!cert_item) {
LOGE("Failed to parse CBOR DICE Cert: %s", error_message.c_str());
return false;
}
if (!IsItemMapType(*cert_item)) {
LOGE("CBOR item is not a map: type = %s", ItemTypeToString(*cert_item));
return false;
}
const cppbor::Map* cert_map = cert_item->asMap();
// Issuer "iss" => tstr
const auto& issuer_item = cert_map->get(kDiceIssuerLabel);
if (issuer_item) {
if (!IsItemTstrType(*issuer_item)) {
LOGE("Expected 'iss' field to be a tstr: type = %s",
ItemTypeToString(*issuer_item));
return false;
}
issuer_ = issuer_item->asTstr()->value();
}
// Subject "sub" => tstr
const auto& subject_item = cert_map->get(kDiceSubjectLabel);
if (subject_item) {
if (!IsItemTstrType(*subject_item)) {
LOGE("Expected 'sub' field to be a tstr: type = %s",
ItemTypeToString(*subject_item));
return false;
}
subject_ = subject_item->asTstr()->value();
}
// Audience "aud" => tstr
const auto& audience_item = cert_map->get(kDiceAudienceLabel);
if (audience_item) {
if (!IsItemTstrType(*audience_item)) {
LOGE("Expected 'aud' field to be a tstr: type = %s",
ItemTypeToString(*audience_item));
return false;
}
audience_ = audience_item->asTstr()->value();
}
// Note: Version of cppbor does not support float/double.
// Expiration time "exp" -> int / float
const auto& expiration_time_item = cert_map->get(kDiceExpirationTimeLabel);
if (expiration_time_item) {
if (!IsItemIntType(*expiration_time_item)) {
LOGE("Expected 'exp' field to be a int: type = %s",
ItemTypeToString(*expiration_time_item));
return false;
}
expiration_time_ = expiration_time_item->asInt()->value();
}
// Not before time "nbf" -> int / float
const auto& not_before_time_item = cert_map->get(kDiceNotBeforeLabel);
if (not_before_time_item) {
if (!IsItemIntType(*not_before_time_item)) {
LOGE("Expected 'nbf' field to be a int: type = %s",
ItemTypeToString(*not_before_time_item));
return false;
}
not_before_time_ = not_before_time_item->asInt()->value();
}
// Issued at time "iat" -> int / float
const auto& issued_time_item = cert_map->get(kDiceIssuedAtLabel);
if (issued_time_item) {
if (!IsItemIntType(*issued_time_item)) {
LOGE("Expected 'iat' field to be a int: type = %s",
ItemTypeToString(*issued_time_item));
return false;
}
issued_time_ = issued_time_item->asInt()->value();
}
// CWT ID "cti" => bstr
const auto& cwt_id_item = cert_map->get(kDiceCwtIdLabel);
if (cwt_id_item) {
if (!IsItemBstrType(*cwt_id_item)) {
LOGE("Expected 'cti' field to be a bstr: type = %s",
ItemTypeToString(*cwt_id_item));
return false;
}
cwt_id_ = cwt_id_item->asBstr()->value();
}
// Code Hash "codeHash" => bstr
const auto& code_hash_item = cert_map->get(kDiceCodeHashLabel);
if (code_hash_item) {
if (!IsItemBstrType(*code_hash_item)) {
LOGE("Expected 'codeHash' field to be a bstr: type = %s",
ItemTypeToString(*code_hash_item));
return false;
}
code_hash_ = code_hash_item->asBstr()->value();
}
// Code descriptor "codeDescriptor" => bstr
const auto& code_descriptor_item = cert_map->get(kDiceCodeDescriptorLabel);
if (code_descriptor_item) {
if (!IsItemBstrType(*code_descriptor_item)) {
LOGE("Expected 'codeDescriptor' field to be a bstr: type = %s",
ItemTypeToString(*code_descriptor_item));
return false;
}
code_descriptor_ = code_descriptor_item->asBstr()->value();
}
// Config Hash "configHash" => bstr
const auto& config_hash_item = cert_map->get(kDiceConfigHashLabel);
if (config_hash_item) {
if (!IsItemBstrType(*config_hash_item)) {
LOGE("Expected 'configHash' field to be a bstr: type = %s",
ItemTypeToString(*config_hash_item));
return false;
}
config_hash_ = config_hash_item->asBstr()->value();
}
// Config descriptor "configDescriptor" => bstr
const auto& config_descriptor_item =
cert_map->get(kDiceConfigDescriptorLabel);
if (config_descriptor_item) {
if (!IsItemBstrType(*config_descriptor_item)) {
LOGE("Expected 'configDescriptor' field to be a bstr: type = %s",
ItemTypeToString(*config_descriptor_item));
return false;
}
config_descriptor_ = config_descriptor_item->asBstr()->value();
}
// Authority Hash "configHash" => bstr
const auto& authority_hash_item = cert_map->get(kDiceAuthorityHashLabel);
if (authority_hash_item) {
if (!IsItemBstrType(*authority_hash_item)) {
LOGE("Expected 'authorityHash' field to be a bstr: type = %s",
ItemTypeToString(*authority_hash_item));
return false;
}
authority_hash_ = authority_hash_item->asBstr()->value();
}
// Authority descriptor "authorityDescriptor" => bstr
const auto& authority_descriptor_item =
cert_map->get(kDiceAuthorityDescriptorLabel);
if (authority_descriptor_item) {
if (!IsItemBstrType(*authority_descriptor_item)) {
LOGE("Expected 'authorityDescriptor' field to be a bstr: type = %s",
ItemTypeToString(*authority_descriptor_item));
return false;
}
authority_descriptor_ = authority_descriptor_item->asBstr()->value();
}
// Mode "mode" => bstr .size 1
const auto& mode_item = cert_map->get(kDiceModeLabel);
if (mode_item) {
if (!IsItemBstrType(*mode_item)) {
LOGE("Expected 'mode' field to be a bstr: type = %s",
ItemTypeToString(*mode_item));
return false;
}
const std::vector<uint8_t> mode_bstr = mode_item->asBstr()->value();
mode_ = DecodeModeBytes(mode_bstr);
if (!mode_.has_value()) {
LOGE("Invalid 'mode' format: length = %zu, mode_bstr = %s",
mode_bstr.size(), wvutil::b2a_hex(mode_bstr).c_str());
return false;
}
}
// Subject public key "subjectPublicKey" => bstr
const auto& subject_public_key_item =
cert_map->get(kDiceSubjectPublicKeyLabel);
if (subject_public_key_item) {
if (!IsItemBstrType(*subject_public_key_item)) {
LOGE("Expected 'subjectPublicKey' field to be a bstr: type = %s",
ItemTypeToString(*subject_public_key_item));
return false;
}
subject_public_key_ = subject_public_key_item->asBstr()->value();
}
// Key usage "keyUsage" => bstr .size 1..2
const auto& key_usage_item = cert_map->get(kDiceKeyUsageLabel);
if (key_usage_item) {
if (!IsItemBstrType(*key_usage_item)) {
LOGE("Expected 'keyUsage' field to be a bstr: type = %s",
ItemTypeToString(*key_usage_item));
return false;
}
const std::vector<uint8_t> key_usage_bstr =
key_usage_item->asBstr()->value();
key_usage_ = DecodeKeyUsageBytes(key_usage_bstr);
if (!key_usage_.has_value()) {
LOGE("Invalid 'keyUsage' format: length = %zu, key_usage_bstr = %s",
key_usage_bstr.size(), wvutil::b2a_hex(key_usage_bstr).c_str());
return false;
}
}
// Profile name "profileName" => tstr
const auto& profile_name_item = cert_map->get(kDiceProfileNameLabel);
if (profile_name_item) {
if (!IsItemTstrType(*profile_name_item)) {
LOGE("Expected 'profileName' field to be a tstr: type = %s",
ItemTypeToString(*profile_name_item));
return false;
}
profile_name_ = profile_name_item->asTstr()->value();
}
return true;
}
std::vector<uint8_t> DiceCertData::SerializeCbor() const {
auto cert = cppbor::Map();
if (issuer_.has_value()) {
cert.add(kDiceIssuerLabel, cppbor::Tstr(issuer_.value()));
}
if (subject_.has_value()) {
cert.add(kDiceSubjectLabel, cppbor::Tstr(subject_.value()));
}
if (audience_.has_value()) {
cert.add(kDiceAudienceLabel, cppbor::Tstr(audience_.value()));
}
if (expiration_time_.has_value()) {
cert.add(kDiceExpirationTimeLabel, expiration_time_.value());
}
if (not_before_time_.has_value()) {
cert.add(kDiceNotBeforeLabel, not_before_time_.value());
}
if (issued_time_.has_value()) {
cert.add(kDiceIssuedAtLabel, issued_time_.value());
}
if (cwt_id_.has_value()) {
cert.add(kDiceCwtIdLabel, cppbor::Bstr(cwt_id_.value()));
}
if (code_hash_.has_value()) {
cert.add(kDiceCodeHashLabel, cppbor::Bstr(code_hash_.value()));
}
if (code_descriptor_.has_value()) {
cert.add(kDiceCodeDescriptorLabel, cppbor::Bstr(code_descriptor_.value()));
}
if (config_hash_.has_value()) {
cert.add(kDiceConfigHashLabel, cppbor::Bstr(config_hash_.value()));
}
if (config_descriptor_.has_value()) {
cert.add(kDiceConfigDescriptorLabel,
cppbor::Bstr(config_descriptor_.value()));
}
if (authority_hash_.has_value()) {
cert.add(kDiceAuthorityHashLabel, cppbor::Bstr(authority_hash_.value()));
}
if (authority_descriptor_.has_value()) {
cert.add(kDiceAuthorityDescriptorLabel,
cppbor::Bstr(authority_descriptor_.value()));
}
if (mode_.has_value()) {
cert.add(kDiceModeLabel, cppbor::Bstr(EncodeModeBytes(mode_.value())));
}
if (subject_public_key_.has_value()) {
cert.add(kDiceSubjectPublicKeyLabel,
cppbor::Bstr(subject_public_key_.value()));
}
if (key_usage_.has_value()) {
cert.add(kDiceKeyUsageLabel,
cppbor::Bstr(EncodeKeyUsageBytes(key_usage_.value())));
}
if (profile_name_.has_value()) {
cert.add(kDiceProfileNameLabel, cppbor::Tstr(profile_name_.value()));
}
// All CBOR DICE certs must be canonicalized.
return cert.canonicalize().encode();
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,561 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include "dice_cert_utils.h"
#include <inttypes.h>
#include <optional>
#include <string>
#include <vector>
#include <cppbor.h>
#include <gtest/gtest.h>
#include "oemcrypto_ref_test_utils.h"
namespace wvoec {
namespace util {
TEST(DiceCertDataTest, Empty) {
const DiceCertData cert_data;
EXPECT_FALSE(cert_data.has_issuer());
EXPECT_FALSE(cert_data.has_subject());
EXPECT_FALSE(cert_data.has_audience());
EXPECT_FALSE(cert_data.has_expiration_time());
EXPECT_FALSE(cert_data.has_not_before_time());
EXPECT_FALSE(cert_data.has_issued_time());
EXPECT_FALSE(cert_data.has_cwt_id());
EXPECT_FALSE(cert_data.has_code_hash());
EXPECT_FALSE(cert_data.has_code_descriptor());
EXPECT_FALSE(cert_data.has_config_hash());
EXPECT_FALSE(cert_data.has_config_descriptor());
EXPECT_FALSE(cert_data.has_authority_hash());
EXPECT_FALSE(cert_data.has_authority_descriptor());
EXPECT_FALSE(cert_data.has_mode());
EXPECT_FALSE(cert_data.has_subject_public_key());
EXPECT_FALSE(cert_data.has_key_usage());
EXPECT_FALSE(cert_data.has_profile_name());
EXPECT_EQ(cert_data.issuer(), std::nullopt);
EXPECT_EQ(cert_data.subject(), std::nullopt);
EXPECT_EQ(cert_data.audience(), std::nullopt);
EXPECT_EQ(cert_data.expiration_time(), std::nullopt);
EXPECT_EQ(cert_data.not_before_time(), std::nullopt);
EXPECT_EQ(cert_data.issued_time(), std::nullopt);
EXPECT_EQ(cert_data.cwt_id(), std::nullopt);
EXPECT_EQ(cert_data.code_hash(), std::nullopt);
EXPECT_EQ(cert_data.code_descriptor(), std::nullopt);
EXPECT_EQ(cert_data.config_hash(), std::nullopt);
EXPECT_EQ(cert_data.config_descriptor(), std::nullopt);
EXPECT_EQ(cert_data.authority_hash(), std::nullopt);
EXPECT_EQ(cert_data.authority_descriptor(), std::nullopt);
EXPECT_EQ(cert_data.mode(), std::nullopt);
EXPECT_EQ(cert_data.subject_public_key(), std::nullopt);
EXPECT_EQ(cert_data.key_usage(), std::nullopt);
EXPECT_EQ(cert_data.profile_name(), std::nullopt);
}
TEST(DiceCertDataTest, Parse_IssuerOnly) {
const std::string kIssuer = "issuer";
cppbor::Map cert_map;
cert_map.add(kDiceIssuerLabel, cppbor::Tstr(kIssuer));
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_issuer());
EXPECT_FALSE(cert_data.has_subject());
EXPECT_FALSE(cert_data.has_audience());
EXPECT_EQ(cert_data.issuer(), kIssuer);
EXPECT_EQ(cert_data.subject(), std::nullopt);
EXPECT_EQ(cert_data.audience(), std::nullopt);
}
TEST(DiceCertDataTest, Parse_SubjectOnly) {
const std::string kSubject = "subject";
cppbor::Map cert_map;
cert_map.add(kDiceSubjectLabel, cppbor::Tstr(kSubject));
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_FALSE(cert_data.has_issuer());
EXPECT_TRUE(cert_data.has_subject());
EXPECT_FALSE(cert_data.has_audience());
EXPECT_EQ(cert_data.issuer(), std::nullopt);
EXPECT_EQ(cert_data.subject(), kSubject);
EXPECT_EQ(cert_data.audience(), std::nullopt);
}
TEST(DiceCertDataTest, Parse_AudienceOnly) {
const std::string kAudience = "audience";
cppbor::Map cert_map;
cert_map.add(kDiceAudienceLabel, cppbor::Tstr(kAudience));
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_FALSE(cert_data.has_issuer());
EXPECT_FALSE(cert_data.has_subject());
EXPECT_TRUE(cert_data.has_audience());
EXPECT_EQ(cert_data.issuer(), std::nullopt);
EXPECT_EQ(cert_data.subject(), std::nullopt);
EXPECT_EQ(cert_data.audience(), kAudience);
}
TEST(DiceCertDataTest, Parse_ExpirationTimeOnly) {
const int64_t kTime = 12345;
cppbor::Map cert_map;
cert_map.add(kDiceExpirationTimeLabel, kTime);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_expiration_time());
EXPECT_FALSE(cert_data.has_not_before_time());
EXPECT_FALSE(cert_data.has_issued_time());
EXPECT_EQ(cert_data.expiration_time(), kTime);
EXPECT_EQ(cert_data.not_before_time(), std::nullopt);
EXPECT_EQ(cert_data.issued_time(), std::nullopt);
}
TEST(DiceCertDataTest, Parse_NotBeforeTimeOnly) {
const int64_t kTime = 12345;
cppbor::Map cert_map;
cert_map.add(kDiceNotBeforeLabel, kTime);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_FALSE(cert_data.has_expiration_time());
EXPECT_TRUE(cert_data.has_not_before_time());
EXPECT_FALSE(cert_data.has_issued_time());
EXPECT_EQ(cert_data.expiration_time(), std::nullopt);
EXPECT_EQ(cert_data.not_before_time(), kTime);
EXPECT_EQ(cert_data.issued_time(), std::nullopt);
}
TEST(DiceCertDataTest, Parse_IssuedAtTimeOnly) {
const int64_t kTime = 12345;
cppbor::Map cert_map;
cert_map.add(kDiceIssuedAtLabel, kTime);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_FALSE(cert_data.has_expiration_time());
EXPECT_FALSE(cert_data.has_not_before_time());
EXPECT_TRUE(cert_data.has_issued_time());
EXPECT_EQ(cert_data.expiration_time(), std::nullopt);
EXPECT_EQ(cert_data.not_before_time(), std::nullopt);
EXPECT_EQ(cert_data.issued_time(), kTime);
}
TEST(DiceCertDataTest, Parse_ModeOnly) {
const uint8_t kMode = kDiceModeDebug;
const std::vector<uint8_t> kModeBytes = {kMode};
cppbor::Map cert_map;
cert_map.add(kDiceModeLabel, kModeBytes);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_mode());
EXPECT_EQ(cert_data.mode(), kMode);
}
TEST(DiceCertDataTest, Parse_KeyUsageOnly_Zero) {
const DiceKeyUsageFlags kKeyUsage = 0;
// little-endian
const std::vector<uint8_t> kKeyUsageBytes = {0};
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kKeyUsageBytes);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_key_usage());
EXPECT_EQ(cert_data.key_usage(), kKeyUsage);
}
TEST(DiceCertDataTest, Parse_KeyUsageOnly_SingleByte) {
const DiceKeyUsageFlags kKeyUsage = 0x40;
// little-endian
const std::vector<uint8_t> kKeyUsageBytes = {0x40};
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kKeyUsageBytes);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_key_usage());
EXPECT_EQ(cert_data.key_usage(), kKeyUsage);
}
TEST(DiceCertDataTest, Parse_KeyUsageOnly_TwoBytes) {
const DiceKeyUsageFlags kKeyUsage = 0x0245;
// little-endian
const std::vector<uint8_t> kKeyUsageBytes = {0x45, 0x02};
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kKeyUsageBytes);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_key_usage());
EXPECT_EQ(cert_data.key_usage(), kKeyUsage);
}
TEST(DiceCertDataTest, Parse_WidevineCert) {
const std::string kIssuer = "Issuer";
const std::string kSubject = "Subject";
const int64_t kExpiryTime = 3003016585;
const int64_t kNotBeforeTime = 1740798985;
const int64_t kIssuedAtTime = 1740712585;
const std::vector<uint8_t> kCodeHash = RandomData(64);
const std::vector<uint8_t> kConfigHash = RandomData(64);
const std::vector<uint8_t> kConfigDescriptor = RandomData(256);
const std::vector<uint8_t> kAuthorityHash = RandomData(64);
const uint8_t kMode = kDiceModeNormal;
const std::vector<uint8_t> kModeBytes = {kMode};
const std::vector<uint8_t> kSubjectPublicKey = RandomData(512);
const DiceKeyUsageFlags kKeyUsage = kKeyCertSignBit;
const std::vector<uint8_t> kKeyUsageBytes =
DiceCertData::EncodeKeyUsageBytes(kKeyUsage);
const std::string kProfileName = "Widevine";
cppbor::Map cert_map;
cert_map.add(kDiceIssuerLabel, kIssuer);
cert_map.add(kDiceSubjectLabel, kSubject);
// No audience
cert_map.add(kDiceExpirationTimeLabel, kExpiryTime);
cert_map.add(kDiceNotBeforeLabel, kNotBeforeTime);
cert_map.add(kDiceIssuedAtLabel, kIssuedAtTime);
// No CWT ID.
cert_map.add(kDiceCodeHashLabel, kCodeHash);
// No code descriptor.
cert_map.add(kDiceConfigHashLabel, kConfigHash);
cert_map.add(kDiceConfigDescriptorLabel, kConfigDescriptor);
cert_map.add(kDiceAuthorityHashLabel, kAuthorityHash);
// No authority descriptor.
cert_map.add(kDiceModeLabel, kModeBytes);
cert_map.add(kDiceSubjectPublicKeyLabel, kSubjectPublicKey);
cert_map.add(kDiceKeyUsageLabel, kKeyUsageBytes);
cert_map.add(kDiceProfileNameLabel, kProfileName);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
ASSERT_TRUE(cert_data.ParseCbor(cbor_cert));
EXPECT_TRUE(cert_data.has_issuer());
EXPECT_TRUE(cert_data.has_subject());
EXPECT_FALSE(cert_data.has_audience());
EXPECT_TRUE(cert_data.has_expiration_time());
EXPECT_TRUE(cert_data.has_not_before_time());
EXPECT_TRUE(cert_data.has_issued_time());
EXPECT_FALSE(cert_data.has_cwt_id());
EXPECT_TRUE(cert_data.has_code_hash());
EXPECT_FALSE(cert_data.has_code_descriptor());
EXPECT_TRUE(cert_data.has_config_hash());
EXPECT_TRUE(cert_data.has_config_descriptor());
EXPECT_TRUE(cert_data.has_authority_hash());
EXPECT_FALSE(cert_data.has_authority_descriptor());
EXPECT_TRUE(cert_data.has_mode());
EXPECT_TRUE(cert_data.has_subject_public_key());
EXPECT_TRUE(cert_data.has_key_usage());
EXPECT_TRUE(cert_data.has_profile_name());
EXPECT_EQ(cert_data.issuer(), kIssuer);
EXPECT_EQ(cert_data.subject(), kSubject);
EXPECT_EQ(cert_data.audience(), std::nullopt);
EXPECT_EQ(cert_data.expiration_time(), kExpiryTime);
EXPECT_EQ(cert_data.not_before_time(), kNotBeforeTime);
EXPECT_EQ(cert_data.issued_time(), kIssuedAtTime);
EXPECT_EQ(cert_data.cwt_id(), std::nullopt);
EXPECT_EQ(cert_data.code_hash(), kCodeHash);
EXPECT_EQ(cert_data.code_descriptor(), std::nullopt);
EXPECT_EQ(cert_data.config_hash(), kConfigHash);
EXPECT_EQ(cert_data.config_descriptor(), kConfigDescriptor);
EXPECT_EQ(cert_data.authority_hash(), kAuthorityHash);
EXPECT_EQ(cert_data.authority_descriptor(), std::nullopt);
EXPECT_EQ(cert_data.mode(), kMode);
EXPECT_EQ(cert_data.subject_public_key(), kSubjectPublicKey);
EXPECT_EQ(cert_data.key_usage(), kKeyUsage);
EXPECT_EQ(cert_data.profile_name(), kProfileName);
const std::vector<uint8_t> repackaged_cbor_cert = cert_data.SerializeCbor();
ASSERT_FALSE(repackaged_cbor_cert.empty());
EXPECT_EQ(cbor_cert, repackaged_cbor_cert);
}
TEST(DiceCertDataTest, ParseBad_BadIssuerType) {
const int64_t kBadIssuer = 64;
cppbor::Map cert_map;
cert_map.add(kDiceIssuerLabel, kBadIssuer); // Must be tstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadSubjectType) {
const std::vector<uint8_t> kBadSubject = {'b', 's', 't', 'r'};
cppbor::Map cert_map;
cert_map.add(kDiceSubjectLabel, kBadSubject); // Must be tstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadAudienceType) {
const bool kBadAudience = true;
cppbor::Map cert_map;
cert_map.add(kDiceAudienceLabel, cppbor::Bool(kBadAudience)); // Must be tstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadExpirationTimeType) {
cppbor::Map cert_map;
cert_map.add(kDiceExpirationTimeLabel, cppbor::Null()); // Must be int
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadNotBeforeTimeType) {
const std::string kBadNotBeforeTime = "02/27/25";
cppbor::Map cert_map;
cert_map.add(kDiceNotBeforeLabel, kBadNotBeforeTime); // Must be int
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadIssuedAtTimeType) {
const std::vector<uint8_t> kBadIssuedAtTime = {1, 2, 3, 4, 5};
cppbor::Map cert_map;
cert_map.add(kDiceIssuedAtLabel, kBadIssuedAtTime); // Must be int
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadCwtIdType) {
const std::string kBadCwtId = "CWT ID as string";
cppbor::Map cert_map;
cert_map.add(kDiceCwtIdLabel, kBadCwtId); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadCodeHashType) {
const int64_t kBadCodeHash = 1234;
cppbor::Map cert_map;
cert_map.add(kDiceCodeHashLabel, kBadCodeHash); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadCodeDescriptorType) {
cppbor::Map cert_map;
cert_map.add(kDiceCodeDescriptorLabel, cppbor::Null()); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadConfigHashType) {
const int64_t kBadConfigHash = 1234;
cppbor::Map cert_map;
cert_map.add(kDiceConfigHashLabel, kBadConfigHash); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadConfigDescriptorType) {
cppbor::Map cert_map;
cert_map.add(kDiceConfigDescriptorLabel, cppbor::Null()); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadAuthorityHashType) {
const int64_t kBadAuthorityHash = 1234;
cppbor::Map cert_map;
cert_map.add(kDiceAuthorityHashLabel, kBadAuthorityHash); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadAuthorityDescriptorType) {
cppbor::Map cert_map;
cert_map.add(kDiceAuthorityDescriptorLabel, cppbor::Null()); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadModeType) {
const std::string kBadMode = "Normal";
cppbor::Map cert_map;
cert_map.add(kDiceModeLabel, kBadMode); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadModeLength_TooLong) {
const std::vector<uint8_t> kBadMode = {kDiceModeRecovery, 0};
cppbor::Map cert_map;
cert_map.add(kDiceModeLabel, kBadMode);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadModeLength_TooShort) {
const std::vector<uint8_t> kBadMode;
cppbor::Map cert_map;
cert_map.add(kDiceModeLabel, kBadMode);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadSubjectPublicKeyType) {
const std::string kBadSubjectPublicKey = "I'm a key, not!";
cppbor::Map cert_map;
cert_map.add(kDiceSubjectPublicKeyLabel, kBadSubjectPublicKey);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadKeyUsageType) {
const int64_t kBadKeyUsage = 1234;
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kBadKeyUsage); // Must be bstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadKeyUsageLength_TooLong) {
const std::vector<uint8_t> kBadKeyUsage = {0, 0, 1};
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kBadKeyUsage);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadKeyUsageLength_TooShort) {
const std::vector<uint8_t> kBadKeyUsage;
cppbor::Map cert_map;
cert_map.add(kDiceKeyUsageLabel, kBadKeyUsage);
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
TEST(DiceCertDataTest, ParseBad_BadProfileNameType) {
cppbor::Map cert_map;
cert_map.add(kDiceProfileNameLabel, cppbor::Bool(false)); // Must be tstr
const std::vector<uint8_t> cbor_cert = cert_map.canonicalize().encode();
ASSERT_FALSE(cbor_cert.empty());
DiceCertData cert_data;
EXPECT_FALSE(cert_data.ParseCbor(cbor_cert));
}
} // namespace util
} // namespace wvoec

210
util/include/hls_key.h Normal file
View File

@@ -0,0 +1,210 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_HLS_KEY_H_
#define WVCDM_UTIL_HLS_KEY_H_
#include <inttypes.h>
#include <ostream>
#include <set>
#include <string>
#include <vector>
#include "wv_class_utils.h"
namespace wvutil {
class HlsAttributeList;
// Data wrapper types for HLS Key fields.
//
// Ensures that the values of each attribute of an
// HLS key is valid. The class does not enforce contextual
// requirements between attributes; however, IsWellFormed()
// can optionally be called to ensure some fields are set
// appropriately based on the values of other fields.
//
// This class is based on RFC 8216 section 4.3.2.4.
class HlsKey {
public:
// HLS Tag for key data.
static constexpr char kTag[] = "EXT-X-KEY";
// Attribute names for HLS keys.
// METHOD is an enumerated string with three known values
// of NONE, AES-128, or SAMPLE-AES.
// METHOD may be a different value so long as it is a
// valid enum string.
// METHOD is required, if set to NONE, then the other
// fields SHOULD NOT be set.
static constexpr char kMethodName[] = "METHOD";
// URI is a quoted string containing be a valid URI which
// identifies where to find the key(s) used for content
// decryption.
// Must be set if METHOD is not NONE.
// Should not be set if METHOD is NONE.
static constexpr char kUriName[] = "URI";
// IV is a hex sequence for a 128-bit initialization vector
// used for decryption of media content.
// Must be set if METHOD is not NONE.
// Should not be set if METHOD is NONE.
static constexpr char kIvName[] = "IV";
// The key format is a quoted string which identifies the
// format of the key acquired by the URI.
// Optionally set if METHOD is not NONE, if not set, then
// it is assumed the URI will directly acquire the key.
// Should not be set if METHOD is NONE.
static constexpr char kKeyFormatName[] = "KEYFORMAT";
// The key format versions is a quoted string containing one
// or more positive integers (of HLS integer format) separated
// by a '/', which indicates the vendor-specific versions of the
// key acquired via the URI.
// Optionally set if METHOD is not NONE, assumed to contain
// a value of 1 if not set but a version is required.
// Should not be set if METHOD is NONE.
static constexpr char kKeyFormatVersionsName[] = "KEYFORMATVERSIONS";
// List of known METHOD values.
static constexpr char kMethodNone[] = "NONE";
static constexpr char kMethodAes128[] = "AES-128";
static constexpr char kMethodSampleAes[] = "SAMPLE-AES";
// HLS keys require an IV length of 128-bits / 16 octets.
static constexpr size_t kIvLength = 16;
// Checks if the provided |method| is one of the known
// values for the enumerated string METHOD.
static bool IsKnownMethod(const std::string& method);
// Checks if the provided |method| is a valid HLS enum
// string value.
static bool IsValidMethod(const std::string& method);
// Simply checks that the provided URI is a valid HLS quoted
// string value. Does not valid URI format.
static bool IsValidUriValue(const std::string& uri);
// Checks that the specified IV length (in octets) is a supported
// length.
static constexpr bool IsValidIvLength(size_t iv_length) {
return iv_length == kIvLength;
}
static bool IsValidIv(const std::string& iv) {
return IsValidIvLength(iv.size());
}
static bool IsValidIv(const std::vector<uint8_t>& iv) {
return IsValidIvLength(iv.size());
}
// Checks that the provided key format conforms to the quoted
// string value requirements of HLS.
static bool IsValidKeyFormat(const std::string& key_format);
// Checks if the specified |version| is a valid key format
// version (must be positive).
static constexpr bool IsValidKeyFormatVersion(uint64_t version) {
return version > 0;
}
// Checks that all the values in the set of |versions| is a valid
// versions.
// The set of versions cannot be empty, and all versions must be a
// positive value.
static bool IsValidKeyFormatVersions(const std::set<uint64_t>& versions);
// Checks that the provided formatted version list is a valid
// representation of key format versions.
// Note: There is no requirement that the versions are unique.
static bool IsValidKeyFormatVersionListRep(
const std::string& version_list_rep);
HlsKey() = default;
WVCDM_DEFAULT_COPY_AND_MOVE(HlsKey);
const std::string& method() const { return method_; }
bool HasMethod() const { return !method_.empty(); }
bool SetMethod(const std::string& method);
void ClearMethod() { method_.clear(); }
// URIs are only validated that they are valid quoted string
// values; not that they are well formed URIs.
const std::string& uri() const { return uri_; }
bool HasUri() const { return !uri_.empty(); }
bool SetUri(const std::string& uri);
void ClearUri() { uri_.clear(); }
const std::vector<uint8_t>& iv() const { return iv_; }
bool HasIv() const { return !iv_.empty(); }
bool SetIv(const std::vector<uint8_t>& iv);
bool SetIv(const std::string& iv);
bool SetIvHex(const std::string& iv_hex);
void ClearIv() { iv_.clear(); }
const std::string& key_format() const { return key_format_; }
bool HasKeyFormat() const { return !key_format_.empty(); }
bool SetKeyFormat(const std::string& key_format);
void ClearKeyFormat() { key_format_.clear(); }
const std::set<uint64_t>& key_format_versions() const {
return key_format_versions_;
}
bool HasKeyFormatVersions() const { return !key_format_versions_.empty(); }
bool HasKeyFormatVersion(uint64_t version) const;
bool SetKeyFormatVersions(const std::set<uint64_t>& versions);
bool SetKeyFormatVersions(const std::vector<uint64_t>& versions);
bool AddKeyFormatVersion(uint64_t version);
void ClearKeyFormatVersions() { key_format_versions_.clear(); }
void Clear() {
ClearMethod();
ClearUri();
ClearIv();
ClearKeyFormat();
ClearKeyFormatVersions();
}
// Checks that the HLS key is well-formed based on the requirements
// of an HLS key. Relies mainly on the requirements of METHOD.
bool IsWellFormed() const;
// == Parsing / Serialization ==
// Initializes the HLS key from a populated instance of
// HlsAttributeList.
//
// Requirements:
// - METHOD must be set, must be one of the well-known methods.
// - URI is optional, must be quoted string if set.
// - IV is optional, must be a 16-byte / 128-bit hex sequence if set.
// - KEYFORMAT is optional, must be a quoted string if set, defaults
// to "identify" is not set.
// - KEYFORMATVERSIONS is optional, must be a quoted string containing
// a valid sequence of '/' separated version if set, defaults to
// "1" if not set.
bool FromAttributeList(const HlsAttributeList& attr_list);
// Initializes the HLS key from the HLS serialized HLS Attribute List.
// Internally uses FromAttributeList(), and follows the same requirements.
bool ParseAttributeList(const std::string& attr_list_rep);
// Packages the HLS key into the HlsAttributeList instance.
// Clears existing data in |attr_list|.
//
// Serializing does not enforce any field requirements other than
// types.
//
// Follows the following rules:
// - METHOD packaged as enum string if set
// - URI packaged as quoted string if set
// - IV packaged as hex sequence if set
// - KEYFORMAT packaged as quoted string if set
// - KEYFORMATVERSIONS packaged as quoted string containing slash
// separated version if any version is set.
bool ToAttributeList(HlsAttributeList* attr_list) const;
// Serializes the HLS key into an HLS attribute string format.
// Internally uses ToAttributeList(), and follows the same packaging
// rules.
std::string Serialize() const;
bool SerializeToStream(std::ostream* out) const;
private:
// Values are either empty or set to valid values.
std::string method_;
std::string uri_;
std::vector<uint8_t> iv_;
std::string key_format_;
std::set<uint64_t> key_format_versions_;
}; // class HlsKey
} // namespace wvutil
#endif // WVCDM_UTIL_HLS_KEY_H_

469
util/src/hls_key.cpp Normal file
View File

@@ -0,0 +1,469 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "hls_key.h"
#include <inttypes.h>
#include <algorithm>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "hls_attribute_list.h"
#include "log.h"
#include "string_conversions.h"
#include "string_utils.h"
namespace wvutil {
namespace {
// If KEYFORMATVERSIONS specifies multiple versions, then each
// version is separated by a '/' character.
constexpr char kVersionSeparatorChar = '/';
constexpr char kZeroChar = '0';
// RFC 8216 specifies that KEYFORMAT is optional, but when
// METHOD is not NONE and KEYFORMAT absent, then KEYFORMAT is assumed
// to be "identity".
constexpr char kDefaultKeyFormat[] = "identity";
// RFC 8216 specifies that KEYFORMATVERSIONS is optional, but when
// METHOD is not NONE, and KEYFORMATVERSIONS absent, then
// KEYFORMATVERSIONS is assumed to be "1".
constexpr uint64_t kDefaultKeyFormatVersion = 1;
// Checks each version in the |versions| container are valid
// values.
template <class Container>
bool IsValidKeyFormatVersionsInternal(const Container& versions) {
// Container should be of uint64_t.
static_assert(std::is_same_v<typename Container::value_type, uint64_t>,
"Container must have values of uint64_t");
if (versions.empty()) return false;
// Note: Duplicate values are technically not invalid.
return std::all_of(versions.begin(), versions.end(),
HlsKey::IsValidKeyFormatVersion);
}
bool IsValidKeyFormatVersionRep(const std::string& version_rep) {
// Must be a valid HLS integer.
if (!HlsAttributeList::IsValidIntegerRep(version_rep)) return false;
// Version cannot be zero.
// Note: HLS integer strings may contain leading zeros, so long
// as not all the digits are zero, the |version_rep| is valid.
const size_t zero_count =
std::count(version_rep.begin(), version_rep.end(), kZeroChar);
return zero_count < version_rep.size();
}
bool ParseKeyFormatVersionList(const std::string& version_list_rep,
std::set<uint64_t>* versions) {
versions->clear();
const std::vector<std::string> version_reps =
StringSplit(version_list_rep, kVersionSeparatorChar);
if (version_reps.empty()) {
LOGV("Invalid key format version list rep");
return false;
}
for (const auto& version_rep : version_reps) {
uint64_t version = 0;
if (!HlsAttributeList::ParseInteger(version_rep, &version)) {
LOGV("Failed to parse key format version: %s", version_rep.c_str());
versions->clear();
return false;
}
if (!HlsKey::IsValidKeyFormatVersion(version)) {
LOGV("Found invalid key format version: %" PRIu64, version);
versions->clear();
return false;
}
versions->insert(version);
}
return true;
}
std::string KeyFormatVersionsToString(const std::vector<uint64_t>& versions) {
std::string result;
for (const uint64_t& version : versions) {
// Ensure each version is separated by a '/' character.
if (!result.empty()) result.push_back(kVersionSeparatorChar);
result.append(std::to_string(version));
}
return result;
}
std::string KeyFormatVersionsToString(const std::set<uint64_t>& versions) {
// std::set are not necessarily sorted. Converting to a vector
// to allow formatting in ascending order.
std::vector<uint64_t> versions_vec(versions.begin(), versions.end());
std::sort(versions_vec.begin(), versions_vec.end());
return KeyFormatVersionsToString(versions_vec);
}
} // namespace
// static
bool HlsKey::IsKnownMethod(const std::string& method) {
return method == kMethodNone || method == kMethodAes128 ||
method == kMethodSampleAes;
}
// static
bool HlsKey::IsValidMethod(const std::string& method) {
return !method.empty() && HlsAttributeList::IsValidEnumStringValue(method);
}
// static
bool HlsKey::IsValidUriValue(const std::string& uri) {
return !uri.empty() && HlsAttributeList::IsValidQuotedStringValue(uri);
}
// static
bool HlsKey::IsValidKeyFormat(const std::string& key_format) {
return !key_format.empty() &&
HlsAttributeList::IsValidQuotedStringValue(key_format);
}
// static
bool HlsKey::IsValidKeyFormatVersions(const std::set<uint64_t>& versions) {
return IsValidKeyFormatVersionsInternal(versions);
}
// static
bool HlsKey::IsValidKeyFormatVersionListRep(
const std::string& version_list_rep) {
const std::vector<std::string> version_reps =
StringSplit(version_list_rep, kVersionSeparatorChar);
if (version_reps.empty()) return false;
return std::all_of(version_reps.begin(), version_reps.end(),
IsValidKeyFormatVersionRep);
}
bool HlsKey::SetMethod(const std::string& method) {
if (!IsValidMethod(method)) {
LOGV("Invalid method: %s", SafeByteIdToString(method).c_str());
return false;
}
method_ = method;
return true;
}
bool HlsKey::SetUri(const std::string& uri) {
if (!IsValidUriValue(uri)) {
LOGV("Invalid URI: %s", SafeByteIdToString(uri).c_str());
return false;
}
uri_ = uri;
return true;
}
bool HlsKey::SetIv(const std::vector<uint8_t>& iv) {
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_ = iv;
return true;
}
bool HlsKey::SetIv(const std::string& iv) {
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_.assign(iv.begin(), iv.end());
return true;
}
bool HlsKey::SetIvHex(const std::string& iv_hex) {
std::vector<uint8_t> iv = a2b_hex(iv_hex);
if (iv.empty() && !iv_hex.empty()) {
LOGV("Invalid IV hex: %s", SafeByteIdToString(iv_hex).c_str());
return false;
}
if (!IsValidIv(iv)) {
LOGV("Invalid IV: 0x%s (length = %zu)", b2a_hex(iv).c_str(), iv.size());
return false;
}
iv_ = std::move(iv);
return true;
}
bool HlsKey::SetKeyFormat(const std::string& key_format) {
if (!IsValidKeyFormat(key_format)) {
LOGV("Invalid key format: %s", SafeByteIdToString(key_format).c_str());
return false;
}
key_format_ = key_format;
return true;
}
bool HlsKey::HasKeyFormatVersion(uint64_t version) const {
return key_format_versions_.find(version) != key_format_versions_.end();
}
bool HlsKey::SetKeyFormatVersions(const std::set<uint64_t>& versions) {
if (!IsValidKeyFormatVersionsInternal(versions)) {
LOGV("Invalid key format versions: %s",
KeyFormatVersionsToString(versions).c_str());
return false;
}
key_format_versions_ = versions;
return true;
}
bool HlsKey::SetKeyFormatVersions(const std::vector<uint64_t>& versions) {
if (!IsValidKeyFormatVersionsInternal(versions)) {
LOGV("Invalid key format versions: %s",
KeyFormatVersionsToString(versions).c_str());
return false;
}
key_format_versions_.clear();
key_format_versions_.insert(versions.begin(), versions.end());
return true;
}
bool HlsKey::AddKeyFormatVersion(uint64_t version) {
if (!IsValidKeyFormatVersion(version)) {
LOGV("Invalid key format version: %" PRIu64, version);
return false;
}
key_format_versions_.insert(version);
return true;
}
bool HlsKey::IsWellFormed() const {
// METHOD is always required.
if (!HasMethod()) {
LOGV("Missing method");
return false;
}
if (method() == kMethodNone) {
// From RFC 8216, section 4.3.2.4:
// If the encryption method is NONE, other attributes
// MUST NOT be present.
if (HasUri()) {
LOGV("URI not allowed if method is NONE: uri = %s", uri_.c_str());
return false;
}
if (HasIv()) {
LOGV("IV not allowed if method is NONE: iv = 0x%s", b2a_hex(iv_).c_str());
return false;
}
if (HasKeyFormat()) {
LOGV("Key format not allowed if method is NONE: key_format = %s",
key_format_.c_str());
return false;
}
if (HasKeyFormatVersions()) {
LOGV(
"Key format versions are not allowed if method is NONE: "
"key_format_versions = %s",
KeyFormatVersionsToString(key_format_versions_).c_str());
return false;
}
return true;
}
// METHOD != NONE
// URI is the only required field if METHOD is not NONE.
if (!HasUri()) {
LOGV("Missing URI");
return false;
}
// IV, key format and key format versions are optional fields.
return true;
}
bool HlsKey::FromAttributeList(const HlsAttributeList& list) {
if (list.IsEmpty()) {
LOGV("HLS attribute list is empty");
return false;
}
if (!list.IsType(kMethodName, HlsAttributeList::kEnumStringType)) {
LOGV("HLS attribute list is missing METHOD");
return false;
}
std::string method;
if (!list.GetEnumString(kMethodName, &method)) {
// Definitely an error as the check above should
// ensure method is available.
LOGE("Failed to get METHOD");
return false;
}
if (!IsValidMethod(method)) {
LOGV("Invalid method: %s", method.c_str());
return false;
}
if (method == kMethodNone) {
// Ignore all other fields.
Clear();
method_ = std::move(method);
return true;
}
// URI is the only required field.
if (!list.IsType(kUriName, HlsAttributeList::kQuotedStringType)) {
LOGV("HLS attribute list is missing URI");
return false;
}
std::string uri;
if (!list.GetQuotedString(kUriName, &uri)) {
LOGE("Failed to get URI");
return false;
}
if (!IsValidUriValue(uri)) {
LOGV("Invalid URI: \"%s\"", uri.c_str());
return false;
}
std::vector<uint8_t> iv;
if (list.IsType(kIvName, HlsAttributeList::kHexSequenceType)) {
if (!list.GetHexSequence(kIvName, &iv)) {
LOGE("Failed to get IV");
return false;
}
if (!IsValidIv(iv)) {
// Note: hex sequences are never empty.
LOGV("Invalid IV: 0x%s", b2a_hex(iv).c_str());
return false;
}
} else if (list.Contains(kIvName)) {
LOGV("HLS attribute list contains an invalid IV");
return false;
}
std::string key_format = kDefaultKeyFormat;
if (list.IsType(kKeyFormatName, HlsAttributeList::kQuotedStringType)) {
if (!list.GetQuotedString(kKeyFormatName, &key_format)) {
LOGE("Failed to get KEYFORMAT");
return false;
}
if (!IsValidKeyFormat(key_format)) {
LOGV("Invalid key format: \"%s\"", key_format.c_str());
return false;
}
} else if (list.Contains(kKeyFormatName)) {
LOGV("HLS attribute list contains an invalid KEYFORMAT");
return false;
}
std::set<uint64_t> versions = {kDefaultKeyFormatVersion};
if (list.IsType(kKeyFormatVersionsName,
HlsAttributeList::kQuotedStringType)) {
versions.clear();
std::string version_list_rep;
if (!list.GetQuotedString(kKeyFormatVersionsName, &version_list_rep)) {
LOGE("Failed to get KEYFORMATVERSIONS");
return false;
}
if (!IsValidKeyFormatVersionListRep(version_list_rep)) {
LOGV("Invalid key format versions: \"%s\"", version_list_rep.c_str());
return false;
}
if (!ParseKeyFormatVersionList(version_list_rep, &versions)) {
LOGE("Failed to parse key format versions: \"%s\"",
version_list_rep.c_str());
return false;
}
} else if (list.Contains(kKeyFormatVersionsName)) {
LOGV("HLS attribute list contains an invalid KEYFORMATVERSIONS");
return false;
}
// Ignore all other fields as they might be from a future HLS version.
method_ = std::move(method);
uri_ = std::move(uri);
iv_ = std::move(iv);
key_format_ = std::move(key_format);
key_format_versions_ = std::move(versions);
return true;
}
bool HlsKey::ParseAttributeList(const std::string& list_rep) {
if (list_rep.empty()) {
LOGV("HLS attribute list rep is empty");
return false;
}
HlsAttributeList list;
if (!list.Parse(list_rep)) {
LOGV("Failed to parse HLS attribute list");
return false;
}
return FromAttributeList(list);
}
// Note: Any value that is set is assumed to able to
// produce a valid HLS attribute value, and therefore, setting
// the attribute should be successful. Failure to do so is
// considered an internal error.
bool HlsKey::ToAttributeList(HlsAttributeList* list) const {
if (list == nullptr) {
LOGE("Output |list| is null");
return false;
}
if (!IsWellFormed()) {
// Serialize anyways.
LOGW("Current HLS key is incomplete");
}
list->Clear();
if (HasMethod() && !list->SetEnumString(kMethodName, method_)) {
LOGE("Failed to set method: %s=%s", kMethodName, method_.c_str());
list->Clear();
return false;
}
if (HasUri() && !list->SetQuotedString(kUriName, uri_)) {
LOGE("Failed to set URI: %s=\"%s\"", kUriName, uri_.c_str());
list->Clear();
return false;
}
if (HasIv() && !list->SetHexSequence(kIvName, iv_)) {
LOGE("Failed to set IV: %s=0x%s", kIvName, b2a_hex(iv_).c_str());
list->Clear();
return false;
}
if (HasKeyFormat() && !list->SetQuotedString(kKeyFormatName, key_format_)) {
LOGE("Failed to set key format: %s=\"%s\"", kKeyFormatName,
key_format_.c_str());
list->Clear();
return false;
}
if (HasKeyFormatVersions()) {
const std::string formatted_versions =
KeyFormatVersionsToString(key_format_versions_);
if (!list->SetQuotedString(kKeyFormatVersionsName, formatted_versions)) {
LOGE("Failed to set key format versions: %s=\"%s\"",
kKeyFormatVersionsName, formatted_versions.c_str());
list->Clear();
return false;
}
}
return true;
}
std::string HlsKey::Serialize() const {
HlsAttributeList list;
if (!ToAttributeList(&list)) {
LOGV("Failed to convert HLS key to HLS attribute list");
return std::string();
}
return list.Serialize();
}
bool HlsKey::SerializeToStream(std::ostream* out) const {
if (out == nullptr) {
LOGE("Output stream is null");
return false;
}
HlsAttributeList list;
if (!ToAttributeList(&list)) {
LOGV("Failed to convert HLS key to HLS attribute list");
return false;
}
return list.SerializeToStream(out);
}
} // namespace wvutil

View File

@@ -0,0 +1,866 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "hls_key.h"
#include <inttypes.h>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "hls_attribute_list.h"
#include "string_conversions.h"
namespace wvutil {
namespace test {
TEST(HlsKeyTest, ClassConstants) {
// Validity checks on the class constants.
// Verify names.
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kMethodName))
<< "METHOD key invalid: " << HlsKey::kMethodName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kUriName))
<< "URI key invalid: " << HlsKey::kUriName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kIvName))
<< "IV key invalid: " << HlsKey::kIvName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kKeyFormatName))
<< "KEYFORMAT key invalid: " << HlsKey::kKeyFormatName;
EXPECT_TRUE(HlsAttributeList::IsValidName(HlsKey::kKeyFormatVersionsName))
<< "KEYFORMATVERSIONS key invalid: " << HlsKey::kKeyFormatVersionsName;
// Well-known methods.
EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodNone))
<< "METHOD=NONE value invalid: " << HlsKey::kMethodNone;
EXPECT_TRUE(HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodAes128))
<< "METHOD=AES-128 value invalid: " << HlsKey::kMethodAes128;
EXPECT_TRUE(
HlsAttributeList::IsValidEnumStringValue(HlsKey::kMethodSampleAes))
<< "METHOD=SAMPLE-AES value invalid: " << HlsKey::kMethodSampleAes;
}
TEST(HlsKeyTest, IsValidKeyFormatVersion) {
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(1));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(2));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersion(1337));
EXPECT_TRUE(
HlsKey::IsValidKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
}
TEST(HlsKeyTest, IsValidKeyFormatVersion_Invalid) {
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersion(0));
}
TEST(HlsKeyTest, IsValidKeyFormatVersions) {
const std::set<uint64_t> kSingleVersion = {2};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kSingleVersion));
const std::set<uint64_t> kFewVersions = {1, 2, 5};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kFewVersions));
const std::set<uint64_t> kLargeVersion = {
1, 2, std::numeric_limits<uint64_t>::max()};
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersions(kLargeVersion));
}
TEST(HlsKeyTest, IsValidKeyFormatVersions_Invalid) {
const std::set<uint64_t> kEmptySet;
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kEmptySet));
const std::set<uint64_t> kZeroOnly = {0};
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kZeroOnly));
const std::set<uint64_t> kWithZero = {0, 1, 2, 5};
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersions(kWithZero));
}
TEST(HlsKeyTest, IsValidKeyFormatVersionListRep) {
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1/2"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1/2/5"));
EXPECT_TRUE(HlsKey::IsValidKeyFormatVersionListRep("1337"));
}
TEST(HlsKeyTest, IsValidKeyFormatVersionListRep_Invalid) {
// Empty not allowed.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep(""));
// Zero is not allowed.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("0"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("0/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/0/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/0"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/000000"));
// All must integers.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("NONE"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/NONE/4"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/1.2/2"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/0xDEADBEAF"));
// Cannot have empty values.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1//5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("/"));
// Must be a slash separated list.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1,0,5"));
// Cannot have spaces.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep(" 1/2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1 /2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/ 2/5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2 /5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/ 5"));
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("1/2/5 "));
// Must fit in the 64-bit integer range.
EXPECT_FALSE(HlsKey::IsValidKeyFormatVersionListRep("100000000000000000000"));
}
TEST(HlsKeyTest, Empty) {
const HlsKey key;
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_FALSE(key.HasKeyFormatVersion(1));
}
TEST(HlsKeyTest, SetMethod) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
EXPECT_TRUE(key.HasMethod());
EXPECT_EQ(key.method(), "NONE");
key.ClearMethod();
EXPECT_FALSE(key.HasMethod());
}
TEST(HlsKeyTest, SetMethod_Invalid) {
HlsKey key;
// Check that an invalid cannot be set.
EXPECT_FALSE(key.SetMethod("NOT VALID"));
EXPECT_FALSE(key.HasMethod());
EXPECT_TRUE(key.SetMethod("SAMPLE-AES"));
// Check that setting an invalid method does not
// overwrite the existing method.
EXPECT_FALSE(key.SetMethod("NOT VALID"));
EXPECT_EQ(key.method(), "SAMPLE-AES");
}
TEST(HlsKeyTest, SetUri) {
const std::string kSampleUri = "http://media.src/some-key";
HlsKey key;
EXPECT_TRUE(key.SetUri(kSampleUri));
EXPECT_TRUE(key.HasUri());
EXPECT_EQ(key.uri(), kSampleUri);
key.ClearUri();
EXPECT_FALSE(key.HasUri());
}
TEST(HlsKeyTest, SetUri_Invalid) {
const std::string kBadUri = "No\"quotes";
const std::string kGoodUri = "http://media.src/some-key";
HlsKey key;
EXPECT_FALSE(key.SetUri(kBadUri));
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.SetUri(""));
EXPECT_FALSE(key.HasUri());
EXPECT_TRUE(key.SetUri(kGoodUri));
EXPECT_FALSE(key.SetUri(kBadUri));
EXPECT_EQ(key.uri(), kGoodUri);
}
TEST(HlsKeyTest, SetIv_Vec) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
HlsKey key;
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_TRUE(key.HasIv());
EXPECT_EQ(key.iv(), kGoodIv);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIv_Vec_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::vector<uint8_t> kEmptyIv;
const std::vector<uint8_t> kShortIv(HlsKey::kIvLength / 2, 22);
const std::vector<uint8_t> kLargeIv(HlsKey::kIvLength * 2, 22);
HlsKey key;
EXPECT_FALSE(key.SetIv(kEmptyIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_FALSE(key.SetIv(kLargeIv));
EXPECT_FALSE(key.HasIv());
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetIv_String) {
const std::string kGoodIv(HlsKey::kIvLength, 'x');
HlsKey key;
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_TRUE(key.HasIv());
const std::vector<uint8_t> kGoodIvVec(kGoodIv.begin(), kGoodIv.end());
EXPECT_EQ(key.iv(), kGoodIvVec);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIv_String_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kEmptyIv;
const std::string kShortIv(HlsKey::kIvLength / 2, 'y');
const std::string kLargeIv(HlsKey::kIvLength * 2, 'z');
HlsKey key;
EXPECT_FALSE(key.SetIv(kEmptyIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_FALSE(key.SetIv(kLargeIv));
EXPECT_FALSE(key.HasIv());
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIv(kShortIv));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetIvHex) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kGoodIvHex = b2a_hex(kGoodIv);
HlsKey key;
EXPECT_TRUE(key.SetIvHex(kGoodIvHex));
EXPECT_TRUE(key.HasIv());
EXPECT_EQ(key.iv(), kGoodIv);
key.ClearIv();
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, SetIvHex_Invalid) {
const std::vector<uint8_t> kGoodIv(HlsKey::kIvLength, 42);
const std::string kEmptyIvHex;
const std::string kShortIvHex(HlsKey::kIvLength, 'e');
const std::string kLargeIvHex(HlsKey::kIvLength * 4, 'e');
const std::string kNonHexIv(HlsKey::kIvLength * 2, 'x');
HlsKey key;
EXPECT_FALSE(key.SetIvHex(kEmptyIvHex));
EXPECT_FALSE(key.SetIvHex(kShortIvHex));
EXPECT_FALSE(key.SetIvHex(kLargeIvHex));
EXPECT_FALSE(key.SetIvHex(kNonHexIv));
EXPECT_TRUE(key.SetIv(kGoodIv));
EXPECT_FALSE(key.SetIvHex(kShortIvHex));
EXPECT_EQ(key.iv(), kGoodIv);
}
TEST(HlsKeyTest, SetKeyFormat) {
const std::string kGoodKeyFormat = "com.widevine";
HlsKey key;
EXPECT_TRUE(key.SetKeyFormat(kGoodKeyFormat));
EXPECT_TRUE(key.HasKeyFormat());
EXPECT_EQ(key.key_format(), kGoodKeyFormat);
key.ClearKeyFormat();
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, SetKeyFormat_Invalid) {
const std::string kGoodKeyFormat = "com.widevine";
const std::string kEmptyKeyFormat;
const std::string kInvalidKeyFormat = "com\"widevine";
HlsKey key;
EXPECT_FALSE(key.SetKeyFormat(kEmptyKeyFormat));
EXPECT_FALSE(key.SetKeyFormat(kInvalidKeyFormat));
EXPECT_TRUE(key.SetKeyFormat(kGoodKeyFormat));
EXPECT_FALSE(key.SetKeyFormat(kInvalidKeyFormat));
EXPECT_EQ(key.key_format(), kGoodKeyFormat);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Set) {
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
key.ClearKeyFormatVersions();
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersions));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Ensure that set is not merging.
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Set_Invalid) {
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kBadVersions = {0, 2, 5};
HlsKey key;
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersions));
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersions));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Vec) {
const std::vector<uint64_t> kSingleVersionVec = {3};
const std::vector<uint64_t> kFewVersionsVec = {1, 2, 5};
const std::vector<uint64_t> kRepeatedVersionsVec = {1, 1, 2, 5, 5, 5, 5, 5};
const std::set<uint64_t> kSingleVersion = {3};
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersionVec));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
key.ClearKeyFormatVersions();
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersionsVec));
EXPECT_TRUE(key.HasKeyFormatVersions());
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Ensure that set is not merging.
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersionVec));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
// Ensure that repeated values are not stored.
EXPECT_TRUE(key.SetKeyFormatVersions(kRepeatedVersionsVec));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
}
TEST(HlsKeyTest, SetKeyFormatVersions_Vec_Invalid) {
const std::set<uint64_t> kSingleVersion = {3};
const std::vector<uint64_t> kBadVersionsVec = {1, 2, 0, 5};
HlsKey key;
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersionsVec));
EXPECT_FALSE(key.HasKeyFormatVersions());
EXPECT_TRUE(key.SetKeyFormatVersions(kSingleVersion));
EXPECT_FALSE(key.SetKeyFormatVersions(kBadVersionsVec));
EXPECT_EQ(key.key_format_versions(), kSingleVersion);
}
TEST(HlsKeyTest, HasKeyFormatVersion) {
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_FALSE(key.HasKeyFormatVersion(1));
EXPECT_FALSE(key.HasKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
EXPECT_TRUE(key.SetKeyFormatVersions(kFewVersions));
EXPECT_FALSE(key.HasKeyFormatVersion(0));
EXPECT_TRUE(key.HasKeyFormatVersion(1));
EXPECT_TRUE(key.HasKeyFormatVersion(2));
EXPECT_FALSE(key.HasKeyFormatVersion(3));
EXPECT_FALSE(key.HasKeyFormatVersion(4));
EXPECT_TRUE(key.HasKeyFormatVersion(5));
EXPECT_FALSE(key.HasKeyFormatVersion(std::numeric_limits<uint64_t>::max()));
}
TEST(HlsKeyTest, AddKeyFormatVersion) {
const std::set<uint64_t> kFewVersions = {1, 2, 5};
HlsKey key;
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_TRUE(key.AddKeyFormatVersion(2));
EXPECT_TRUE(key.AddKeyFormatVersion(5));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
// Repeated adds doesn't count as error.
EXPECT_TRUE(key.AddKeyFormatVersion(5));
// Cannot add invalid.
EXPECT_FALSE(key.AddKeyFormatVersion(0));
EXPECT_EQ(key.key_format_versions(), kFewVersions);
}
TEST(HlsKeyTest, IsWellFormed_Empty) {
const HlsKey key;
EXPECT_FALSE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNone) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
EXPECT_TRUE(key.IsWellFormed());
}
// When method is none, all other fields must be empty.
TEST(HlsKeyTest, IsWellFormed_MethodNone_Invalid) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("NONE"));
// With URI.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_FALSE(key.IsWellFormed());
key.ClearUri();
// With IV.
EXPECT_TRUE(key.SetIv(std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_FALSE(key.IsWellFormed());
key.ClearIv();
// With key format.
EXPECT_TRUE(key.SetKeyFormat("com.widevine"));
EXPECT_FALSE(key.IsWellFormed());
key.ClearKeyFormat();
// With key format versions.
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_FALSE(key.IsWellFormed());
key.ClearKeyFormatVersions();
// Should be well-formed again.
EXPECT_TRUE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNonNone) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("AES-128"));
// Required fields.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_TRUE(key.SetIv(std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_TRUE(key.IsWellFormed());
// Optional fields.
EXPECT_TRUE(key.SetKeyFormat("com.widevine"));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_TRUE(key.AddKeyFormatVersion(1));
EXPECT_TRUE(key.IsWellFormed());
}
TEST(HlsKeyTest, IsWellFormed_MethodNonNone_Invalid) {
HlsKey key;
EXPECT_TRUE(key.SetMethod("AES-128"));
// Missing required all fields.
EXPECT_FALSE(key.IsWellFormed());
// URI is the only required field.
EXPECT_TRUE(key.SetUri("http://domain.tld/key"));
EXPECT_TRUE(key.IsWellFormed());
// Missing URI.
key.ClearUri();
EXPECT_FALSE(key.IsWellFormed());
}
TEST(HlsKeyTest, FromAttributeList_MethodNone) {
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "NONE"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_MethodNone_IgnoreOtherFields) {
// The HLS specification states that if METHOD is NONE, then
// there should not be any other fields. However, this parser
// is less strict, and will simply ignore the other fields
// when reading from an attribute list.
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "NONE"));
EXPECT_TRUE(
list.SetHexSequence("IV", std::vector<uint8_t>(HlsKey::kIvLength, 42)));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1337"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_MethodNonNone_OnlyRequiredFields) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
const std::string kUri = "http://domain.tld/key";
// HLS standard says to use the following defaults if not
// present in the HLS list.
const std::string kDefaultKeyFormat = "identity";
const std::set<uint64_t> kDefaultKeyFormatVersions = {1};
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "AES-128");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kDefaultKeyFormat);
EXPECT_EQ(key.key_format_versions(), kDefaultKeyFormatVersions);
}
TEST(HlsKeyTest, FromAttributeList_MethodNonNone_AllFields) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
const std::string kUri = "http://domain.tld/key";
const std::string kKeyFormat = "com.widevine";
const std::set<uint64_t> kKeyFormatVersion = {1, 2, 5};
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "SAMPLE-AES"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", kKeyFormat));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1/2/5"));
HlsKey key;
EXPECT_TRUE(key.FromAttributeList(list));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "SAMPLE-AES");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kKeyFormat);
EXPECT_EQ(key.key_format_versions(), kKeyFormatVersion);
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_All) {
HlsAttributeList list;
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_Method) {
const std::string kUri = "http://domain.tld/key";
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetQuotedString("URI", kUri));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_MissingFields_MissingUri) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadMethodType) {
HlsAttributeList list;
// Must be enum string not quoted string.
EXPECT_TRUE(list.SetQuotedString("METHOD", "NONE"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyUri) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", ""));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadUriType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetEnumString("URI", "DATA"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_ShortIv) {
const std::vector<uint8_t> kShortIv(HlsKey::kIvLength / 2, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kShortIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_LargeIv) {
const std::vector<uint8_t> kLargeIv(HlsKey::kIvLength * 2, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kLargeIv));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadIvType) {
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetQuotedString("IV", "42 repeated 16 times"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyKeyFormat) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", ""));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetEnumString("KEYFORMAT", "COM-EXAMPLE"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_EmptyKeyFormatVersions) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", ""));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_ZeroKeyFormatVersions) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "0/1/5"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatVersionsFormat) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetQuotedString("KEYFORMATVERSIONS", "1,2,5"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidFields_BadKeyFormatVersionsType) {
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 42);
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("METHOD", "AES-128"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/key-data"));
EXPECT_TRUE(list.SetHexSequence("IV", kIv));
EXPECT_TRUE(list.SetQuotedString("KEYFORMAT", "com.example"));
EXPECT_TRUE(list.SetInteger("KEYFORMATVERSIONS", 1));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
// Nothing should be set.
EXPECT_FALSE(key.HasMethod());
EXPECT_FALSE(key.HasUri());
EXPECT_FALSE(key.HasIv());
EXPECT_FALSE(key.HasKeyFormat());
EXPECT_FALSE(key.HasKeyFormatVersions());
}
TEST(HlsKeyTest, FromAttributeList_InvalidList) {
// Populate HLS Attribute List as if it is a different message.
// Using EXT-X-MEDIA.
HlsAttributeList list;
EXPECT_TRUE(list.SetEnumString("TYPE", "AUDIO"));
EXPECT_TRUE(list.SetQuotedString("URI", "http://example.com/video-data"));
EXPECT_TRUE(list.SetQuotedString("GROUP-ID", "Video Group"));
EXPECT_TRUE(list.SetQuotedString("LANGUAGE", "en"));
EXPECT_TRUE(list.SetQuotedString("NAME", "A nice video"));
EXPECT_TRUE(list.SetEnumString("DEFAULT", "YES"));
HlsKey key;
EXPECT_FALSE(key.FromAttributeList(list));
}
TEST(HlsKeyTest, ParseAttributeList_MethodNone) {
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList("METHOD=NONE"));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "NONE");
}
TEST(HlsKeyTest, ParseAttributeList_MethodNonNone_OnlyRequiredFields) {
const std::string kUri = "http://domain.tld/key";
const std::vector<uint8_t> kEmptyIv;
const std::string kDefaultKeyFormat = "identity";
const std::set<uint64_t> kDefaultKeyFormatVersions = {1};
std::ostringstream hls_stream;
hls_stream << "METHOD=AES-128,";
hls_stream << "URI=\"" << kUri << "\"";
const std::string key_rep = hls_stream.str();
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList(key_rep));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "AES-128");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kEmptyIv);
EXPECT_EQ(key.key_format(), kDefaultKeyFormat);
EXPECT_EQ(key.key_format_versions(), kDefaultKeyFormatVersions);
}
TEST(HlsKeyTest, ParseAttributeList_MethodNonNone_AllFields) {
const std::string kIvHex(HlsKey::kIvLength * 2, 'A');
const std::vector<uint8_t> kIv(HlsKey::kIvLength, 0xaa);
const std::string kUri = "http://domain.tld/key";
const std::string kKeyFormat = "com.widevine";
const std::set<uint64_t> kKeyFormatVersion = {1, 2, 5};
std::ostringstream hls_stream;
hls_stream << "METHOD=SAMPLE-AES,";
hls_stream << "URI=\"" << kUri << "\",";
hls_stream << "IV=0x" << kIvHex << ',';
hls_stream << "KEYFORMAT=\"" << kKeyFormat << "\",";
hls_stream << "KEYFORMATVERSIONS=\"1/2/5\"";
const std::string key_rep = hls_stream.str();
HlsKey key;
EXPECT_TRUE(key.ParseAttributeList(key_rep));
EXPECT_TRUE(key.IsWellFormed());
EXPECT_EQ(key.method(), "SAMPLE-AES");
EXPECT_EQ(key.uri(), kUri);
EXPECT_EQ(key.iv(), kIv);
EXPECT_EQ(key.key_format(), kKeyFormat);
EXPECT_EQ(key.key_format_versions(), kKeyFormatVersion);
}
TEST(HlsKeyTest, ParseAttributeList_Empty) {
HlsKey key;
EXPECT_FALSE(key.ParseAttributeList(""));
}
TEST(HlsKeyTest, ParseAttributeList_InvalidAttributeList) {
HlsKey key;
EXPECT_FALSE(key.ParseAttributeList("Not an Attribute List"));
}
} // namespace test
} // namespace wvutil