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:
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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_
|
|
||||||
@@ -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
|
|
||||||
@@ -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_ */
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Android Format Style
|
|
||||||
|
|
||||||
edition = "2021"
|
|
||||||
use_small_heuristics = "Max"
|
|
||||||
newline_style = "Unix"
|
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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);
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
346
oemcrypto/util/include/dice_cert_utils.h
Normal file
346
oemcrypto/util/include/dice_cert_utils.h
Normal 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_
|
||||||
@@ -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',
|
||||||
|
|||||||
421
oemcrypto/util/src/dice_cert_utils.cpp
Normal file
421
oemcrypto/util/src/dice_cert_utils.cpp
Normal 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
|
||||||
561
oemcrypto/util/test/dice_cert_utils_unittest.cpp
Normal file
561
oemcrypto/util/test/dice_cert_utils_unittest.cpp
Normal 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
210
util/include/hls_key.h
Normal 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
469
util/src/hls_key.cpp
Normal 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
|
||||||
866
util/test/hls_key_unittest.cpp
Normal file
866
util/test/hls_key_unittest.cpp
Normal 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
|
||||||
Reference in New Issue
Block a user