Source release v3.0.0-0-g8d3792b-ce + third_party
Change-Id: I399e71ddfffcd436171d1c60283c63ab4658e0b1
This commit is contained in:
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,13 +1,16 @@
|
||||
# GYP generated makefiles
|
||||
_auto_*
|
||||
|
||||
# GYP-generated files.
|
||||
Makefile
|
||||
*.Makefile
|
||||
*.mk
|
||||
*.xcodeproj
|
||||
|
||||
# Output directory
|
||||
# Output directory.
|
||||
out/
|
||||
|
||||
# Certificate created by unit tests
|
||||
cert.bin
|
||||
|
||||
# Python object files
|
||||
# Python object files.
|
||||
*.pyc
|
||||
|
||||
# Ignoring backup files.
|
||||
*~
|
||||
|
||||
252
README
252
README
@@ -1,252 +0,0 @@
|
||||
README for Widevine CDM Partner Kit v2.2
|
||||
Date: 12/16/2014
|
||||
|
||||
This document provides additional details on installation, system
|
||||
setup, building, and testing components of the Widevine Content
|
||||
Decryption Module supplied in the Widevine CDM Partner Kit.
|
||||
This document supplements the information found in the "WV Modular DRM
|
||||
Security Integration Guide for Common Encryption (CENC): EME/Embedded
|
||||
Device Supplement v2.1", which is the EME-specific portion of the
|
||||
dcoeument "WV Modular DRM Security Integration Guide for Common
|
||||
Encryption (CENC)".
|
||||
|
||||
Kit Contents
|
||||
|
||||
The Widevine CDM Partner Kit is distributed as an XZipped tar file
|
||||
(.tar.xz). To unpack the kit into your current working directory:
|
||||
|
||||
tar xf widevine-cdm_<kit-version>.tar.xz
|
||||
|
||||
cd widevine-cdm_<kit-version>
|
||||
|
||||
The remaining instructions will refer to this directory as <cdm-kit-dir>.
|
||||
|
||||
The top level directories and files of the kit are as follows:
|
||||
cdm - the CDM interface declarations and definitions, and tests
|
||||
core - the CDM implementation, and tests
|
||||
linux - platform-specific files for the CDM implementation
|
||||
oemcrypto - OEMCrypto declarations, a mock implementation, and tests
|
||||
platforms - platform-specific build configs
|
||||
README - this file
|
||||
run_test.sh - builds and tests the CDM with mock OEMCrypto
|
||||
|
||||
System Setup
|
||||
|
||||
Some third-party software packages are required to build the CDM, OEMCrypto,
|
||||
and various test modules. The actual versions that have been used and
|
||||
verified are listed here, but earlier or later versions may also work. If
|
||||
you already have a different version of a particular package installed
|
||||
on your system, then you should try the build and unit tests to see if
|
||||
there are any issues.
|
||||
|
||||
The current set of third-party packages includes:
|
||||
|
||||
- gTest and gMock (version 1.6.0)
|
||||
- protobufs (Google Protocol Buffers) (version 2.5)
|
||||
- OpenSSL (libssl and libcrypto) (version 1.0.1g)
|
||||
- stringencoders (https://code.google.com/p/stringencoders)
|
||||
- python (version 2.7 or better)
|
||||
- GYP (python-based build tool) (https://code.google.com/p/gyp,
|
||||
svn revision 1846)
|
||||
|
||||
The following sections provide information about acquiring
|
||||
and installing some of these packages. This is not a complete list, but
|
||||
it tries to cover the items that do not follow the typical methodologies.
|
||||
Also, the instructions apply to and were tested on an up-to-date Ubuntu
|
||||
system. They may not work on your system exactly as shown.
|
||||
|
||||
GYP
|
||||
|
||||
The kit uses GYP (Generate Your Projects) to create makefiles.
|
||||
|
||||
The standard GYP source release is through a subversion (svn) checkout
|
||||
of trunk. Most of this kit's development and testing was performed with
|
||||
GYP revision 1846. The newest revision at the time of this document
|
||||
is 1884. You may already have GYP installed on your system, or you may
|
||||
try "apt-get install gyp" to install the version packaged for your
|
||||
system. If you choose to install the latest revision (option 1) or install
|
||||
revision 1846 (option 2), follow the instructions below.
|
||||
|
||||
GYP requires Python. You should already have python 2.7 or better on your
|
||||
system. There is also a python package called setuptools that GYP requires.
|
||||
If the GYP build fails, see the section below to install setuptools.
|
||||
|
||||
python --version # will return "Python 2.7.3" or something similar
|
||||
|
||||
mkdir <gyp-kit-root>
|
||||
cd <gyp-kit-root>
|
||||
|
||||
# checkout latest GYP (option 1)
|
||||
svn checkout http://gyp.googlecode.com/svn/trunk/ gyp-read-only
|
||||
|
||||
# checkout GYP revision 1846 (option 2)
|
||||
svn checkout http://gyp.googlecode.com/svn/trunk/ gyp-read-only -r1846
|
||||
|
||||
cd gyp-read-only/
|
||||
./setup.py build # this will fail if setuptools is not installed
|
||||
sudo ./setup.py install
|
||||
|
||||
Python setuptools
|
||||
|
||||
Do this if the "setup.py build" fails.
|
||||
|
||||
mkdir <setuptools-temp>
|
||||
cd <setuptools-temp>
|
||||
wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py \
|
||||
-O - | sudo python
|
||||
|
||||
Return to the GYP installation.
|
||||
|
||||
Google Protocol Buffers (protobufs)
|
||||
|
||||
The following instructions will install the protobuf compiler and libraries
|
||||
in /usr. The default installation directory is /usr/local, which you may
|
||||
also use, but you should make sure that an older version is not installed
|
||||
in /usr.
|
||||
|
||||
protoc --version # test whether protobufs already on system
|
||||
which protoc # where is it installed?
|
||||
mkdir <protobuf-kit-root>
|
||||
cd <protobuf-kit-root>
|
||||
wget http://protobuf.googlecode.com/files/protobuf-2.5.0.tar.gz
|
||||
tar xzf protobuf-2.5.0.tar.gz
|
||||
cd protobuf-2.5.0/
|
||||
./configure --prefix=/usr --exec-prefix=/usr
|
||||
make
|
||||
sudo make install
|
||||
protoc --version
|
||||
|
||||
The final command should return: libprotoc 2.5.0
|
||||
|
||||
Google C++ Mocking Framework (gmock and gtest)
|
||||
|
||||
The following instructions will install the Google Mock and
|
||||
Google Test frameworks. The package can exist anywhere on
|
||||
the build system. The kit uses gmock-1.6.0.zip and can be
|
||||
downloaded from https://code.google.com/p/googlemock/downloads/list.
|
||||
|
||||
mkdir <gmock_dir>
|
||||
cd <gmock_dir>
|
||||
wget https://googlemock.googlecode.com/files/gmock-1.6.0.zip
|
||||
unzip gmock-1.6.0.zip
|
||||
cd <cdm-kit-dir>
|
||||
mkdir third_party
|
||||
cd third_party
|
||||
ln -sf <gmock_dir>/gmock-1.6.0 gmock
|
||||
|
||||
The kit build will use the symlink when building the unit tests.
|
||||
|
||||
StringEncoders 3.10.3 (stringencoders)
|
||||
|
||||
The following instructions will install and configure the
|
||||
stringencoders package. After configuring and building the
|
||||
package, several files must be copied to the kit.
|
||||
The kit uses stringencoders-v3.10.3.tar.gz which can be downloaded from
|
||||
https://code.google.com/p/stringencoders/downloads/list.
|
||||
|
||||
mkdir <stringencoders-dir>
|
||||
cd <stringencoders-dir>
|
||||
wget \
|
||||
https://stringencoders.googlecode.com/files/stringencoders-v3.10.3.tar.gz
|
||||
tar xzvf stringencoders-v3.10.3.tar.gz
|
||||
cd stringencoders-v3.10.3
|
||||
./configure --with-b64wchars='-_=' CFLAGS=-Wno-unused-but-set-variable
|
||||
make
|
||||
mkdir -p <cdm-kit-dir>/third_party/stringencoders/src
|
||||
cp modp_b64w_data.h <cdm-kit-dir>/third_party/stringencoders/src
|
||||
cp src/modp_b64w.c \
|
||||
<cdm-kit-dir>/third_party/stringencoders/src/modp_b64w.cpp
|
||||
cp src/modp_b64w.h <cdm-kit-dir>/third_party/stringencoders/src
|
||||
|
||||
Note that the file extension of the source is changed from .c to .cpp.
|
||||
|
||||
The final step will be to edit
|
||||
<cdm-kit-dir>/third_party/stringencoders/src/modp_b64w.cpp
|
||||
and comment out the line '#include "config.h"'.
|
||||
|
||||
What's in the kit?
|
||||
|
||||
The kit contains two major components and a small suite of unit tests:
|
||||
a Widevine CDM and a Mock OEMCrypto. These components will enable you
|
||||
to create an OEMCrypto library and a CDM module for your target devices.
|
||||
|
||||
The CDM relies on OEMCrypto for performing secure operations. The Mock
|
||||
OEMCrypto uses OpenSSL to perform all OEMCrypto functions. This library
|
||||
is *NOT* secure and it *CANNOT* be used in a production environment, but
|
||||
it allows you to verify your CDM port before you have a working OEMCrypto
|
||||
for your device, plus it provides a reference implementation to help you
|
||||
implement and verify your OEMCrypto.
|
||||
|
||||
The CDM uses a "Host" interface to communicate with the upper layers of
|
||||
the system and also to obtain certain services it needs, such as timers
|
||||
and file I/O support. This kit contains a simple implementation of the
|
||||
Host interface. You will create a complete Host interface for each of
|
||||
your target devices.
|
||||
|
||||
Building components
|
||||
|
||||
The easiest way to build is to navigate to the top level directory of
|
||||
the installed kit and run the following script. This does a full build
|
||||
of the linux target and runs wvcdm_shared_api_unittest:
|
||||
|
||||
./run_test.sh
|
||||
|
||||
The top-level build script is build.py. It configures the build
|
||||
environment, runs GYP to generate a series of make files. and starts the
|
||||
top level make. The build scripts are parameterized to build for multiple
|
||||
targets. All the pieces are provided to build for x86-64 (linux). You
|
||||
can use the x86-64 build as a template for building for your embedded targets.
|
||||
The "CDM Porting Guide" supplied with this kit discusses the steps involved.
|
||||
|
||||
Configuring for a new target device
|
||||
|
||||
The kit "out of the box" builds a CDM module and links it into a unit
|
||||
test executable image that can be run on your linux system. To build
|
||||
the CDM for a different target device, you will have to install an appropriate
|
||||
toolchain and SDK for your targets. Then you will have to extend
|
||||
the build scripts to utilize these resources. You will also have to modify
|
||||
or add certain source modules to integrate the CDM into your target's
|
||||
media and/or browser environment. This process is documented in the
|
||||
"CDM Porting Guide".
|
||||
|
||||
Tests
|
||||
|
||||
Read the sections related to testing in the "WV Modular DRM Security
|
||||
Integration Guide for Common Encryption (CENC): EME/Embedded Device
|
||||
Supplement v2.1"
|
||||
|
||||
Test directories can be found in many of the kit's directories. The GYP files
|
||||
for building the test images are found in the test directory or one level
|
||||
above the test directory. The test-releated GYP targets will have "test" or
|
||||
"unittest" in the name.
|
||||
|
||||
Here's a current list of GYP files and test-related targets:
|
||||
|
||||
File - Targets Purpose
|
||||
cdm/cdm.gyp
|
||||
license_protocol Create protobuf sources for license protocol
|
||||
device_files Create protobuf sources for license storage
|
||||
wvcdm_sysdep Build system-dependent layer of CDM
|
||||
wvcdm_shared Build CDM shared library
|
||||
cdm/cdm_unittests.gyp
|
||||
wvcdm_shared_api_unittest Build CDM unit tests
|
||||
third_party/gmock.gyp
|
||||
gmock Provides gmock modules for unit tests
|
||||
gmock_main Provides main routine for unit tests
|
||||
gtest Provides gtest modules for unit tests
|
||||
oemcrypto/oemcrypto.gyp
|
||||
oec_lib Build OEMCrypto library
|
||||
oec_unittest Build OEMCrypto unit tests
|
||||
oemcrypto/mock/oec_mock.gyp
|
||||
oec_mock Build mock OEMCrypto sources
|
||||
oemcrypto/prebuilt/oec_prebuilt.gyp
|
||||
oec_prebuilt Integrate prebuilt OEMCrypto library
|
||||
|
||||
The "CDM Porting Guide" discusses how these targets are used and how you
|
||||
will modify them to build libraries and test binaries for your target devices.
|
||||
|
||||
The "CDM Porting Guide", the "WV Modular DRM Security Integration Guide for
|
||||
Common Encryption (CENC): EME/Embedded Device Supplement v2.1", and the
|
||||
"WV Modular DRM Security Integration Guide for Common Encryption (CENC)"
|
||||
should provide the information you need to create your OEMCrypto and CDM
|
||||
implementations for your target devices.
|
||||
BIN
README.pdf
Normal file
BIN
README.pdf
Normal file
Binary file not shown.
143
README.upgrading
143
README.upgrading
@@ -1,143 +0,0 @@
|
||||
README.upgrading for Widevine CDM Partner Kit v2.2
|
||||
Date: 12/16/2014
|
||||
|
||||
This document provides details on important changes between versions of the
|
||||
Widevine CDM. Some upgrades require you to make changes to your application,
|
||||
so please read carefully.
|
||||
|
||||
NOTE: All gyp variables have default values in platforms/global_config.gypi.
|
||||
You may override these defaults in your platform-specific gypi.
|
||||
|
||||
|
||||
New in v2.2.0:
|
||||
=====
|
||||
New gyp variables have been introduced to make the build more configurable:
|
||||
* privacy_crypto_impl
|
||||
This replaces the variable 'disable_privacy_crypto'. To achieve the same
|
||||
effect, set the value to 'dummy'. Defaults to 'openssl'.
|
||||
|
||||
ContentDecryptionModule_1::GenerateKeyRequest did not previously require the
|
||||
'type' parameter to be supplied. This is now required, and must be set to the
|
||||
content's MIME type.
|
||||
|
||||
ContentDecryptionModule_1::Decrypt* previously required subsamples. This is no
|
||||
longer required. WebM content, for example, does not contain subsamples.
|
||||
|
||||
ContentDecryptionModule_1::Decrypt* did not previously set the size of a
|
||||
decrypted Buffer. This has been fixed.
|
||||
|
||||
New CDM/Host interfaces, CDM_4/Host_4, have been introduced. CDM_1/Host_1 will
|
||||
continue to be supported until the end of Q1, 2015. The new interface includes
|
||||
many fixes and improvements, updates to reflect the evolving EME standard, and
|
||||
the introduction of offline playback APIs.
|
||||
|
||||
|
||||
New in v2.1.6:
|
||||
=====
|
||||
The following methods have been removed from the CDM interface (class
|
||||
ContentDecryptionModule):
|
||||
* CancelKeyRequest
|
||||
|
||||
The following methods have been added to the CDM interface (class
|
||||
ContentDecryptionModule):
|
||||
* CloseSession
|
||||
|
||||
Previous versions of the CDM did not dispatch key errors (Host::SendKeyError)
|
||||
on failure. This has been fixed. If you added workarounds to your Host,
|
||||
please remove them to avoid duplicate key errors.
|
||||
|
||||
|
||||
New in v2.1.5:
|
||||
=====
|
||||
New gyp variables have been introduced to make the build more configurable:
|
||||
* protobuf_lib_type and protobuf_lib
|
||||
These, along with 'protoc_dir', form a new protobuf configuration model
|
||||
and replace the variables 'protobuf_source_dir', 'use_system_protobuf',
|
||||
and 'protobuf_gyp'. For details, see platforms/global_config.gypi.
|
||||
|
||||
|
||||
New in v2.1.3:
|
||||
=====
|
||||
New gyp variables have been introduced to make the build more configurable:
|
||||
* oemcrypto_v8
|
||||
The current version of OEMCrypto is v9. Set to 'true' if your system
|
||||
supplies OEMCrypto v8. Defaults to 'false'.
|
||||
|
||||
The CDM interface (class ContentDecryptionModule) has been simplified.
|
||||
The following methods have been removed:
|
||||
* InitializeAudioDecoder
|
||||
* InitializeVideoDecoder
|
||||
* DeinitializeDecoder
|
||||
* ResetDecoder
|
||||
* DecryptAndDecodeFrame
|
||||
* DecryptAndDecodeSamples
|
||||
|
||||
The Host interface (class Host) has been simplified.
|
||||
The following methods have been removed:
|
||||
* GetPrivateData
|
||||
* PersistPlatformString
|
||||
* GetPlatformByteArray
|
||||
* SetPlatformByteArray
|
||||
* PersistPlatformByteArray
|
||||
|
||||
The following Host methods have changed:
|
||||
* GetPlatformString
|
||||
Now does the job of GetPlatformByteArray as well.
|
||||
* SetPlatformString
|
||||
Now does the job of SetPlatformByteArray, PersistPlatformString, and
|
||||
PersistPlatformByteArray as well.
|
||||
* SetTimer
|
||||
This should schedule a single callback, not a repeating timer. Multiple
|
||||
timer callbacks may be scheduled by the CDM at once. All callbacks should
|
||||
occur serially on the same thread/queue as all other calls into the CDM.
|
||||
|
||||
|
||||
New in v2.1.2:
|
||||
=====
|
||||
The following file locations have changed:
|
||||
|
||||
./cdm/cdm_api_internal.gyp => ./cdm/cdm.gyp
|
||||
./cdm/cdm_api_external.gyp => ./cdm/cdm_unittests.gyp
|
||||
|
||||
If you have your own gyp files which depend on CDM targets, please update them
|
||||
accordingly.
|
||||
|
||||
./build.py will use the ninja build tool if available. Otherwise, it will
|
||||
continue to use make.
|
||||
|
||||
The Host is no longer expected to allocate a Buffer before calling
|
||||
ContentDecryptionModule::Decrypt. The CDM will now call Host::Allocate and
|
||||
DecryptedBlock::SetBuffer as needed in its Decrypt method. To avoid the
|
||||
potential for a memory leak, make sure that your Host implementation no
|
||||
longer allocates a Buffer before calling Decrypt.
|
||||
|
||||
For more details on the Host interface, see "Widevine Security Integration
|
||||
Guide for CENC: EME Supplement".
|
||||
|
||||
|
||||
New in v2.1.1:
|
||||
=====
|
||||
The following file locations have changed:
|
||||
|
||||
./build/build.py => ./build.py
|
||||
./build/platforms/ => ./platforms/
|
||||
./build/global_config.gypi => ./platforms/global_config.gypi
|
||||
./build/protoc.gypi => ./third_party/protoc.gypi
|
||||
|
||||
The file ./build/platforms/cdm_platforms.py no longer exists. Platform
|
||||
definitions are now based on the folder structure in ./platforms rather than
|
||||
a python-based config file.
|
||||
|
||||
The OEMCrypto interface has been updated to v9, and documentation has been
|
||||
updated in oemcrypto/include. The following functions were added:
|
||||
|
||||
* OEMCrypto_GetHDCPCapability
|
||||
* OEMCrypto_SupportsUsageTable
|
||||
* OEMCrypto_UpdateUsageTable
|
||||
* OEMCrypto_DeactivateUsageEntry
|
||||
* OEMCrypto_ReportUsage
|
||||
* OEMCrypto_DeleteUsageEntry
|
||||
* OEMCrypto_DeleteUsageTable
|
||||
|
||||
The file ./platforms/x86-64/x86-64.gypi now contains examples of how settings
|
||||
can be customized for a given platform.
|
||||
15
build.py
15
build.py
@@ -3,14 +3,17 @@
|
||||
"""Generates build files and builds the CDM source release."""
|
||||
|
||||
import argparse
|
||||
import imp
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import gyp
|
||||
|
||||
cdm_top = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# If gyp has been installed locally in third_party, this will find it.
|
||||
# Irrelevant if gyp has been installed globally.
|
||||
sys.path.insert(1, os.path.join(cdm_top, 'third_party'))
|
||||
import gyp
|
||||
|
||||
def IsNinjaInstalled():
|
||||
"""Determine if ninja is installed."""
|
||||
@@ -74,16 +77,14 @@ def ImportPlatform(name, gyp_args):
|
||||
|
||||
platforms_path = os.path.join(cdm_top, 'platforms')
|
||||
target_path = os.path.join(platforms_path, name)
|
||||
platform_gypi_path = os.path.join(target_path, name + '.gypi')
|
||||
global_gypi_path = os.path.join(platforms_path, 'global_config.gypi')
|
||||
platform_gypi_path = os.path.join(target_path, 'settings.gypi')
|
||||
output_path = os.path.join(cdm_top, 'out', name)
|
||||
|
||||
gyp_args.append('--include=%s' % platform_gypi_path)
|
||||
gyp_args.append('--include=%s' % global_gypi_path)
|
||||
gyp_args.append('-Goutput_dir=%s' % output_path)
|
||||
|
||||
sys.path.insert(0, target_path)
|
||||
target = __import__(name)
|
||||
platform_environment_path = os.path.join(target_path, 'environment.py')
|
||||
target = imp.load_source('environment', platform_environment_path)
|
||||
|
||||
if hasattr(target, 'export_variables'):
|
||||
for k, v in target.export_variables.iteritems():
|
||||
|
||||
179
cdm/cdm.gyp
179
cdm/cdm.gyp
@@ -1,14 +1,49 @@
|
||||
# Copyright 2013 Google Inc. All Rights Reserved.
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Refer to the distribution package's README for information about
|
||||
# setting up your system, performing the build, and using/testing
|
||||
# the build targets.
|
||||
|
||||
{
|
||||
'variables': {
|
||||
# Override if you intend to link against a different OEMCrypto API version.
|
||||
'oemcrypto_version%': 10,
|
||||
|
||||
# Override if you can't depend on OpenSSL for privacy features.
|
||||
# If set to 'dummy', privacy mode in the CDM will fail.
|
||||
'privacy_crypto_impl%': 'openssl',
|
||||
|
||||
# There are three protobuf configurations:
|
||||
#
|
||||
# 1) protobuf_config == 'system'
|
||||
# Use a system-wide installation of protobuf.
|
||||
# Specify the protobuf library in protobuf_lib.
|
||||
# Specify the path to protoc in protoc_bin.
|
||||
#
|
||||
# 2) protobuf_config == 'target'
|
||||
# Use an existing protobuf gyp target from your project.
|
||||
# Specify the protobuf gyp file and target in protobuf_lib_target.
|
||||
# Specify the protoc gyp file and target in protoc_host_target.
|
||||
# Specify the path to protoc in protoc_bin.
|
||||
#
|
||||
# 3) protobuf_config == 'source' (default)
|
||||
# Build protobuf and protoc from source.
|
||||
# Specify the path to the protobuf source in protobuf_source.
|
||||
# Make sure that a valid config.h for your target is in the source tree.
|
||||
'protobuf_config%': 'source',
|
||||
'protobuf_source%': '../third_party/protobuf',
|
||||
}, # variables
|
||||
'conditions': [
|
||||
['protobuf_config=="source"', {
|
||||
# Include protobuf targets used by protobuf_config=='source'
|
||||
'includes': ['../third_party/protobuf.gypi'],
|
||||
}],
|
||||
], # conditions
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'license_protocol',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'sources': ['../core/src/license_protocol.proto',],
|
||||
'variables': {
|
||||
'proto_in_dir': '../core/src',
|
||||
@@ -18,6 +53,7 @@
|
||||
{
|
||||
'target_name': 'device_files',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'sources': ['../core/src/device_files.proto',],
|
||||
'variables': {
|
||||
'proto_in_dir': '../core/src',
|
||||
@@ -25,54 +61,49 @@
|
||||
'includes': ['../third_party/protoc.gypi'],
|
||||
},
|
||||
{
|
||||
'target_name': 'wvcdm_sysdep',
|
||||
'target_name': 'widevine_cdm_core',
|
||||
'type': 'static_library',
|
||||
'defines': ['CDM_IMPLEMENTATION'],
|
||||
'include_dirs': [
|
||||
'../cdm/include',
|
||||
'../core/include',
|
||||
'../linux/include',
|
||||
'../third_party/stringencoders/src',
|
||||
],
|
||||
'sources': [
|
||||
'../cdm/src/host_4_file_io_client.cpp',
|
||||
'../cdm/src/file_store.cpp',
|
||||
'../cdm/src/properties_common.cpp',
|
||||
'../core/src/string_conversions.cpp',
|
||||
'../linux/src/lock.cpp',
|
||||
'../linux/src/log.cpp',
|
||||
'../third_party/stringencoders/src/modp_b64w.cpp',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'wvcdm_static',
|
||||
'type': 'static_library',
|
||||
'defines': ['CDM_IMPLEMENTATION'],
|
||||
'standalone_static_library': 1,
|
||||
'dependencies': [
|
||||
'device_files',
|
||||
'license_protocol',
|
||||
'wvcdm_sysdep',
|
||||
'<(oemcrypto_target)',
|
||||
],
|
||||
# Without this, library deps do not propagate from the protocol targets
|
||||
# up to the shared lib or executable above.
|
||||
'export_dependent_settings': [
|
||||
'device_files',
|
||||
'license_protocol',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../cdm/include',
|
||||
'../core/include',
|
||||
'../linux/include',
|
||||
'../oemcrypto/include',
|
||||
'../third_party/stringencoders/src',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../core/include',
|
||||
'../oemcrypto/include',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
# uses common published api
|
||||
'../cdm/src/cdm_library_init.cpp',
|
||||
'../cdm/src/clock.cpp',
|
||||
'../cdm/src/host_event_listener.cpp',
|
||||
'../cdm/src/wv_content_decryption_module_1.cpp',
|
||||
'../cdm/src/wv_content_decryption_module_4.cpp',
|
||||
'../core/include/buffer_reader.h',
|
||||
'../core/include/cdm_client_property_set.h',
|
||||
'../core/include/cdm_engine.h',
|
||||
'../core/include/cdm_session.h',
|
||||
'../core/include/certificate_provisioning.h',
|
||||
'../core/include/clock.h',
|
||||
'../core/include/crypto_key.h',
|
||||
'../core/include/crypto_session.h',
|
||||
'../core/include/device_files.h',
|
||||
'../core/include/file_store.h',
|
||||
'../core/include/initialization_data.h',
|
||||
'../core/include/license.h',
|
||||
'../core/include/lock.h',
|
||||
'../core/include/log.h',
|
||||
'../core/include/max_res_engine.h',
|
||||
'../core/include/oemcrypto_adapter.h',
|
||||
'../core/include/policy_engine.h',
|
||||
'../core/include/privacy_crypto.h',
|
||||
'../core/include/properties.h',
|
||||
'../core/include/scoped_ptr.h',
|
||||
'../core/include/string_conversions.h',
|
||||
'../core/include/wv_cdm_constants.h',
|
||||
'../core/include/wv_cdm_event_listener.h',
|
||||
'../core/include/wv_cdm_types.h',
|
||||
'../core/src/buffer_reader.cpp',
|
||||
'../core/src/cdm_engine.cpp',
|
||||
'../core/src/cdm_session.cpp',
|
||||
@@ -81,32 +112,80 @@
|
||||
'../core/src/device_files.cpp',
|
||||
'../core/src/initialization_data.cpp',
|
||||
'../core/src/license.cpp',
|
||||
'../core/src/max_res_engine.cpp',
|
||||
'../core/src/oemcrypto_adapter_static.cpp',
|
||||
'../core/src/policy_engine.cpp',
|
||||
'../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
|
||||
'../core/src/properties.cpp',
|
||||
'../core/src/string_conversions.cpp',
|
||||
'../third_party/stringencoders/src/modp_b64w_data.h',
|
||||
'../third_party/stringencoders/src/modp_b64w.cpp',
|
||||
'../third_party/stringencoders/src/modp_b64w.h',
|
||||
],
|
||||
'conditions': [
|
||||
['oemcrypto_v8=="true"', {
|
||||
'sources!': [ # exclude
|
||||
'../core/src/oemcrypto_adapter_static.cpp',
|
||||
['oemcrypto_version < 9', {
|
||||
'sources': [
|
||||
# Include APIs introduced in v9.
|
||||
'../core/src/oemcrypto_adapter_static_v9.cpp',
|
||||
],
|
||||
'sources': [ # include
|
||||
'../core/src/oemcrypto_adapter_static_v8.cpp',
|
||||
}],
|
||||
['oemcrypto_version < 10', {
|
||||
'sources': [
|
||||
# Include APIs introduced in v10.
|
||||
'../core/src/oemcrypto_adapter_static_v10.cpp',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'wvcdm_shared',
|
||||
'type': 'shared_library',
|
||||
'target_name': 'widevine_ce_cdm_static',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'dependencies': [
|
||||
'wvcdm_static',
|
||||
'widevine_cdm_core',
|
||||
'device_files',
|
||||
'license_protocol',
|
||||
],
|
||||
# Without this, library deps do not propagate from the protocol targets
|
||||
# up to the shared lib or executable above.
|
||||
'export_dependent_settings': [
|
||||
'device_files',
|
||||
'license_protocol',
|
||||
],
|
||||
'defines': ['CDM_IMPLEMENTATION'],
|
||||
'include_dirs': [
|
||||
'include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'include',
|
||||
],
|
||||
'libraries': [
|
||||
'-lpthread',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
'include/cdm.h',
|
||||
'include/cdm_version.h',
|
||||
'include/override.h',
|
||||
'include/properties_ce.h',
|
||||
'src/cdm.cpp',
|
||||
'src/lock.cpp',
|
||||
'src/log.cpp',
|
||||
'src/properties_ce.cpp',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'dummy',
|
||||
'type': 'none',
|
||||
}
|
||||
'target_name': 'widevine_ce_cdm_shared',
|
||||
'type': 'shared_library',
|
||||
'dependencies': [
|
||||
'widevine_ce_cdm_static',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'include',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,51 +1,50 @@
|
||||
# Copyright 2013 Google Inc. All Rights Reserved.
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Builds under the CDM ./build.py (target platform) build system
|
||||
# Refer to the distribution package's README for details.
|
||||
#
|
||||
{
|
||||
'variables': {
|
||||
'oemcrypto_lib%': '',
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'wvcdm_shared_api_unittest',
|
||||
'target_name': 'widevine_ce_cdm_unittest',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'../cdm/test/cdm_api_1_test.cpp',
|
||||
'../cdm/test/cdm_api_4_test.cpp',
|
||||
'../cdm/test/cdm_test_main.cpp',
|
||||
'../cdm/test/device_cert.cpp',
|
||||
'../cdm/test/device_cert.h',
|
||||
'../cdm/test/test_host_1.cpp',
|
||||
'../cdm/test/test_host_4.cpp',
|
||||
'../cdm/test/test_host_4_file_io.cpp',
|
||||
'../cdm/test/test_util.cpp',
|
||||
'../core/test/config_test_env.cpp',
|
||||
'../core/test/license_request.cpp',
|
||||
'../core/test/http_socket.cpp',
|
||||
'../core/test/test_printers.cpp',
|
||||
'../core/test/url_request.cpp',
|
||||
# The test runner and the testing device certificate.
|
||||
'test/cdm_test_main.cpp',
|
||||
'test/device_cert.cpp',
|
||||
'test/device_cert.h',
|
||||
# The test host, which is required for all test suites on CE.
|
||||
'test/test_host.cpp',
|
||||
'test/test_host.h',
|
||||
],
|
||||
'include_dirs': [
|
||||
'include',
|
||||
'../cdm/include',
|
||||
'../core/include',
|
||||
'../core/test',
|
||||
'../oemcrypto/include',
|
||||
],
|
||||
'defines': [
|
||||
'UNIT_TEST',
|
||||
'includes': [
|
||||
'oemcrypto_unittests.gypi',
|
||||
'core_unittests.gypi',
|
||||
'cdm_unittests.gypi',
|
||||
],
|
||||
'libraries': [
|
||||
'-lssl',
|
||||
'-lcrypto',
|
||||
'-lpthread',
|
||||
'-lcrypto', # oec_mock
|
||||
'-lssl', # oec_mock
|
||||
'-lpthread', # gtest
|
||||
],
|
||||
'dependencies': [
|
||||
'cdm.gyp:wvcdm_shared',
|
||||
'cdm.gyp:widevine_ce_cdm_shared',
|
||||
'../third_party/gmock.gyp:gmock',
|
||||
'../third_party/gmock.gyp:gmock_main',
|
||||
'../third_party/gmock.gyp:gtest',
|
||||
],
|
||||
'conditions': [
|
||||
['oemcrypto_lib==""', {
|
||||
'dependencies': [
|
||||
'../oemcrypto/mock/oec_mock.gyp:oec_mock',
|
||||
],
|
||||
}, {
|
||||
'libraries': [
|
||||
'<(oemcrypto_lib)',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
24
cdm/cdm_unittests.gypi
Normal file
24
cdm/cdm_unittests.gypi
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Include this in any custom unit test targets.
|
||||
# Does not include the device certificate or the test runner main.
|
||||
{
|
||||
'sources': [
|
||||
'../core/test/http_socket.cpp',
|
||||
'../core/test/license_request.cpp',
|
||||
'../core/test/url_request.cpp',
|
||||
|
||||
'test/cdm_test.cpp',
|
||||
'test/cdm_test_printers.cpp',
|
||||
'test/cdm_test_printers.h',
|
||||
'test/test_host.cpp',
|
||||
'test/test_host.h',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../core/test',
|
||||
],
|
||||
'defines': [
|
||||
'UNIT_TEST',
|
||||
'CDM_TESTS',
|
||||
],
|
||||
}
|
||||
32
cdm/core_unittests.gypi
Normal file
32
cdm/core_unittests.gypi
Normal file
@@ -0,0 +1,32 @@
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Include this in any custom unit test targets.
|
||||
# Does not include the test runner main.
|
||||
{
|
||||
'sources': [
|
||||
'../core/test/base64_test.cpp',
|
||||
'../core/test/cdm_engine_test.cpp',
|
||||
'../core/test/cdm_session_unittest.cpp',
|
||||
'../core/test/config_test_env.cpp',
|
||||
'../core/test/device_files_unittest.cpp',
|
||||
'../core/test/http_socket.cpp',
|
||||
'../core/test/initialization_data_unittest.cpp',
|
||||
'../core/test/license_request.cpp',
|
||||
'../core/test/license_unittest.cpp',
|
||||
'../core/test/max_res_engine_unittest.cpp',
|
||||
'../core/test/policy_engine_unittest.cpp',
|
||||
'../core/test/test_printers.cpp',
|
||||
'../core/test/url_request.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../core/include',
|
||||
'../core/test',
|
||||
],
|
||||
'defines': [
|
||||
'UNIT_TEST',
|
||||
'CORE_TESTS',
|
||||
],
|
||||
'dependencies': [
|
||||
'cdm.gyp:license_protocol',
|
||||
],
|
||||
}
|
||||
434
cdm/include/cdm.h
Normal file
434
cdm/include/cdm.h
Normal file
@@ -0,0 +1,434 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
// Based on the EME draft spec from 2015 June 01.
|
||||
// https://rawgit.com/w3c/encrypted-media/1cbedad/index.html
|
||||
// TODO: Verify behavior and update to June 12 draft.
|
||||
#ifndef WVCDM_CDM_CDM_H_
|
||||
#define WVCDM_CDM_CDM_H_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
// Define CDM_EXPORT to export functionality across shared library boundaries.
|
||||
#if defined(WIN32)
|
||||
# if defined(CDM_IMPLEMENTATION)
|
||||
# define CDM_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define CDM_EXPORT __declspec(dllimport)
|
||||
# endif // defined(CDM_IMPLEMENTATION)
|
||||
#else // defined(WIN32)
|
||||
# if defined(CDM_IMPLEMENTATION)
|
||||
# define CDM_EXPORT __attribute__((visibility("default")))
|
||||
# else
|
||||
# define CDM_EXPORT
|
||||
# endif
|
||||
#endif // defined(WIN32)
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class CDM_EXPORT Cdm {
|
||||
public:
|
||||
// Session types defined by EME.
|
||||
typedef enum {
|
||||
kTemporary = 0,
|
||||
kPersistent = 1,
|
||||
} SessionType;
|
||||
|
||||
// Message types defined by EME.
|
||||
typedef enum {
|
||||
kLicenseRequest = 0,
|
||||
kLicenseRenewal = 1,
|
||||
kLicenseRelease = 2,
|
||||
kIndividualizationRequest = 3,
|
||||
} MessageType;
|
||||
|
||||
typedef enum {
|
||||
// These are defined by Widevine:
|
||||
kSuccess = 0,
|
||||
kNeedsDeviceCertificate = 1,
|
||||
kSessionNotFound = 2,
|
||||
kDecryptError = 3,
|
||||
kNoKey = 4,
|
||||
// These are analogous to the errors used by EME:
|
||||
kInvalidAccess = 5,
|
||||
kNotSupported = 6,
|
||||
kInvalidState = 7,
|
||||
kQuotaExceeded = 8,
|
||||
// This covers errors that we do not expect (see logs for details):
|
||||
kUnexpectedError = 99999,
|
||||
} Status;
|
||||
|
||||
// These are the init data types defined by EME.
|
||||
typedef enum {
|
||||
kCenc = 0,
|
||||
kKeyIds = 1, // NOTE: not supported by Widevine at this time
|
||||
kWebM = 2,
|
||||
} InitDataType;
|
||||
|
||||
// These are key statuses defined by EME.
|
||||
typedef enum {
|
||||
kUsable = 0,
|
||||
kExpired = 1,
|
||||
kOutputNotAllowed = 2,
|
||||
kStatusPending = 3,
|
||||
kInternalError = 4,
|
||||
} KeyStatus;
|
||||
|
||||
// These are defined by Widevine. The CDM can be configured to decrypt in
|
||||
// three modes (dependent on OEMCrypto support).
|
||||
typedef enum {
|
||||
// Data is decrypted to an opaque handle.
|
||||
// Translates to OEMCrypto's OEMCrypto_BufferType_Secure.
|
||||
kOpaqueHandle = 0,
|
||||
|
||||
// Decrypted data never returned to the caller, but is decoded and rendered
|
||||
// by OEMCrypto.
|
||||
// Translates to OEMCrypto's OEMCrypto_BufferType_Direct.
|
||||
kDirectRender = 1,
|
||||
|
||||
// There is no secure output available, so all data is decrypted into a
|
||||
// clear buffer in main memory.
|
||||
// Translates to OEMCrypto's OEMCrypto_BufferType_Clear.
|
||||
kNoSecureOutput = 2,
|
||||
} SecureOutputType;
|
||||
|
||||
// Logging levels defined by Widevine.
|
||||
// See Cdm::initialize().
|
||||
typedef enum {
|
||||
kSilent = -1,
|
||||
kErrors = 0,
|
||||
kWarnings = 1,
|
||||
kInfo = 2,
|
||||
kDebug = 3,
|
||||
kVerbose = 4,
|
||||
} LogLevel;
|
||||
|
||||
// A map of key statuses.
|
||||
// See Cdm::getKeyStatuses().
|
||||
typedef std::map<std::string, KeyStatus> KeyStatusMap;
|
||||
|
||||
// An event listener interface provided by the application and attached to
|
||||
// each CDM session.
|
||||
// See Cdm::createSession().
|
||||
class IEventListener {
|
||||
public:
|
||||
// A message (license request, renewal, etc.) to be dispatched to the
|
||||
// application's license server.
|
||||
// The response, if successful, should be provided back to the CDM via a
|
||||
// call to Cdm::update().
|
||||
virtual void onMessage(const std::string& session_id,
|
||||
MessageType message_type,
|
||||
const std::string& message) = 0;
|
||||
|
||||
// There has been a change in the keys in the session or their status.
|
||||
virtual void onKeyStatusesChange(const std::string& session_id) = 0;
|
||||
|
||||
// A remove() operation has been completed.
|
||||
virtual void onRemoveComplete(const std::string& session_id) = 0;
|
||||
|
||||
protected:
|
||||
IEventListener() {}
|
||||
virtual ~IEventListener() {}
|
||||
};
|
||||
|
||||
// A storage interface provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
// NOTE: It is important for users of your application to be able to clear
|
||||
// stored data. Also, browsers or other multi-application systems should
|
||||
// store data separately per-app or per-origin.
|
||||
// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
|
||||
class IStorage {
|
||||
public:
|
||||
virtual bool read(const std::string& name,
|
||||
std::string* data) = 0;
|
||||
virtual bool write(const std::string& name,
|
||||
const std::string& data) = 0;
|
||||
virtual bool exists(const std::string& name) = 0;
|
||||
virtual bool remove(const std::string& name) = 0;
|
||||
virtual int32_t size(const std::string& name) = 0;
|
||||
|
||||
protected:
|
||||
IStorage() {}
|
||||
virtual ~IStorage() {}
|
||||
};
|
||||
|
||||
// A clock interface provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
class IClock {
|
||||
public:
|
||||
// Returns the current time in milliseconds since 1970 UTC.
|
||||
virtual int64_t now() = 0;
|
||||
|
||||
protected:
|
||||
IClock() {}
|
||||
virtual ~IClock() {}
|
||||
};
|
||||
|
||||
// A timer interface provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
class ITimer {
|
||||
public:
|
||||
class IClient {
|
||||
public:
|
||||
// Called by ITimer when a timer expires.
|
||||
virtual void onTimerExpired(void* context) = 0;
|
||||
|
||||
protected:
|
||||
IClient() {}
|
||||
virtual ~IClient() {}
|
||||
};
|
||||
|
||||
// Call |client->onTimerExpired(context)| after a delay of |delay_ms| ms.
|
||||
virtual void setTimeout(int64_t delay_ms,
|
||||
IClient* client,
|
||||
void* context) = 0;
|
||||
|
||||
protected:
|
||||
ITimer() {}
|
||||
virtual ~ITimer() {}
|
||||
};
|
||||
|
||||
// Client information, provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
// These parameters end up as client indentification in license requests.
|
||||
// All fields may be used by a license server proxy to drive business logic.
|
||||
// Some fields are required (indicated below), but please fill out as many
|
||||
// as make sense for your application.
|
||||
// No user-identifying information may be put in these fields!
|
||||
struct ClientInfo {
|
||||
// The name of the product or application, e.g. "TurtleTube"
|
||||
// Required.
|
||||
std::string product_name;
|
||||
|
||||
// The name of the company who makes the device, e.g. "Kubrick, Inc."
|
||||
// Required.
|
||||
std::string company_name;
|
||||
|
||||
// The name of the device, e.g. "HAL"
|
||||
std::string device_name;
|
||||
|
||||
// The device model, e.g. "HAL 9000"
|
||||
// Required.
|
||||
std::string model_name;
|
||||
|
||||
// The architecture of the device, e.g. "x86-64"
|
||||
std::string arch_name;
|
||||
|
||||
// Information about the build of the browser, application, or platform into
|
||||
// which the CDM is integrated, e.g. "v2.71828, 2038-01-19-03:14:07"
|
||||
std::string build_info;
|
||||
};
|
||||
|
||||
// Device certificate request information.
|
||||
// The structure is passed by the application to the library in as an output
|
||||
// parameter to Cdm::initialize().
|
||||
// All fields are filled in by the library to instruct the application to
|
||||
// handle device certificate requests, if needed.
|
||||
struct DeviceCertificateRequest {
|
||||
// If false, the library is ready to create and/or load sessions.
|
||||
// If true, a device certificate is needed first.
|
||||
// Sessions cannot be created or loaded until the device certificate has
|
||||
// been provisioned.
|
||||
bool needed;
|
||||
|
||||
// If |needed| is true, this string contains the URL that must be used to
|
||||
// provision a device certificate. The request must be a POST.
|
||||
std::string url;
|
||||
|
||||
// If |needed| is true, the response from the above-described HTTP POST
|
||||
// must be provided as an argument to this method.
|
||||
// Returns kSuccess if the provisioning was successful.
|
||||
// Any other return value means the provisioning failed and the CDM cannot
|
||||
// be used yet.
|
||||
Status acceptReply(const std::string& reply);
|
||||
};
|
||||
|
||||
// Initialize the CDM library and provide access to platform services.
|
||||
// All platform interfaces are required.
|
||||
// The |device_certificate_request| parameter will be filled in by
|
||||
// initialize().
|
||||
// See documentation for DeviceCertificateRequest for more information.
|
||||
// Logging is controlled by |verbosity|.
|
||||
// Must be called and must return kSuccess before create() is called.
|
||||
static Status initialize(
|
||||
SecureOutputType secure_output_type,
|
||||
const ClientInfo& client_info,
|
||||
IStorage* storage,
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
DeviceCertificateRequest* device_certificate_request,
|
||||
LogLevel verbosity);
|
||||
|
||||
// Query the CDM library version.
|
||||
static const char* version();
|
||||
|
||||
// Constructs a new CDM instance.
|
||||
// initialize() must be called first and must return kSuccess before a CDM
|
||||
// instance may be constructed.
|
||||
// The CDM may notify of events at any time via the provided |listener|,
|
||||
// which may not be NULL.
|
||||
// If |privacy_mode| is true, server certificates are required and will be
|
||||
// used to encrypt messages to the license server.
|
||||
// By using server certificates to encrypt communication with the license
|
||||
// server, device-identifying information cannot be extracted from the
|
||||
// license exchange process by an intermediate layer between the CDM and
|
||||
// the server.
|
||||
// This is particularly useful for browser environments, but is recommended
|
||||
// for use whenever possible.
|
||||
static Cdm* create(IEventListener* listener,
|
||||
bool privacy_mode);
|
||||
|
||||
virtual ~Cdm() {}
|
||||
|
||||
// Provides a server certificate to be used to encrypt messages to the
|
||||
// license server.
|
||||
// If |privacy_mode| was true in create() and setServerCertificate() is not
|
||||
// called, the CDM will attempt to provision a server certificate through
|
||||
// IEventListener::onMessage() with messageType == kIndividualizationRequest.
|
||||
// May not be called if |privacy_mode| was false.
|
||||
virtual Status setServerCertificate(const std::string& certificate) = 0;
|
||||
|
||||
// Creates a new session.
|
||||
// Do not use this to load an existing persistent session.
|
||||
// If successful, the session_id is returned via |sessionId|.
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) = 0;
|
||||
|
||||
// Generates a request based on the initData.
|
||||
// The request will be provided via a synchronous call to
|
||||
// IEventListener::onMessage().
|
||||
// This is done so that license requests and renewals follow the same flow.
|
||||
virtual Status generateRequest(const std::string& session_id,
|
||||
InitDataType init_data_type,
|
||||
const std::string& init_data) = 0;
|
||||
|
||||
// Loads an existing persisted session from storage.
|
||||
virtual Status load(const std::string& session_id) = 0;
|
||||
|
||||
// Provides messages, including licenses, to the CDM.
|
||||
// If the message is a successful response to a release message, stored
|
||||
// session data will be removed for the session.
|
||||
virtual Status update(const std::string& session_id,
|
||||
const std::string& response) = 0;
|
||||
|
||||
// The time, in milliseconds since 1970 UTC, after which the key(s) in the
|
||||
// session will no longer be usable to decrypt media data, or -1 if no such
|
||||
// time exists.
|
||||
virtual Status getExpiration(const std::string& session_id,
|
||||
int64_t* expiration) = 0;
|
||||
|
||||
// A map of known key IDs to the current status of the associated key.
|
||||
virtual Status getKeyStatuses(const std::string& session_id,
|
||||
KeyStatusMap* key_statuses) = 0;
|
||||
|
||||
// Indicates that the application no longer needs the session and the CDM
|
||||
// should release any resources associated with it and close it.
|
||||
// Does not generate release messages for persistent sessions.
|
||||
// Does not remove stored session data for persistent sessions.
|
||||
virtual Status close(const std::string& session_id) = 0;
|
||||
|
||||
// Removes stored session data associated with the session.
|
||||
// The session must be loaded before it can be removed.
|
||||
// Generates release messages, which must be delivered to the license server.
|
||||
// A reply from the license server must be provided via update() before the
|
||||
// session is fully removed.
|
||||
virtual Status remove(const std::string& session_id) = 0;
|
||||
|
||||
struct InputBuffer {
|
||||
public:
|
||||
InputBuffer()
|
||||
: key_id(NULL),
|
||||
key_id_length(0),
|
||||
iv(NULL),
|
||||
iv_length(0),
|
||||
data(NULL),
|
||||
data_length(0),
|
||||
block_offset(0),
|
||||
is_encrypted(true),
|
||||
is_video(true),
|
||||
first_subsample(true),
|
||||
last_subsample(true) {}
|
||||
|
||||
const uint8_t* key_id;
|
||||
uint32_t key_id_length;
|
||||
|
||||
// The IV is expected to be 16 bytes.
|
||||
const uint8_t* iv;
|
||||
uint32_t iv_length;
|
||||
|
||||
const uint8_t* data;
|
||||
uint32_t data_length;
|
||||
|
||||
// |data|'s offset within its 16-byte AES block, used for CENC subsamples.
|
||||
// Should start at 0 for each sample, then go up by |data_length| (mod 16)
|
||||
// after the |is_encrypted| part of each subsample.
|
||||
uint32_t block_offset;
|
||||
|
||||
// If false, copies the input data directly to the output buffer. Used for
|
||||
// secure output types, where the output buffer cannot be directly accessed
|
||||
// above the CDM.
|
||||
bool is_encrypted;
|
||||
|
||||
// Used by secure output type kDirectRender, where the secure hardware must
|
||||
// decode and render the decrypted content:
|
||||
bool is_video;
|
||||
bool first_subsample;
|
||||
bool last_subsample;
|
||||
};
|
||||
|
||||
struct OutputBuffer {
|
||||
OutputBuffer()
|
||||
: data(NULL),
|
||||
data_length(0),
|
||||
data_offset(0),
|
||||
is_secure(false) {}
|
||||
|
||||
// If |is_secure| is false or the secure output type is kNoSecureOutput,
|
||||
// this is a memory address in main memory.
|
||||
// If |is_secure| is true and the secure output type is kOpaqueHandle,
|
||||
// this is an opaque handle.
|
||||
// If |is_secure| is true and the secure output type is kDirectRender,
|
||||
// this is ignored.
|
||||
// See also SecureOutputType argument to initialize().
|
||||
uint8_t* data;
|
||||
|
||||
// The maximum amount of data that can be decrypted to the buffer in this
|
||||
// call, starting from |data|.
|
||||
// Must be at least as large as the input buffer's |data_length|.
|
||||
// This size accounts for the bytes that will be skipped by |data_offset|.
|
||||
uint32_t data_length;
|
||||
|
||||
// An offset applied to the output address.
|
||||
// Useful when |data| is an opaque handle rather than an address.
|
||||
uint32_t data_offset;
|
||||
|
||||
// False for clear buffers, true otherwise.
|
||||
// Must be false if the secure output type is kNoSecureOutput.
|
||||
// See also SecureOutputType argument to initialize().
|
||||
bool is_secure;
|
||||
};
|
||||
|
||||
// Decrypt the input as described by |input| and pass the output as described
|
||||
// in |output|.
|
||||
virtual Status decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) = 0;
|
||||
|
||||
protected:
|
||||
Cdm() {}
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WVCDM_CDM_CDM_H_
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_CDM_HOST_CLOCK_H_
|
||||
#define WVCDM_CDM_CDM_HOST_CLOCK_H_
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class IClock {
|
||||
public:
|
||||
IClock(){}
|
||||
virtual ~IClock();
|
||||
virtual int64_t GetCurrentTimeInSeconds() = 0;
|
||||
};
|
||||
|
||||
class HostClock {
|
||||
friend class Clock;
|
||||
friend class IClock;
|
||||
public:
|
||||
static void SetClockInterface(IClock* iclock);
|
||||
int64_t GetCurrentTimeInSeconds();
|
||||
private:
|
||||
static IClock* impl_;
|
||||
};
|
||||
|
||||
} // namspace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_CDM_HOST_CLOCK_H_
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_CDM_HOST_FILE_H_
|
||||
#define WVCDM_CDM_CDM_HOST_FILE_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "file_store.h"
|
||||
#include "host_4_file_io_client.h"
|
||||
#include "scoped_ptr.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class IFileFactory;
|
||||
|
||||
class IHostFile {
|
||||
public:
|
||||
virtual ~IHostFile() {}
|
||||
virtual bool Open(const std::string& name) = 0;
|
||||
virtual ssize_t Read(char* buffer, size_t bytes) = 0;
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes) = 0;
|
||||
virtual bool Close() = 0;
|
||||
virtual bool Remove(const std::string& name) = 0;
|
||||
virtual ssize_t FileSize(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
class File1Impl : public IHostFile {
|
||||
public:
|
||||
explicit File1Impl(cdm::Host_1* const host_1) : host_1_(host_1) {}
|
||||
virtual ~File1Impl() {}
|
||||
virtual bool Open(const std::string& name) OVERRIDE;
|
||||
virtual ssize_t Read(char* buffer, size_t bytes) OVERRIDE;
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes) OVERRIDE;
|
||||
virtual bool Close() OVERRIDE;
|
||||
virtual bool Remove(const std::string& name) OVERRIDE;
|
||||
virtual ssize_t FileSize(const std::string& name) OVERRIDE;
|
||||
private:
|
||||
cdm::Host_1* const host_1_;
|
||||
std::string fname_;
|
||||
};
|
||||
|
||||
class File4Impl : public IHostFile {
|
||||
public:
|
||||
explicit File4Impl(cdm::Host_4* const host_4) :
|
||||
host_4_(host_4), host_4_file_io_client_(host_4) {}
|
||||
virtual ~File4Impl() {}
|
||||
virtual bool Open(const std::string& name) OVERRIDE;
|
||||
virtual ssize_t Read(char* buffer, size_t bytes) OVERRIDE;
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes) OVERRIDE;
|
||||
virtual bool Close() OVERRIDE;
|
||||
virtual bool Remove(const std::string& name) OVERRIDE;
|
||||
virtual ssize_t FileSize(const std::string& name) OVERRIDE;
|
||||
private:
|
||||
cdm::Host_4* const host_4_;
|
||||
Host4FileIOClient host_4_file_io_client_;
|
||||
};
|
||||
|
||||
class File::Impl {
|
||||
public:
|
||||
explicit Impl(cdm::Host_1* const host_1) :
|
||||
file_api_(new File1Impl(host_1)) {}
|
||||
|
||||
explicit Impl(cdm::Host_4* const host_4) :
|
||||
file_api_(new File4Impl(host_4)) {}
|
||||
|
||||
virtual ~Impl() {}
|
||||
|
||||
static void RegisterFileFactory(IFileFactory* factory) { factory_ = factory; }
|
||||
|
||||
virtual bool Open(const std::string& name);
|
||||
virtual ssize_t Read(char* buffer, size_t bytes);
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes);
|
||||
virtual bool Close();
|
||||
virtual bool Exists(const std::string& name);
|
||||
virtual bool Remove(const std::string& name);
|
||||
virtual ssize_t FileSize(const std::string& name);
|
||||
|
||||
private:
|
||||
static IFileFactory* factory_;
|
||||
friend class File;
|
||||
|
||||
scoped_ptr<IHostFile> file_api_;
|
||||
};
|
||||
|
||||
class IFileFactory {
|
||||
protected:
|
||||
IFileFactory() { File::Impl::RegisterFileFactory(this); }
|
||||
|
||||
virtual ~IFileFactory() {}
|
||||
|
||||
public:
|
||||
virtual File::Impl* NewFileImpl() = 0;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_CDM_HOST_FILE_H_
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_CDM_HOST_TIMER_H_
|
||||
#define WVCDM_CDM_CDM_HOST_TIMER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace wvcdm {
|
||||
class ITimerFactory {
|
||||
public:
|
||||
virtual Timer::Impl* NewTimerImpl() = 0;
|
||||
};
|
||||
|
||||
class Timer::Impl {
|
||||
friend class wvcdm::Timer;
|
||||
typedef enum {kIdle, kRunning} TimerState;
|
||||
public:
|
||||
static void RegisterTimerFactory(ITimerFactory* factory);
|
||||
|
||||
explicit Impl(cdm::Host* const host);
|
||||
virtual ~Impl(){}
|
||||
|
||||
void Start(TimerHandler *handler, uint32_t time_in_secs);
|
||||
|
||||
void Stop();
|
||||
|
||||
bool IsRunning(){return state_ == kRunning;}
|
||||
|
||||
void OnTimerEvent();
|
||||
|
||||
private:
|
||||
static ITimerFactory* factory_;
|
||||
cdm::Host* const host_;
|
||||
TimerHandler* handler_;
|
||||
int64_t delay_ms_;
|
||||
TimerState state_;
|
||||
};
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_CDM_HOST_TIMER_H_
|
||||
2
cdm/include/cdm_version.h
Normal file
2
cdm/include/cdm_version.h
Normal file
@@ -0,0 +1,2 @@
|
||||
// Widevine CE CDM Version
|
||||
#define CDM_VERSION "v3.0.0-0-g8d3792b-ce"
|
||||
@@ -1,654 +0,0 @@
|
||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef WVCDM_CDM_CONTENT_DECRYPTION_MODULE_H_
|
||||
#define WVCDM_CDM_CONTENT_DECRYPTION_MODULE_H_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Define CDM_EXPORT so that functionality implemented by the CDM module
|
||||
// can be exported to consumers.
|
||||
#if defined(WIN32)
|
||||
|
||||
#if defined(CDM_IMPLEMENTATION)
|
||||
#define CDM_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define CDM_EXPORT __declspec(dllimport)
|
||||
#endif // defined(CDM_IMPLEMENTATION)
|
||||
|
||||
#else // defined(WIN32)
|
||||
|
||||
#if defined(CDM_IMPLEMENTATION)
|
||||
#define CDM_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define CDM_EXPORT
|
||||
#endif
|
||||
|
||||
#endif // defined(WIN32)
|
||||
|
||||
// We maintain this macro for backward compatibility only.
|
||||
#define INITIALIZE_CDM_MODULE InitializeCdmModule
|
||||
|
||||
extern "C" {
|
||||
CDM_EXPORT void InitializeCdmModule();
|
||||
|
||||
CDM_EXPORT void DeinitializeCdmModule();
|
||||
|
||||
// Returns a pointer to the requested CDM Host interface upon success.
|
||||
// Returns NULL if the requested CDM Host interface is not supported.
|
||||
// The caller should cast the returned pointer to the type matching
|
||||
// |host_interface_version|.
|
||||
typedef void* (*GetCdmHostFunc)(int host_interface_version, void* user_data);
|
||||
|
||||
// Returns a pointer to the requested CDM upon success.
|
||||
// Returns NULL if an error occurs or the requested |cdm_interface_version| or
|
||||
// |key_system| is not supported or another error occurs.
|
||||
// The caller should cast the returned pointer to the type matching
|
||||
// |cdm_interface_version|.
|
||||
// Caller retains ownership of arguments and must call Destroy() on the returned
|
||||
// object.
|
||||
CDM_EXPORT void* CreateCdmInstance(
|
||||
int cdm_interface_version,
|
||||
const char* key_system, uint32_t key_system_size,
|
||||
GetCdmHostFunc get_cdm_host_func, void* user_data);
|
||||
|
||||
CDM_EXPORT const char* GetCdmVersion();
|
||||
}
|
||||
|
||||
namespace cdm {
|
||||
|
||||
class Host_1;
|
||||
class Host_4;
|
||||
|
||||
enum Status {
|
||||
kSuccess = 0,
|
||||
kNoKey = 2, // The required decryption key is not available.
|
||||
kSessionError = 3, // Session management error.
|
||||
kDecryptError = 4, // Decryption failed.
|
||||
kDecodeError = 5, // Error decoding audio or video.
|
||||
kRetry = 6, // Buffer temporarily cannot be accepted, delay and retry.
|
||||
kNeedsDeviceCertificate = 7 // A certificate is required for licensing.
|
||||
};
|
||||
|
||||
// This must be consistent with MediaKeyError defined in the
|
||||
// Encrypted media Extensions (EME) specification: http://goo.gl/IBjNCP
|
||||
enum MediaKeyError {
|
||||
kUnknownError = 1,
|
||||
kClientError = 2,
|
||||
kOutputError = 4,
|
||||
};
|
||||
|
||||
// The type of session to create. The valid types are defined in the spec:
|
||||
// http://goo.gl/vmc3pd
|
||||
enum SessionType {
|
||||
kTemporary = 0,
|
||||
kPersistent = 1,
|
||||
kProvisioning = 2,
|
||||
};
|
||||
|
||||
// The type of stream. Used in DecryptDecodeAndRender.
|
||||
enum StreamType {
|
||||
kStreamTypeAudio = 0,
|
||||
kStreamTypeVideo = 1
|
||||
};
|
||||
|
||||
// An input buffer can be split into several continuous subsamples.
|
||||
// A SubsampleEntry specifies the number of clear and cipher bytes in each
|
||||
// subsample. For example, the following buffer has three subsamples:
|
||||
//
|
||||
// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
|
||||
// | clear1 | cipher1 | clear2 | cipher2 | clear3 | cipher3 |
|
||||
//
|
||||
// For decryption, all of the cipher bytes in a buffer should be concatenated
|
||||
// (in the subsample order) into a single logical stream. The clear bytes should
|
||||
// not be considered as part of decryption.
|
||||
//
|
||||
// Stream to decrypt: | cipher1 | cipher2 | cipher3 |
|
||||
// Decrypted stream: | decrypted1| decrypted2 | decrypted3 |
|
||||
//
|
||||
// After decryption, the decrypted bytes should be copied over the position
|
||||
// of the corresponding cipher bytes in the original buffer to form the output
|
||||
// buffer. Following the above example, the decrypted buffer should be:
|
||||
//
|
||||
// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
|
||||
// | clear1 | decrypted1| clear2 | decrypted2 | clear3 | decrypted3 |
|
||||
//
|
||||
struct SubsampleEntry {
|
||||
SubsampleEntry(uint32_t clear_bytes, uint32_t cipher_bytes)
|
||||
: clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {}
|
||||
|
||||
uint32_t clear_bytes;
|
||||
uint32_t cipher_bytes;
|
||||
};
|
||||
|
||||
// Represents an input buffer to be decrypted (and possibly decoded). It
|
||||
// does not own any pointers in this struct.
|
||||
struct InputBuffer {
|
||||
InputBuffer()
|
||||
: data(NULL),
|
||||
data_size(0),
|
||||
data_offset(0),
|
||||
key_id(NULL),
|
||||
key_id_size(0),
|
||||
iv(NULL),
|
||||
iv_size(0),
|
||||
subsamples(NULL),
|
||||
num_subsamples(0),
|
||||
timestamp(0) {}
|
||||
|
||||
const uint8_t* data; // Pointer to the beginning of the input data.
|
||||
uint32_t data_size; // Size (in bytes) of |data|.
|
||||
|
||||
uint32_t data_offset; // Number of bytes to be discarded before decryption.
|
||||
|
||||
const uint8_t* key_id; // Key ID to identify the decryption key.
|
||||
uint32_t key_id_size; // Size (in bytes) of |key_id|.
|
||||
|
||||
const uint8_t* iv; // Initialization vector.
|
||||
uint32_t iv_size; // Size (in bytes) of |iv|.
|
||||
|
||||
const struct SubsampleEntry* subsamples;
|
||||
uint32_t num_subsamples; // Number of subsamples in |subsamples|.
|
||||
|
||||
int64_t timestamp; // Presentation timestamp in microseconds.
|
||||
};
|
||||
|
||||
// Represents a buffer created by the Host.
|
||||
class Buffer {
|
||||
public:
|
||||
// Destroys the buffer in the same context as it was created.
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
virtual int32_t Capacity() const = 0;
|
||||
virtual uint8_t* Data() = 0;
|
||||
virtual void SetSize(int32_t size) = 0;
|
||||
virtual int32_t Size() const = 0;
|
||||
|
||||
protected:
|
||||
Buffer() {}
|
||||
virtual ~Buffer() {}
|
||||
|
||||
private:
|
||||
Buffer(const Buffer&);
|
||||
void operator=(const Buffer&);
|
||||
};
|
||||
|
||||
// Represents a key-value map.
|
||||
// Both created and destroyed by the Host.
|
||||
// Data is filled in by the CDM.
|
||||
// Need not be implemented if QueryKeyStatus() is not called.
|
||||
class KeyValueMap {
|
||||
public:
|
||||
virtual void Set(const char* key, void* value, size_t value_size) = 0;
|
||||
|
||||
protected:
|
||||
KeyValueMap() {}
|
||||
virtual ~KeyValueMap() {}
|
||||
|
||||
private:
|
||||
KeyValueMap(const KeyValueMap&);
|
||||
void operator=(const KeyValueMap&);
|
||||
};
|
||||
|
||||
// Represents a decrypted block that has not been decoded.
|
||||
class DecryptedBlock {
|
||||
public:
|
||||
virtual void SetDecryptedBuffer(Buffer* buffer) = 0;
|
||||
virtual Buffer* DecryptedBuffer() = 0;
|
||||
|
||||
virtual void SetTimestamp(int64_t timestamp) = 0;
|
||||
virtual int64_t Timestamp() const = 0;
|
||||
|
||||
protected:
|
||||
DecryptedBlock() {}
|
||||
virtual ~DecryptedBlock() {}
|
||||
};
|
||||
|
||||
// The FileIO interface provides a way for the CDM to store data in a file in
|
||||
// persistent storage. This interface aims only at providing basic read/write
|
||||
// capabilities and should not be used as a full fledged file IO API.
|
||||
//
|
||||
// All methods that report their result via calling a method on FileIOClient
|
||||
// (currently, this is Open, Read, and Write) must call into FileIOClient on the
|
||||
// same thread they were called on and must do so before returning. This
|
||||
// restriction may be lifted in the future.
|
||||
//
|
||||
// Each domain (e.g. "example.com") and each CDM has it's own persistent
|
||||
// storage. All instances of a given CDM associated with a given domain share
|
||||
// the same persistent storage.
|
||||
//
|
||||
// Note to implementors of this interface:
|
||||
// Per-origin storage and the ability for users to clear it are important.
|
||||
// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
|
||||
class FileIO {
|
||||
public:
|
||||
// Opens the file with |file_name| for read and write.
|
||||
// FileIOClient::OnOpenComplete() will be called after the opening
|
||||
// operation finishes.
|
||||
// - When the file is opened by a CDM instance, it will be classified as "in
|
||||
// use". In this case other CDM instances in the same domain may receive
|
||||
// kInUse status when trying to open it.
|
||||
// - |file_name| should not include path separators.
|
||||
virtual void Open(const char* file_name, uint32_t file_name_size) = 0;
|
||||
|
||||
// Reads the contents of the file. FileIOClient::OnReadComplete() will be
|
||||
// called with the read status. Read() should not be called if a previous
|
||||
// Read() or Write() call is still pending; otherwise OnReadComplete() will
|
||||
// be called with kInUse.
|
||||
virtual void Read() = 0;
|
||||
|
||||
// Writes |data_size| bytes of |data| into the file.
|
||||
// FileIOClient::OnWriteComplete() will be called with the write status.
|
||||
// All existing contents in the file will be overwritten. Calling Write() with
|
||||
// NULL |data| will clear all contents in the file. Write() should not be
|
||||
// called if a previous Write() or Read() call is still pending; otherwise
|
||||
// OnWriteComplete() will be called with kInUse.
|
||||
virtual void Write(const uint8_t* data, uint32_t data_size) = 0;
|
||||
|
||||
// Closes the file if opened, destroys this FileIO object and releases any
|
||||
// resources allocated. The CDM must call this method when it finished using
|
||||
// this object. A FileIO object must not be used after Close() is called.
|
||||
virtual void Close() = 0;
|
||||
|
||||
protected:
|
||||
FileIO() {}
|
||||
virtual ~FileIO() {}
|
||||
};
|
||||
|
||||
// Responses to FileIO calls.
|
||||
class FileIOClient {
|
||||
public:
|
||||
enum Status {
|
||||
kSuccess = 0,
|
||||
kInUse,
|
||||
kError
|
||||
};
|
||||
|
||||
// Response to a FileIO::Open() call with the open |status|.
|
||||
virtual void OnOpenComplete(Status status) = 0;
|
||||
|
||||
// Response to a FileIO::Read() call to provide |data_size| bytes of |data|
|
||||
// read from the file.
|
||||
// - kSuccess indicates that all contents of the file has been successfully
|
||||
// read. In this case, 0 |data_size| means that the file is empty.
|
||||
// - kInUse indicates that there are other read/write operations pending.
|
||||
// - kError indicates read failure, e.g. the storage isn't open or cannot be
|
||||
// fully read.
|
||||
virtual void OnReadComplete(Status status,
|
||||
const uint8_t* data, uint32_t data_size) = 0;
|
||||
|
||||
// Response to a FileIO::Write() call.
|
||||
// - kSuccess indicates that all the data has been written into the file
|
||||
// successfully.
|
||||
// - kInUse indicates that there are other read/write operations pending.
|
||||
// - kError indicates write failure, e.g. the storage isn't open or cannot be
|
||||
// fully written. Upon write failure, the contents of the file should be
|
||||
// regarded as corrupt and should not used.
|
||||
virtual void OnWriteComplete(Status status) = 0;
|
||||
|
||||
protected:
|
||||
FileIOClient() {}
|
||||
virtual ~FileIOClient() {}
|
||||
};
|
||||
|
||||
// ContentDecryptionModule interface that all CDMs need to implement.
|
||||
// CDM interfaces are versioned for backward compatibility.
|
||||
// Note: ContentDecryptionModule implementations must use the Host
|
||||
// to allocate any Buffer that needs to be passed back to the caller.
|
||||
// Host implementations must call Buffer::Destroy() when a Buffer is created
|
||||
// that will never be returned to the caller.
|
||||
|
||||
// Based on chromium's ContentDecryptionModule_1.
|
||||
class ContentDecryptionModule_1 {
|
||||
public:
|
||||
static const int kVersion = 1002;
|
||||
typedef Host_1 Host;
|
||||
|
||||
// Generates a |key_request| given |type| and |init_data|.
|
||||
//
|
||||
// Returns kSuccess if the key request was successfully generated, in which
|
||||
// case the CDM must send the key message by calling Host::SendKeyMessage().
|
||||
// Returns kSessionError if any error happened, in which case the CDM must
|
||||
// send a key error by calling Host::SendKeyError().
|
||||
virtual Status GenerateKeyRequest(
|
||||
const char* type, int type_size,
|
||||
const uint8_t* init_data, int init_data_size) = 0;
|
||||
|
||||
// Adds the |key| to the CDM to be associated with |key_id|.
|
||||
//
|
||||
// Returns kSuccess if the key was successfully added, kSessionError
|
||||
// otherwise.
|
||||
virtual Status AddKey(const char* session_id, int session_id_size,
|
||||
const uint8_t* key, int key_size,
|
||||
const uint8_t* key_id, int key_id_size) = 0;
|
||||
|
||||
// Tests whether |key_id| is known to any current session.
|
||||
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) = 0;
|
||||
|
||||
// Closes the session identified by |session_id| and releases all crypto
|
||||
// resources related to that session. After calling this, it is invalid to
|
||||
// refer to this session any more, because the session has been destroyed.
|
||||
//
|
||||
// Returns kSuccess if the session |session_id| was successfully closed and
|
||||
// all resources released, kSessionError otherwise.
|
||||
virtual Status CloseSession(const char* session_id, int session_id_size) = 0;
|
||||
|
||||
// Performs scheduled operation with |context| when the timer fires.
|
||||
virtual void TimerExpired(void* context) = 0;
|
||||
|
||||
// Decrypts the |encrypted_buffer|.
|
||||
//
|
||||
// Returns kSuccess if decryption succeeded, in which case the callee
|
||||
// should have filled the |decrypted_buffer| and passed the ownership of
|
||||
// |data| in |decrypted_buffer| to the caller.
|
||||
// Returns kNoKey if the CDM did not have the necessary decryption key
|
||||
// to decrypt.
|
||||
// Returns kDecryptError if any other error happened.
|
||||
// If the return value is not kSuccess, |decrypted_buffer| should be ignored
|
||||
// by the caller.
|
||||
virtual Status Decrypt(const InputBuffer& encrypted_buffer,
|
||||
DecryptedBlock* decrypted_buffer) = 0;
|
||||
|
||||
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer, and passes
|
||||
// the video frame or audio samples to the rendering FW/HW. No data is
|
||||
// returned to the caller.
|
||||
//
|
||||
// Returns kSuccess if decryption, decoding, and rendering all succeeded.
|
||||
// Returns kNoKey if the CDM did not have the necessary decryption key
|
||||
// to decrypt.
|
||||
// Returns kRetry if |encrypted_buffer| cannot be accepted (e.g, video
|
||||
// pipeline is full). Caller should retry after a short delay.
|
||||
// Returns kDecryptError if any decryption error happened.
|
||||
// Returns kDecodeError if any decoding error happened.
|
||||
// If the return value is not kSuccess, |video_frame| should be ignored by
|
||||
// the caller.
|
||||
virtual Status DecryptDecodeAndRenderFrame(
|
||||
const InputBuffer& encrypted_buffer) = 0;
|
||||
|
||||
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer into a
|
||||
// video frame, and passes the frame to the rendering FW/HW. No data
|
||||
// is returned.
|
||||
//
|
||||
// Returns kSuccess if decryption, decoding, and rendering all succeeded.
|
||||
// Returns kNoKey if the CDM did not have the necessary decryption key
|
||||
// to decrypt.
|
||||
// Returns kRetry if |encrypted_buffer| cannot be accepted (e.g., audio
|
||||
// pipeline is full). Caller should retry after a short delay.
|
||||
// Returns kDecryptError if any decryption error happened.
|
||||
// Returns kDecodeError if any decoding error happened.
|
||||
// If the return value is not kSuccess or kRetry, the audiostream has failed
|
||||
// and should be reset.
|
||||
virtual Status DecryptDecodeAndRenderSamples(
|
||||
const InputBuffer& encrypted_buffer) = 0;
|
||||
|
||||
// Destroys the object in the same context as it was created.
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
// Provisioning-related methods.
|
||||
virtual Status GetProvisioningRequest(
|
||||
std::string* request, std::string* default_url) = 0;
|
||||
|
||||
virtual Status HandleProvisioningResponse(
|
||||
std::string& response) = 0;
|
||||
|
||||
protected:
|
||||
ContentDecryptionModule_1() {}
|
||||
virtual ~ContentDecryptionModule_1() {}
|
||||
};
|
||||
|
||||
// Based on chromium's ContentDecryptionModule_4 and ContentDecryptionModule_5.
|
||||
class ContentDecryptionModule_4 {
|
||||
public:
|
||||
static const int kVersion = 1004;
|
||||
typedef Host_4 Host;
|
||||
|
||||
// The non-decryption methods on this class, such as CreateSession(),
|
||||
// get passed a |session_id| for a MediaKeySession object. It must be used in
|
||||
// the reply via Host methods (e.g. Host::OnSessionMessage()).
|
||||
// Note: |session_id| is different from MediaKeySession's sessionId attribute,
|
||||
// which is referred to as |web_session_id| in this file.
|
||||
|
||||
// Creates a new session and generates a key request given |init_data| and
|
||||
// |session_type|. OnSessionCreated() will be called with a web session ID
|
||||
// once the session exists. OnSessionMessage() will subsequently be called
|
||||
// with the key request. A session represents hardware crypto resources,
|
||||
// (if any exist for the platform), which may be in limited supply.
|
||||
// For sessions of type kProvisioning, |mime_type| and |init_data| will be
|
||||
// ignored and may be NULL.
|
||||
virtual void CreateSession(uint32_t session_id,
|
||||
const char* mime_type, uint32_t mime_type_size,
|
||||
const uint8_t* init_data, uint32_t init_data_size,
|
||||
SessionType session_type) = 0;
|
||||
|
||||
// Creates a new session and loads a previous persistent session into it that
|
||||
// has a web session ID of |web_session_id|. OnSessionCreated() will be
|
||||
// called once the session is loaded.
|
||||
virtual void LoadSession(
|
||||
uint32_t session_id,
|
||||
const char* web_session_id, uint32_t web_session_id_length) = 0;
|
||||
|
||||
// Updates the session with |response|.
|
||||
virtual void UpdateSession(
|
||||
uint32_t session_id,
|
||||
const uint8_t* response, uint32_t response_size) = 0;
|
||||
|
||||
// Tests whether |key_id| is known to any current session.
|
||||
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) = 0;
|
||||
|
||||
// Releases the resources for the session |session_id|.
|
||||
// After calling this, it is invalid to refer to this session any more.
|
||||
// If any hardware crypto resources were being used by this session, they will
|
||||
// be released.
|
||||
// If this session was a persistent session, this will NOT delete the
|
||||
// persisted data. The persisted data will be preserved so that the session
|
||||
// can be reloaded later with LoadSession(). To delete the persisted session,
|
||||
// use RemoveSession().
|
||||
virtual void ReleaseSession(uint32_t session_id) = 0;
|
||||
|
||||
// Creates a new session and generates a key release request for the
|
||||
// existing persistent session identified by |web_session_id|.
|
||||
// OnSessionCreated() will be called once the session exists.
|
||||
// OnSessionMessage() will subsequently be called with the key release
|
||||
// request.
|
||||
virtual void RemoveSession(
|
||||
uint32_t session_id,
|
||||
const char* web_session_id, uint32_t web_session_id_length) = 0;
|
||||
|
||||
// Signals to the CDM that it should use server certificates to protect the
|
||||
// privacy of the user. The primary use of this is when the application
|
||||
// driving the CDM is untrusted code, such as when a web browser allows a web
|
||||
// page's JavaScript to access the CDM. By using server certificates to
|
||||
// encrypt communication with the license server, device-identifying
|
||||
// information cannot be extracted from the license exchange process by a
|
||||
// malicious caller.
|
||||
// Unless you also call SetServerCertificate() to set a pre-cached server
|
||||
// certificate, the CDM will perform a certificate exchange with the server
|
||||
// prior to any key exchanges.
|
||||
// This method may not be called if any sessions are open. It is typically
|
||||
// called before any sessions have been opened, but may also be called if all
|
||||
// open sessions have been released.
|
||||
// Note that calling SetServerCertificate() implicitly calls this method as
|
||||
// well.
|
||||
virtual Status UsePrivacyMode() = 0;
|
||||
|
||||
// Provides a server certificate to be used to encrypt messages to the
|
||||
// license server. Calling this is like calling UsePrivacyMode(), except that
|
||||
// because the certificate is provided up-front, the CDM does not have to
|
||||
// perform a certificate exchange with the server.
|
||||
// This method may not be called if any sessions are open. It is typically
|
||||
// called before any sessions have been opened, but may also be called if all
|
||||
// open sessions have been released.
|
||||
// Note that calling this method also implicitly calls UsePrivacyMode().
|
||||
virtual Status SetServerCertificate(
|
||||
const uint8_t* server_certificate_data,
|
||||
uint32_t server_certificate_data_size) = 0;
|
||||
|
||||
// Performs scheduled operation with |context| when the timer fires.
|
||||
virtual void TimerExpired(void* context) = 0;
|
||||
|
||||
// Decrypts the |encrypted_buffer|.
|
||||
//
|
||||
// Returns kSuccess if decryption succeeded, in which case the callee
|
||||
// should have filled the |decrypted_buffer| and passed the ownership of
|
||||
// |data| in |decrypted_buffer| to the caller.
|
||||
// Returns kNoKey if the CDM did not have the necessary decryption key
|
||||
// to decrypt.
|
||||
// Returns kDecryptError if any other error happened.
|
||||
// If the return value is not kSuccess, |decrypted_buffer| should be ignored
|
||||
// by the caller.
|
||||
virtual Status Decrypt(const InputBuffer& encrypted_buffer,
|
||||
DecryptedBlock* decrypted_buffer) = 0;
|
||||
|
||||
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer, and passes
|
||||
// the video or audio frames to the rendering FW/HW. No data is returned to
|
||||
// the caller.
|
||||
//
|
||||
// Returns kSuccess if decryption, decoding, and rendering all succeeded.
|
||||
// Returns kNoKey if the CDM did not have the necessary decryption key
|
||||
// to decrypt.
|
||||
// Returns kRetry if |encrypted_buffer| cannot be accepted (e.g, video
|
||||
// pipeline is full). Caller should retry after a short delay.
|
||||
// Returns kDecryptError if any decryption error happened.
|
||||
// Returns kDecodeError if any decoding error happened.
|
||||
virtual Status DecryptDecodeAndRender(const InputBuffer& encrypted_buffer,
|
||||
StreamType stream_type) = 0;
|
||||
|
||||
// Destroys the object in the same context as it was created.
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
protected:
|
||||
ContentDecryptionModule_4() {}
|
||||
virtual ~ContentDecryptionModule_4() {}
|
||||
};
|
||||
|
||||
// Host interface that the CDM can call into to access browser side services.
|
||||
// Host interfaces are versioned for backward compatibility.
|
||||
|
||||
// Based on chromium's Host_1.
|
||||
class Host_1 {
|
||||
public:
|
||||
static const int kVersion = 1002;
|
||||
|
||||
// Returns a Buffer* containing non-zero members upon success, or NULL on
|
||||
// failure. The caller owns the Buffer* after this call. The buffer is not
|
||||
// guaranteed to be zero initialized. The capacity of the allocated Buffer
|
||||
// is guaranteed to be not less than |capacity|.
|
||||
virtual Buffer* Allocate(int32_t capacity) = 0;
|
||||
|
||||
// Requests the host to call ContentDecryptionModule::TimerExpired() in
|
||||
// |delay_ms| from now with |context|.
|
||||
virtual void SetTimer(int64_t delay_ms, void* context) = 0;
|
||||
|
||||
// Returns the current epoch wall time in seconds.
|
||||
virtual double GetCurrentWallTimeInSeconds() = 0;
|
||||
|
||||
// Sends a keymessage event to the application.
|
||||
// Length parameters should not include null termination.
|
||||
virtual void SendKeyMessage(
|
||||
const char* session_id, int32_t session_id_length,
|
||||
const char* message, int32_t message_length,
|
||||
const char* default_url, int32_t default_url_length) = 0;
|
||||
|
||||
// Sends a keyerror event to the application.
|
||||
// |session_id_length| should not include null termination.
|
||||
virtual void SendKeyError(const char* session_id,
|
||||
int32_t session_id_length,
|
||||
MediaKeyError error_code,
|
||||
uint32_t system_code) = 0;
|
||||
|
||||
// Version 1.3:
|
||||
// These virtual member functions extend the cdm::Host interface to allow
|
||||
// the CDM to query the host for various information.
|
||||
|
||||
// Asks the host to persist a name-value pair.
|
||||
virtual void SetPlatformString(const std::string& name,
|
||||
const std::string& value) = 0;
|
||||
|
||||
// Retrieves a value by name. If there is no such value, the Host should
|
||||
// set value to an empty string.
|
||||
virtual void GetPlatformString(const std::string& name,
|
||||
std::string* value) = 0;
|
||||
|
||||
protected:
|
||||
Host_1() {}
|
||||
virtual ~Host_1() {}
|
||||
};
|
||||
|
||||
// Based on chromium's Host_4 and Host_5.
|
||||
class Host_4 {
|
||||
public:
|
||||
static const int kVersion = 1004;
|
||||
|
||||
// Returns a Buffer* containing non-zero members upon success, or NULL on
|
||||
// failure. The caller owns the Buffer* after this call. The buffer is not
|
||||
// guaranteed to be zero initialized. The capacity of the allocated Buffer
|
||||
// is guaranteed to be not less than |capacity|.
|
||||
virtual Buffer* Allocate(uint32_t capacity) = 0;
|
||||
|
||||
// Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
|
||||
// from now with |context|.
|
||||
virtual void SetTimer(int64_t delay_ms, void* context) = 0;
|
||||
|
||||
// Returns the current epoch wall time in seconds.
|
||||
virtual double GetCurrentWallTimeInSeconds() = 0;
|
||||
|
||||
// Called by the CDM when a session is created or loaded and the value for the
|
||||
// MediaKeySession's sessionId attribute is available (|web_session_id|).
|
||||
// This must be called before OnSessionMessage() or OnSessionUpdated() is
|
||||
// called for |session_id|. |web_session_id_length| should not include null
|
||||
// termination.
|
||||
// When called in response to LoadSession(), the |web_session_id| must be the
|
||||
// same as the |web_session_id| passed in LoadSession().
|
||||
virtual void OnSessionCreated(
|
||||
uint32_t session_id,
|
||||
const char* web_session_id, uint32_t web_session_id_length) = 0;
|
||||
|
||||
// Called by the CDM when it has a message for session |session_id|.
|
||||
// Length parameters should not include null termination.
|
||||
virtual void OnSessionMessage(
|
||||
uint32_t session_id,
|
||||
const char* message, uint32_t message_length,
|
||||
const char* destination_url, uint32_t destination_url_length) = 0;
|
||||
|
||||
// Called by the CDM when session |session_id| has been updated.
|
||||
virtual void OnSessionUpdated(uint32_t session_id) = 0;
|
||||
|
||||
// Called by the CDM when session |session_id| is closed.
|
||||
virtual void OnSessionClosed(uint32_t session_id) = 0;
|
||||
|
||||
// Called by the CDM when an error occurs in session |session_id|.
|
||||
virtual void OnSessionError(uint32_t session_id,
|
||||
Status error_code,
|
||||
uint32_t system_code) = 0;
|
||||
|
||||
// Creates a FileIO object from the host to do file IO operation. Returns NULL
|
||||
// if a FileIO object cannot be obtained. Once a valid FileIO object is
|
||||
// returned, |client| must be valid until FileIO::Close() is called. The
|
||||
// CDM can call this method multiple times to operate on different files.
|
||||
virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
|
||||
|
||||
protected:
|
||||
Host_4() {}
|
||||
virtual ~Host_4() {}
|
||||
};
|
||||
|
||||
typedef ContentDecryptionModule_1 ContentDecryptionModule;
|
||||
const int kCdmInterfaceVersion = ContentDecryptionModule::kVersion;
|
||||
|
||||
typedef ContentDecryptionModule::Host Host;
|
||||
const int kHostInterfaceVersion = Host::kVersion;
|
||||
|
||||
} // namespace cdm
|
||||
|
||||
#endif // WVCDM_CDM_CONTENT_DECRYPTION_MODULE_H_
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_HOST_4_FILE_IO_CLIENT_H_
|
||||
#define WVCDM_HOST_4_FILE_IO_CLIENT_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Host4FileIOClient : public cdm::FileIOClient {
|
||||
public:
|
||||
explicit Host4FileIOClient(cdm::Host_4* host)
|
||||
: host_(host),
|
||||
file_io_(NULL),
|
||||
status_(kSuccess),
|
||||
data_size_(0),
|
||||
buffer_(NULL),
|
||||
buffer_size_(0) {}
|
||||
~Host4FileIOClient();
|
||||
|
||||
bool Open(const std::string& name);
|
||||
bool Read(char* buffer, size_t buffer_size);
|
||||
bool ReadFileSize() { return Read(NULL, 0); }
|
||||
bool Write(const char* data, size_t data_size);
|
||||
bool Close();
|
||||
|
||||
// cdm::FileIOClient implementation
|
||||
virtual void OnOpenComplete(Status status) OVERRIDE;
|
||||
virtual void OnReadComplete(Status status, const uint8_t* data,
|
||||
uint32_t data_size) OVERRIDE;
|
||||
virtual void OnWriteComplete(Status status) OVERRIDE;
|
||||
|
||||
// Get the result of the last operation
|
||||
Status status() const { return status_; }
|
||||
uint32_t data_size() const { return data_size_; }
|
||||
|
||||
private:
|
||||
cdm::Host_4* host_;
|
||||
cdm::FileIO* file_io_;
|
||||
|
||||
// These hold the result of the last operation
|
||||
Status status_;
|
||||
uint32_t data_size_;
|
||||
char* buffer_;
|
||||
size_t buffer_size_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Host4FileIOClient);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_HOST_4_FILE_IO_CLIENT_H_
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_HOST_EVENT_LISTENER_H_
|
||||
#define WVCDM_CDM_HOST_EVENT_LISTENER_H_
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class HostEventListener : public WvCdmEventListener {
|
||||
public:
|
||||
HostEventListener(cdm::Host* host, CdmEngine* cdm_engine)
|
||||
: host_(host), cdm_engine_(cdm_engine) {}
|
||||
virtual ~HostEventListener() {}
|
||||
|
||||
// wvcdm::WvCdmEventListener implementation.
|
||||
virtual void OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) OVERRIDE;
|
||||
|
||||
private:
|
||||
cdm::Host* const host_;
|
||||
CdmEngine* const cdm_engine_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(HostEventListener);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_HOST_EVENT_LISTENER_H_
|
||||
17
cdm/include/override.h
Normal file
17
cdm/include/override.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
// TODO: Import to core/, use everywhere.
|
||||
#ifndef WVCDM_CDM_OVERRIDE_H_
|
||||
#define WVCDM_CDM_OVERRIDE_H_
|
||||
|
||||
#define GCC_HAS_OVERRIDE ( \
|
||||
(__GNUC__ > 4) || \
|
||||
(__GNUC__ == 4 && __GNUC_MINOR__ >= 7) \
|
||||
)
|
||||
|
||||
#if defined(COMPILER_MSVC) || defined(__clang__) || GCC_HAS_OVERRIDE
|
||||
#define OVERRIDE override
|
||||
#else
|
||||
#define OVERRIDE
|
||||
#endif
|
||||
|
||||
#endif // WVCDM_CDM_OVERRIDE_H_
|
||||
30
cdm/include/properties_ce.h
Normal file
30
cdm/include/properties_ce.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#ifndef WVCDM_CDM_PROPERTIES_CE_H_
|
||||
#define WVCDM_CDM_PROPERTIES_CE_H_
|
||||
|
||||
#include "cdm.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# include <gtest/gtest.h>
|
||||
#endif
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class PropertiesCE {
|
||||
public:
|
||||
static Cdm::ClientInfo GetClientInfo();
|
||||
static Cdm::SecureOutputType GetSecureOutputType();
|
||||
|
||||
private:
|
||||
static void SetSecureOutputType(Cdm::SecureOutputType secure_output_type);
|
||||
static void SetClientInfo(const Cdm::ClientInfo& client_info);
|
||||
|
||||
friend class Cdm;
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(CdmTest, DeviceCertificateRequest);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WVCDM_CDM_PROPERTIES_CE_H_
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_PROPERTIES_CONFIGURATION_H_
|
||||
#define WVCDM_CDM_PROPERTIES_CONFIGURATION_H_
|
||||
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "properties.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Set only one of the two below to true. If secure buffer
|
||||
// is selected, fallback to userspace buffers may occur
|
||||
// if L1/L2 OEMCrypto APIs fail.
|
||||
// Note: This is managed in the build configuration.
|
||||
const bool kPropertyOemCryptoUseSecureBuffers = PLATFORM_REQUIRES_SECURE_BUFFERS;
|
||||
const bool kPropertyOemCryptoUseFifo = true;
|
||||
const bool kPropertyOemCryptoUseUserSpaceBuffers = PLATFORM_USES_CLEAR_BUFFERS;
|
||||
|
||||
// If true, the unit tests require OEMCrypto to support usage tables.
|
||||
const bool kPropertyOemCryptoRequireUsageTable = false;
|
||||
|
||||
// If false, keyboxes will be used as client identification
|
||||
// and passed as the token in the license request.
|
||||
// The default value of false for PLATFORM_CERTIFICATE_PROV is set in
|
||||
// global_config.gypi. It can be overridden to true in the platform specific
|
||||
// .gypi files if you want your device to use certificates for provisioning.
|
||||
const bool kPropertyUseCertificatesAsIdentification = PLATFORM_CERTIFICATE_PROV;
|
||||
|
||||
// If true, device files will be moved to the directory specified by
|
||||
// Properties::GetDeviceFilesBasePath
|
||||
const bool kSecurityLevelPathBackwardCompatibilitySupport = false;
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_PROPERTIES_CONFIGURATION_H_
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_WV_CDM_COMMON_H_
|
||||
#define WVCDM_CDM_WV_CDM_COMMON_H_
|
||||
|
||||
#if defined(COMPILER_MSVC) || defined(__clang__)
|
||||
#define OVERRIDE override
|
||||
#else
|
||||
#define OVERRIDE
|
||||
#endif
|
||||
|
||||
#endif // WVCDM_CDM_WV_CDM_COMMON_H_
|
||||
@@ -1,3 +0,0 @@
|
||||
// Widevine CDM Kit Version
|
||||
#define WV_CDM_VERSION "v2.2.0-0-903"
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_
|
||||
#define WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_
|
||||
|
||||
#include "cdm_client_property_set.h"
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WVClientPropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
WVClientPropertySet()
|
||||
: use_privacy_mode_(false) {}
|
||||
|
||||
virtual ~WVClientPropertySet() {}
|
||||
|
||||
void set_security_level(const std::string& securityLevel) {
|
||||
security_level_ = securityLevel;
|
||||
}
|
||||
|
||||
virtual const std::string& security_level() const {
|
||||
return security_level_;
|
||||
}
|
||||
|
||||
void set_use_privacy_mode(bool usePrivacyMode) {
|
||||
use_privacy_mode_ = usePrivacyMode;
|
||||
}
|
||||
|
||||
virtual bool use_privacy_mode() const {
|
||||
return use_privacy_mode_;
|
||||
}
|
||||
|
||||
void set_service_certificate(const std::string& serviceCertificate) {
|
||||
service_certificate_ = serviceCertificate;
|
||||
}
|
||||
|
||||
virtual const std::string& service_certificate() const {
|
||||
return service_certificate_;
|
||||
}
|
||||
|
||||
virtual bool is_session_sharing_enabled() const {
|
||||
return true; // This is unused by common cdm but we need a definition
|
||||
// for the pure virtual methods.
|
||||
}
|
||||
|
||||
void set_is_session_sharing_enabled(bool shareKeys) {
|
||||
return; // This is unused by common cdm but we need a definition
|
||||
// for the pure virtual methods.
|
||||
}
|
||||
|
||||
virtual uint32_t session_sharing_id() const {
|
||||
return 1; // This is unused by common cdm but we need a
|
||||
// definition for the pure virtual methods.
|
||||
}
|
||||
|
||||
virtual void set_session_sharing_id(uint32_t id) {
|
||||
return; // This is unused by common cdm but we need a
|
||||
// definition for the pure virtual methods.
|
||||
}
|
||||
|
||||
private:
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WVClientPropertySet);
|
||||
|
||||
std::string security_level_;
|
||||
bool use_privacy_mode_;
|
||||
std::string service_certificate_;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_
|
||||
#define WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_
|
||||
|
||||
#include "cdm_client_property_set.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "cdm_host_clock.h"
|
||||
#include "cdm_host_file.h"
|
||||
|
||||
#include "clock.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "host_event_listener.h"
|
||||
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_client_property_set.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WvContentDecryptionModule_1 : public cdm::ContentDecryptionModule_1,
|
||||
public IFileFactory,
|
||||
public IClock {
|
||||
public:
|
||||
explicit WvContentDecryptionModule_1(cdm::Host_1* host);
|
||||
|
||||
virtual ~WvContentDecryptionModule_1();
|
||||
|
||||
// cdm::ContentDecryptionModule_1 implementation.
|
||||
virtual cdm::Status GenerateKeyRequest(const char* type, int type_size,
|
||||
const uint8_t* init_data,
|
||||
int init_data_size) OVERRIDE;
|
||||
|
||||
virtual cdm::Status AddKey(const char* session_id, int session_id_size,
|
||||
const uint8_t* key, int key_size,
|
||||
const uint8_t* key_id, int key_id_size) OVERRIDE;
|
||||
|
||||
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) OVERRIDE;
|
||||
|
||||
virtual cdm::Status CloseSession(const char* session_id,
|
||||
int session_id_size) OVERRIDE;
|
||||
|
||||
virtual void TimerExpired(void* context) OVERRIDE;
|
||||
|
||||
virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_buffer) OVERRIDE;
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRenderFrame(
|
||||
const cdm::InputBuffer& encrypted_buffer) OVERRIDE;
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRenderSamples(
|
||||
const cdm::InputBuffer& encrypted_buffer) OVERRIDE;
|
||||
|
||||
virtual void Destroy() OVERRIDE;
|
||||
|
||||
virtual cdm::Status GetProvisioningRequest(
|
||||
std::string* request, std::string* default_url) OVERRIDE;
|
||||
|
||||
virtual cdm::Status HandleProvisioningResponse(
|
||||
std::string& response) OVERRIDE;
|
||||
|
||||
private:
|
||||
void EnablePolicyTimer();
|
||||
void DisablePolicyTimer();
|
||||
|
||||
virtual File::Impl* NewFileImpl() OVERRIDE;
|
||||
|
||||
virtual int64_t GetCurrentTimeInSeconds() OVERRIDE;
|
||||
|
||||
/* |parameters| is expected to be initialized with anything not related to
|
||||
* subsample parsing. |iv| is initialized by the caller, but may be modified
|
||||
* during decryption. |decrypted_block| may be NULL for L1 decrypts, since
|
||||
* no data is passed back to the caller. */
|
||||
cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
cdm::Status DoDecrypt(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
|
||||
/* |parameters| is expected to be initialized with everything required by
|
||||
* DoSubsampleDecrypt and DoDecrypt, plus |is_encrypted| and
|
||||
* |subsample_flags|. Counters and |iv| will be updated to prepare for
|
||||
* subsequent calls. */
|
||||
cdm::Status DecryptAndUpdateCounters(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
const size_t bytes,
|
||||
cdm::DecryptedBlock* decrypted_block,
|
||||
size_t& offset,
|
||||
size_t& encrypted_offset,
|
||||
uint32_t& block_ctr);
|
||||
|
||||
void SetSizesAndAllocate(size_t output_size,
|
||||
CdmDecryptionParameters& parameters,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
|
||||
cdm::Host_1* const host_;
|
||||
HostEventListener host_event_listener_;
|
||||
|
||||
CdmEngine cdm_engine_;
|
||||
WVClientPropertySet property_set_;
|
||||
bool timer_enabled_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule_1);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_
|
||||
@@ -1,155 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_
|
||||
#define WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
|
||||
#include "cdm_client_property_set.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "cdm_host_clock.h"
|
||||
#include "cdm_host_file.h"
|
||||
#include "clock.h"
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_client_property_set.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# include "gtest/gtest_prod.h"
|
||||
#endif
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WvContentDecryptionModule_4 : public cdm::ContentDecryptionModule_4,
|
||||
public IFileFactory,
|
||||
public IClock,
|
||||
public WvCdmEventListener {
|
||||
public:
|
||||
explicit WvContentDecryptionModule_4(cdm::Host_4* host);
|
||||
|
||||
virtual ~WvContentDecryptionModule_4();
|
||||
|
||||
virtual void CreateSession(uint32_t session_id,
|
||||
const char* mime_type, uint32_t mime_type_size,
|
||||
const uint8_t* init_data, uint32_t init_data_size,
|
||||
cdm::SessionType session_type) OVERRIDE;
|
||||
|
||||
virtual void LoadSession(uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) OVERRIDE;
|
||||
|
||||
virtual void UpdateSession(uint32_t session_id, const uint8_t* response,
|
||||
uint32_t response_size) OVERRIDE;
|
||||
|
||||
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) OVERRIDE;
|
||||
|
||||
virtual void ReleaseSession(uint32_t session_id) OVERRIDE;
|
||||
|
||||
virtual void RemoveSession(uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) OVERRIDE;
|
||||
|
||||
virtual cdm::Status UsePrivacyMode() OVERRIDE;
|
||||
|
||||
virtual cdm::Status SetServerCertificate(
|
||||
const uint8_t* server_certificate_data,
|
||||
uint32_t server_certificate_data_size) OVERRIDE;
|
||||
|
||||
virtual void TimerExpired(void* context) OVERRIDE;
|
||||
|
||||
virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_buffer) OVERRIDE;
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRender(
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::StreamType stream_type) OVERRIDE;
|
||||
|
||||
virtual void Destroy() OVERRIDE;
|
||||
|
||||
// wvcdm::WvCdmEventListener implementation.
|
||||
virtual void OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) OVERRIDE;
|
||||
|
||||
private:
|
||||
class InternalSession {
|
||||
public:
|
||||
enum SessionType {
|
||||
kDecrypt = 1,
|
||||
kRelease = 2,
|
||||
kProvision = 3,
|
||||
};
|
||||
|
||||
InternalSession() : webid_(), session_type_(kDecrypt) {}
|
||||
InternalSession(const std::string& webid, SessionType session_type)
|
||||
: webid_(webid), session_type_(session_type) {}
|
||||
|
||||
const std::string& webid() const { return webid_; }
|
||||
bool is_decrypt() const { return session_type_ == kDecrypt; }
|
||||
bool is_release() const { return session_type_ == kRelease; }
|
||||
bool is_provision() const { return session_type_ == kProvision; }
|
||||
|
||||
private:
|
||||
std::string webid_;
|
||||
SessionType session_type_;
|
||||
};
|
||||
|
||||
void EnablePolicyTimer();
|
||||
void DisablePolicyTimer();
|
||||
|
||||
void CreateProvisionSession(uint32_t session_id);
|
||||
void UpdateProvisionSession(uint32_t session_id, const uint8_t* response,
|
||||
uint32_t response_size);
|
||||
|
||||
bool CallOpenSession(uint32_t session_id);
|
||||
|
||||
virtual File::Impl* NewFileImpl() OVERRIDE;
|
||||
|
||||
virtual int64_t GetCurrentTimeInSeconds() OVERRIDE;
|
||||
|
||||
/* |parameters| is expected to be initialized with anything not related to
|
||||
* subsample parsing. |iv| is initialized by the caller, but may be modified
|
||||
* during decryption. |decrypted_block| may be NULL for L1 decrypts, since
|
||||
* no data is passed back to the caller. */
|
||||
cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
cdm::Status DoDecrypt(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
|
||||
/* |parameters| is expected to be initialized with everything required by
|
||||
* DoSubsampleDecrypt and DoDecrypt, plus |is_encrypted| and
|
||||
* |subsample_flags|. Counters and |iv| will be updated to prepare for
|
||||
* subsequent calls. */
|
||||
cdm::Status DecryptAndUpdateCounters(CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
const size_t bytes,
|
||||
cdm::DecryptedBlock* decrypted_block,
|
||||
size_t& offset, size_t& encrypted_offset,
|
||||
uint32_t& block_ctr);
|
||||
|
||||
void SetSizesAndAllocate(size_t output_size,
|
||||
CdmDecryptionParameters& parameters,
|
||||
cdm::DecryptedBlock* decrypted_block);
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(CdmApi4Test, UsePrivacyMode);
|
||||
FRIEND_TEST(CdmApi4Test, UsePrivacyModeFailsWithOpenSessions);
|
||||
FRIEND_TEST(CdmApi4Test, SetExplicitServerCertificate);
|
||||
FRIEND_TEST(CdmApi4Test, SetServerCertificateFailsWithOpenSessions);
|
||||
#endif
|
||||
|
||||
cdm::Host_4* const host_;
|
||||
WVClientPropertySet property_set_;
|
||||
CdmEngine cdm_engine_;
|
||||
std::map<uint32_t, InternalSession> session_map_;
|
||||
bool timer_enabled_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule_4);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_
|
||||
18
cdm/oemcrypto_unittests.gypi
Normal file
18
cdm/oemcrypto_unittests.gypi
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Include this in any custom unit test targets.
|
||||
# Does not include the test runner main.
|
||||
{
|
||||
'sources': [
|
||||
'../oemcrypto/test/oemcrypto_test.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../core/include', # log.h
|
||||
'../oemcrypto/include',
|
||||
'../oemcrypto/mock/src', # oemcrypto_key_mock.h
|
||||
'../oemcrypto/test',
|
||||
],
|
||||
'defines': [
|
||||
'OEMCRYPTO_TESTS',
|
||||
],
|
||||
}
|
||||
889
cdm/src/cdm.cpp
Normal file
889
cdm/src/cdm.cpp
Normal file
@@ -0,0 +1,889 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#include "cdm.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include <vector>
|
||||
|
||||
// core:
|
||||
#include "cdm_client_property_set.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "file_store.h"
|
||||
#include "license.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
// CE:
|
||||
#include "cdm_version.h"
|
||||
#include "override.h"
|
||||
#include "properties_ce.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using namespace wvcdm;
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kPolicyTimerDurationMilliseconds = 5000;
|
||||
void* const kPolicyTimerContext = NULL;
|
||||
|
||||
struct HostType {
|
||||
Cdm::IStorage* storage;
|
||||
Cdm::IClock* clock;
|
||||
Cdm::ITimer* timer;
|
||||
CdmEngine* provisioning_engine;
|
||||
bool initialized;
|
||||
|
||||
HostType()
|
||||
: storage(NULL),
|
||||
clock(NULL),
|
||||
timer(NULL),
|
||||
provisioning_engine(NULL),
|
||||
initialized(false) {}
|
||||
} host;
|
||||
|
||||
class PropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
PropertySet()
|
||||
: use_privacy_mode_(false) {}
|
||||
|
||||
virtual ~PropertySet() {}
|
||||
|
||||
virtual const std::string& security_level() const OVERRIDE {
|
||||
// Unused on CE platforms. Used by Android to switch to L3.
|
||||
return empty_string_;
|
||||
}
|
||||
|
||||
void set_use_privacy_mode(bool use) {
|
||||
use_privacy_mode_ = use;
|
||||
}
|
||||
|
||||
virtual bool use_privacy_mode() const OVERRIDE {
|
||||
return use_privacy_mode_;
|
||||
}
|
||||
|
||||
virtual const std::string& service_certificate() const OVERRIDE {
|
||||
return service_certificate_;
|
||||
}
|
||||
|
||||
virtual void set_service_certificate(const std::string& cert) OVERRIDE {
|
||||
service_certificate_ = cert;
|
||||
}
|
||||
|
||||
virtual bool is_session_sharing_enabled() const OVERRIDE {
|
||||
// Unused on CE platforms.
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual uint32_t session_sharing_id() const OVERRIDE {
|
||||
// Unused on CE platforms.
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void set_session_sharing_id(uint32_t id) OVERRIDE {
|
||||
// Unused on CE platforms.
|
||||
return;
|
||||
}
|
||||
|
||||
virtual const std::string& app_id() const OVERRIDE {
|
||||
// Unused on CE platforms.
|
||||
return empty_string_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_privacy_mode_;
|
||||
std::string service_certificate_;
|
||||
|
||||
// This is empty, but g++ 4.8 will not allow app_id() to return a string
|
||||
// literal as a const reference to std::string.
|
||||
const std::string empty_string_;
|
||||
};
|
||||
|
||||
class CdmImpl : public Cdm,
|
||||
public Cdm::ITimer::IClient,
|
||||
public WvCdmEventListener {
|
||||
public:
|
||||
CdmImpl(IEventListener* listener,
|
||||
bool privacy_mode);
|
||||
|
||||
virtual ~CdmImpl();
|
||||
|
||||
// Cdm:
|
||||
virtual Status setServerCertificate(const std::string& certificate) OVERRIDE;
|
||||
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) OVERRIDE;
|
||||
|
||||
virtual Status generateRequest(const std::string& session_id,
|
||||
InitDataType init_data_type,
|
||||
const std::string& init_data) OVERRIDE;
|
||||
|
||||
virtual Status load(const std::string& session_id) OVERRIDE;
|
||||
|
||||
virtual Status update(const std::string& session_id,
|
||||
const std::string& response) OVERRIDE;
|
||||
|
||||
virtual Status getExpiration(const std::string& session_id,
|
||||
int64_t* expiration) OVERRIDE;
|
||||
|
||||
virtual Status getKeyStatuses(const std::string& session_id,
|
||||
KeyStatusMap* key_statuses) OVERRIDE;
|
||||
|
||||
virtual Status close(const std::string& session_id) OVERRIDE;
|
||||
|
||||
virtual Status remove(const std::string& session_id) OVERRIDE;
|
||||
|
||||
virtual Status decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) OVERRIDE;
|
||||
|
||||
// ITimer::IClient:
|
||||
virtual void onTimerExpired(void* context) OVERRIDE;
|
||||
|
||||
// WvCdmEventListener:
|
||||
virtual void OnSessionRenewalNeeded(const CdmSessionId& session_id) OVERRIDE;
|
||||
|
||||
virtual void OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) OVERRIDE;
|
||||
|
||||
virtual void OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
int64_t new_expiry_time_seconds) OVERRIDE;
|
||||
|
||||
private:
|
||||
IEventListener* listener_;
|
||||
bool policy_timer_enabled_;
|
||||
|
||||
CdmEngine cdm_engine_;
|
||||
PropertySet property_set_;
|
||||
|
||||
std::map<std::string, SessionType> new_session_types_;
|
||||
std::map<std::string, int64_t> session_expirations_;
|
||||
std::map<std::string, KeyStatusMap> session_key_statuses_;
|
||||
};
|
||||
|
||||
CdmImpl::CdmImpl(IEventListener* listener,
|
||||
bool privacy_mode)
|
||||
: listener_(listener),
|
||||
policy_timer_enabled_(false) {
|
||||
property_set_.set_use_privacy_mode(privacy_mode);
|
||||
}
|
||||
|
||||
CdmImpl::~CdmImpl() {}
|
||||
|
||||
Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
|
||||
if (!property_set_.use_privacy_mode()) {
|
||||
LOGE("Cannot set server certificate if privacy mode is disabled.");
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
if (certificate.empty()) {
|
||||
LOGE("An empty server certificate is invalid.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
if (CdmLicense::VerifySignedServiceCertificate(certificate) != NO_ERROR) {
|
||||
LOGE("Invalid server certificate!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
property_set_.set_service_certificate(certificate);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
std::string* session_id) {
|
||||
if (!session_id) {
|
||||
LOGE("Missing session ID pointer.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
// Important! The caller may pass a pre-filled string, which must be cleared
|
||||
// before being given to CdmEngine.
|
||||
session_id->clear();
|
||||
|
||||
switch (session_type) {
|
||||
case kTemporary:
|
||||
case kPersistent:
|
||||
break;
|
||||
default:
|
||||
LOGE("Unsupported session type: %d", session_type);
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, empty_origin, this,
|
||||
NULL, session_id);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
new_session_types_[*session_id] = session_type;
|
||||
return kSuccess;
|
||||
case NEED_PROVISIONING:
|
||||
LOGE("A device certificate is needed.");
|
||||
return kNeedsDeviceCertificate;
|
||||
default:
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
InitDataType init_data_type,
|
||||
const std::string& init_data) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
std::map<std::string, SessionType>::iterator it =
|
||||
new_session_types_.find(session_id);
|
||||
if (it == new_session_types_.end()) {
|
||||
LOGE("Request already generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
}
|
||||
|
||||
SessionType session_type = it->second;
|
||||
CdmLicenseType license_type;
|
||||
switch (session_type) {
|
||||
case kTemporary:
|
||||
license_type = kLicenseTypeStreaming;
|
||||
break;
|
||||
case kPersistent:
|
||||
license_type = kLicenseTypeOffline;
|
||||
break;
|
||||
default:
|
||||
LOGE("Unexpected session type: %d", session_type);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
std::string init_data_type_name;
|
||||
switch (init_data_type) {
|
||||
case kCenc:
|
||||
init_data_type_name = CENC_INIT_DATA_FORMAT;
|
||||
break;
|
||||
case kKeyIds:
|
||||
LOGE("Key IDs init data type is not supported.");
|
||||
return kNotSupported;
|
||||
case kWebM:
|
||||
init_data_type_name = WEBM_INIT_DATA_FORMAT;
|
||||
break;
|
||||
default:
|
||||
LOGE("Invalid init data type: %d", init_data_type);
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
InitializationData init_data_obj(init_data_type_name, init_data);
|
||||
|
||||
if (init_data_obj.IsEmpty()) {
|
||||
LOGE("Failed to parse init data.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
std::string key_request;
|
||||
CdmKeyRequestType key_request_type;
|
||||
std::string ignored_server_url;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, init_data_obj,
|
||||
license_type, empty_app_parameters, &key_request, &key_request_type,
|
||||
&ignored_server_url, NULL);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
new_session_types_.erase(it);
|
||||
assert(key_request_type == kKeyRequestTypeInitial);
|
||||
MessageType message_type = kLicenseRequest;
|
||||
if (property_set_.use_privacy_mode() &&
|
||||
property_set_.service_certificate().empty()) {
|
||||
// We can deduce that this is a server cert request, even though CdmEgine
|
||||
// cannot currently inform us of this.
|
||||
message_type = kIndividualizationRequest;
|
||||
LOGI("A server certificate request has been generated.");
|
||||
} else {
|
||||
LOGI("A license request has been generated.");
|
||||
}
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
if (session_id.empty()) {
|
||||
LOGE("Empty session ID.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
if (cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("Session ID already loaded.");
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
std::string ignored_session_id_output;
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, empty_origin, this,
|
||||
&session_id, &ignored_session_id_output);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
break;
|
||||
case NEED_PROVISIONING:
|
||||
LOGE("A device certificate is needed.");
|
||||
return kNeedsDeviceCertificate;
|
||||
default:
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
result = cdm_engine_.RestoreKey(session_id, session_id);
|
||||
if (result == GET_LICENSE_ERROR) {
|
||||
LOGE("Unable to load license: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
} else if (result == GET_RELEASED_LICENSE_ERROR) {
|
||||
// This was partially removed already.
|
||||
// The EME spec states that we should send a release message right away.
|
||||
InitializationData empty_initialization_data;
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
CdmKeyMessage key_request;
|
||||
std::string ignored_server_url;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, empty_app_parameters, &key_request, NULL,
|
||||
&ignored_server_url, NULL);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A license release has been generated.");
|
||||
MessageType message_type = kLicenseRelease;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
} else if (result != KEY_ADDED) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
const std::string& response) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
if (new_session_types_.find(session_id) != new_session_types_.end()) {
|
||||
LOGE("Request not yet generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
}
|
||||
|
||||
if (response.empty()) {
|
||||
LOGE("Empty response.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
bool predicted_to_be_server_cert_response =
|
||||
property_set_.use_privacy_mode() &&
|
||||
property_set_.service_certificate().empty();
|
||||
|
||||
// NOTE: If the CdmSession object recognizes that this is not the first
|
||||
// AddKey(), it will internally delegate to RenewKey().
|
||||
CdmKeySetId key_set_id = session_id;
|
||||
CdmResponseType result =
|
||||
cdm_engine_.AddKey(session_id, response, &key_set_id);
|
||||
|
||||
if (result == NEED_KEY) {
|
||||
// We just provisioned a server certificate.
|
||||
assert(predicted_to_be_server_cert_response);
|
||||
|
||||
// The cert is now available to all sessions in this CDM instance.
|
||||
// This is consistent with the behavior of the Chrome CDM.
|
||||
assert(!property_set_.service_certificate().empty());
|
||||
|
||||
// The underlying session in CdmEngine has stored a copy of the original
|
||||
// init data, so we can use an empty one this time.
|
||||
InitializationData empty_init_data;
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
std::string key_request;
|
||||
CdmKeyRequestType key_request_type;
|
||||
std::string ignored_server_url;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
|
||||
empty_app_parameters, &key_request, &key_request_type,
|
||||
&ignored_server_url, NULL);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A deferred license request has been generated.");
|
||||
assert(key_request_type == kKeyRequestTypeInitial);
|
||||
MessageType message_type = kLicenseRequest;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
return kSuccess;
|
||||
}
|
||||
assert(!predicted_to_be_server_cert_response);
|
||||
|
||||
if (result != KEY_ADDED) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
if (!policy_timer_enabled_) {
|
||||
policy_timer_enabled_ = true;
|
||||
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
|
||||
this,
|
||||
kPolicyTimerContext);
|
||||
}
|
||||
|
||||
if (cdm_engine_.IsReleaseSession(session_id)) {
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
listener_->onRemoveComplete(session_id);
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
|
||||
int64_t* expiration) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
*expiration = session_expirations_[session_id];
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getKeyStatuses(const std::string& session_id,
|
||||
KeyStatusMap* key_statuses) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
*key_statuses = session_key_statuses_[session_id];
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::close(const std::string& session_id) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.CloseSession(session_id);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::remove(const std::string& session_id) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
if (new_session_types_.find(session_id) != new_session_types_.end()) {
|
||||
LOGE("Request not yet generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
}
|
||||
|
||||
if (!cdm_engine_.IsOfflineSession(session_id)) {
|
||||
LOGE("Not a persistent session: %s", session_id.c_str());
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
InitializationData empty_initialization_data;
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
CdmKeyMessage key_request;
|
||||
std::string ignored_server_url;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, empty_app_parameters, &key_request, NULL,
|
||||
&ignored_server_url, NULL);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A license release has been generated.");
|
||||
MessageType message_type = kLicenseRelease;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) {
|
||||
if (input.is_encrypted && input.iv_length != 16) {
|
||||
LOGE("The IV must be 16 bytes long.");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
if (PropertiesCE::GetSecureOutputType() == kNoSecureOutput &&
|
||||
output.is_secure) {
|
||||
LOGE("The CDM is configured without secure output support.");
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
std::string key_id(reinterpret_cast<const char *>(input.key_id),
|
||||
input.key_id_length);
|
||||
std::vector<uint8_t> iv(input.iv, input.iv + input.iv_length);
|
||||
|
||||
CdmDecryptionParameters parameters;
|
||||
parameters.is_encrypted = input.is_encrypted;
|
||||
parameters.is_secure = output.is_secure;
|
||||
parameters.key_id = &key_id;
|
||||
parameters.encrypt_buffer = input.data;
|
||||
parameters.encrypt_length = input.data_length;
|
||||
parameters.iv = &iv;
|
||||
parameters.block_offset = input.block_offset;
|
||||
parameters.decrypt_buffer = output.data;
|
||||
parameters.decrypt_buffer_length = output.data_length;
|
||||
parameters.decrypt_buffer_offset = output.data_offset;
|
||||
parameters.subsample_flags =
|
||||
(input.first_subsample ? OEMCrypto_FirstSubsample : 0) |
|
||||
(input.last_subsample ? OEMCrypto_LastSubsample : 0);
|
||||
parameters.is_video = input.is_video;
|
||||
|
||||
CdmSessionId empty_session_id;
|
||||
CdmResponseType result = cdm_engine_.Decrypt(empty_session_id, parameters);
|
||||
if (result == NEED_KEY || result == SESSION_NOT_FOUND_FOR_DECRYPT) {
|
||||
LOGE("Key not available.");
|
||||
return kNoKey;
|
||||
}
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
LOGE("Decrypt error: %d", result);
|
||||
return kDecryptError;
|
||||
}
|
||||
|
||||
void CdmImpl::onTimerExpired(void* context) {
|
||||
if (context == kPolicyTimerContext) {
|
||||
if (policy_timer_enabled_) {
|
||||
cdm_engine_.OnTimerEvent();
|
||||
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
|
||||
this,
|
||||
kPolicyTimerContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
|
||||
CdmKeyMessage message;
|
||||
std::string server_url;
|
||||
CdmResponseType result =
|
||||
cdm_engine_.GenerateRenewalRequest(session_id, &message, &server_url);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGI("A license renewal has been generated.");
|
||||
MessageType message_type = kLicenseRenewal;
|
||||
listener_->onMessage(session_id, message_type, message);
|
||||
}
|
||||
|
||||
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) {
|
||||
KeyStatusMap& map = session_key_statuses_[session_id];
|
||||
|
||||
CdmKeyStatusMap::const_iterator it;
|
||||
for (it = keys_status.begin(); it != keys_status.end(); ++it) {
|
||||
KeyStatus status;
|
||||
switch (it->second) {
|
||||
case kKeyStatusUsable:
|
||||
map[it->first] = kUsable;
|
||||
break;
|
||||
case kKeyStatusExpired:
|
||||
map[it->first] = kExpired;
|
||||
break;
|
||||
case kKeyStatusOutputNotAllowed:
|
||||
map[it->first] = kOutputNotAllowed;
|
||||
break;
|
||||
case kKeyStatusPending:
|
||||
map[it->first] = kStatusPending;
|
||||
break;
|
||||
case kKeyStatusInternalError:
|
||||
map[it->first] = kInternalError;
|
||||
break;
|
||||
default:
|
||||
LOGE("Unrecognized key status: %d", it->second);
|
||||
map[it->first] = kInternalError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
listener_->onKeyStatusesChange(session_id);
|
||||
}
|
||||
|
||||
void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
int64_t new_expiry_time_seconds) {
|
||||
session_expirations_[session_id] = new_expiry_time_seconds * 1000;
|
||||
}
|
||||
|
||||
|
||||
bool VerifyL1() {
|
||||
CryptoSession cs;
|
||||
return cs.GetSecurityLevel() == kSecurityLevelL1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// static
|
||||
Cdm::Status Cdm::initialize(
|
||||
SecureOutputType secure_output_type,
|
||||
const ClientInfo& client_info,
|
||||
IStorage* storage,
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
DeviceCertificateRequest* device_certificate_request,
|
||||
LogLevel verbosity) {
|
||||
// If you want to direct-render on L3, CryptoSession will pass that request
|
||||
// along to OEMCrypto. But if you want to use an opaque handle on L3,
|
||||
// CryptoSession will silently ignore you and tell OEMCrypto to treat the
|
||||
// address as a clear buffer. :-(
|
||||
//
|
||||
// So this logic mirrors that in CryptoSession. Effectively, we are
|
||||
// detecting at init time the conditions that would prevent CryptoSession (in
|
||||
// its current form) from passing the desired buffer type constant to
|
||||
// OEMCrypto.
|
||||
// TODO: Discuss changes to CryptoSession.
|
||||
switch (secure_output_type) {
|
||||
case kOpaqueHandle:
|
||||
// This output type requires an OEMCrypto that reports L1.
|
||||
// This requirement comes from CryptoSession::SetDestinationBufferType().
|
||||
if (!VerifyL1()) {
|
||||
LOGE("Not an L1 implementation, kOpaqueHandle cannot be used!");
|
||||
return kNotSupported;
|
||||
}
|
||||
break;
|
||||
case kDirectRender:
|
||||
case kNoSecureOutput:
|
||||
break;
|
||||
default:
|
||||
LOGE("Invalid output type!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
if (client_info.product_name.empty() ||
|
||||
client_info.company_name.empty() ||
|
||||
client_info.model_name.empty()) {
|
||||
LOGE("Client info requires product_name, company_name, model_name!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
if (!storage || !clock || !timer) {
|
||||
LOGE("All interfaces are required!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
if (!device_certificate_request) {
|
||||
LOGE("Device certificate request pointer is required!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
// Our enum values match those in core/include/log.h
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
PropertiesCE::SetSecureOutputType(secure_output_type);
|
||||
PropertiesCE::SetClientInfo(client_info);
|
||||
Properties::Init();
|
||||
host.storage = storage;
|
||||
host.clock = clock;
|
||||
host.timer = timer;
|
||||
|
||||
device_certificate_request->needed = false;
|
||||
|
||||
if (!host.provisioning_engine) {
|
||||
host.provisioning_engine = new CdmEngine();
|
||||
}
|
||||
bool has_cert = host.provisioning_engine->IsProvisioned(
|
||||
kSecurityLevelL1, "" /* origin */);
|
||||
|
||||
if (!has_cert) {
|
||||
device_certificate_request->needed = true;
|
||||
std::string empty_authority;
|
||||
std::string empty_origin;
|
||||
std::string base_url;
|
||||
std::string signed_request;
|
||||
CdmResponseType result = host.provisioning_engine->GetProvisioningRequest(
|
||||
kCertificateWidevine, empty_authority, empty_origin,
|
||||
&signed_request, &base_url);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
device_certificate_request->url = base_url;
|
||||
device_certificate_request->url.append("&signedRequest=");
|
||||
device_certificate_request->url.append(signed_request);
|
||||
}
|
||||
|
||||
host.initialized = true;
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
// static
|
||||
const char* Cdm::version() {
|
||||
return CDM_VERSION;
|
||||
}
|
||||
|
||||
// static
|
||||
Cdm* Cdm::create(IEventListener* listener,
|
||||
bool privacy_mode) {
|
||||
if (!host.initialized) {
|
||||
LOGE("Not initialized!");
|
||||
return NULL;
|
||||
}
|
||||
if (!listener) {
|
||||
LOGE("No listener!");
|
||||
return NULL;
|
||||
}
|
||||
return new CdmImpl(listener, privacy_mode);
|
||||
}
|
||||
|
||||
Cdm::Status Cdm::DeviceCertificateRequest::acceptReply(
|
||||
const std::string& reply) {
|
||||
if (!host.provisioning_engine) {
|
||||
LOGE("Provisioning reply received while not in a provisioning state!");
|
||||
return kInvalidAccess;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
std::string ignored_cert;
|
||||
std::string ignored_wrapped_key;
|
||||
|
||||
CdmResponseType result =
|
||||
host.provisioning_engine->HandleProvisioningResponse(
|
||||
empty_origin, reply, &ignored_cert, &ignored_wrapped_key);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
// Missing symbols from core:
|
||||
namespace wvcdm {
|
||||
|
||||
using namespace widevine;
|
||||
|
||||
int64_t Clock::GetCurrentTime() {
|
||||
return host.clock->now() / 1000;
|
||||
}
|
||||
|
||||
struct File::Impl {
|
||||
std::string name;
|
||||
bool read_only;
|
||||
bool truncate;
|
||||
};
|
||||
|
||||
File::File() : impl_(NULL) {}
|
||||
|
||||
File::~File() {
|
||||
Close();
|
||||
}
|
||||
|
||||
bool File::Open(const std::string& file_path, int flags) {
|
||||
if (!(flags & kCreate) && !host.storage->exists(file_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
impl_ = new Impl;
|
||||
impl_->name = file_path;
|
||||
impl_->read_only = (flags & kReadOnly);
|
||||
impl_->truncate = (flags & kTruncate);
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t File::Read(char* buffer, size_t bytes) {
|
||||
if (!impl_) {
|
||||
return -1;
|
||||
}
|
||||
std::string data;
|
||||
if (!host.storage->read(impl_->name, &data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t to_copy = std::min(bytes, data.size());
|
||||
memcpy(buffer, data.data(), to_copy);
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
ssize_t File::Write(const char* buffer, size_t bytes) {
|
||||
if (!impl_) {
|
||||
return -1;
|
||||
}
|
||||
if (!impl_->truncate) {
|
||||
LOGE("Internal error: files cannot be appended to.");
|
||||
return -1;
|
||||
}
|
||||
std::string data(buffer, bytes);
|
||||
if (!host.storage->write(impl_->name, data)) {
|
||||
return -1;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void File::Close() {
|
||||
if (impl_) {
|
||||
delete impl_;
|
||||
}
|
||||
impl_ = NULL;
|
||||
}
|
||||
|
||||
bool File::Exists(const std::string& file_path) {
|
||||
return host.storage->exists(file_path);
|
||||
}
|
||||
|
||||
bool File::Remove(const std::string& file_path) {
|
||||
return host.storage->remove(file_path);
|
||||
}
|
||||
|
||||
bool File::Copy(const std::string& old_path, const std::string& new_path) {
|
||||
std::string data;
|
||||
bool read_ok = host.storage->read(old_path, &data);
|
||||
if (!read_ok) return false;
|
||||
return host.storage->write(new_path, data);
|
||||
}
|
||||
|
||||
bool File::List(const std::string& path, std::vector<std::string>* files) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::CreateDirectory(const std::string dir_path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::IsDirectory(const std::string& dir_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::IsRegularFile(const std::string& file_path) {
|
||||
return host.storage->exists(file_path);
|
||||
}
|
||||
|
||||
ssize_t File::FileSize(const std::string& file_path) {
|
||||
return host.storage->size(file_path);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "wv_content_decryption_module_1.h"
|
||||
#include "wv_content_decryption_module_4.h"
|
||||
|
||||
#include "wv_cdm_version.h"
|
||||
|
||||
void InitializeCdmModule() {}
|
||||
|
||||
void DeinitializeCdmModule() {}
|
||||
|
||||
void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
|
||||
uint32_t key_system_size,
|
||||
GetCdmHostFunc get_cdm_host_func, void* user_data) {
|
||||
void *host = NULL;
|
||||
|
||||
switch (cdm_interface_version) {
|
||||
case cdm::ContentDecryptionModule_1::kVersion:
|
||||
host = get_cdm_host_func(cdm::Host_1::kVersion, user_data);
|
||||
break;
|
||||
|
||||
case cdm::ContentDecryptionModule_4::kVersion:
|
||||
host = get_cdm_host_func(cdm::Host_4::kVersion, user_data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!host)
|
||||
return NULL;
|
||||
|
||||
switch (cdm_interface_version) {
|
||||
case cdm::ContentDecryptionModule_1::kVersion:
|
||||
return new wvcdm::WvContentDecryptionModule_1(
|
||||
static_cast<cdm::Host_1*>(host));
|
||||
|
||||
case cdm::ContentDecryptionModule_4::kVersion:
|
||||
return new wvcdm::WvContentDecryptionModule_4(
|
||||
static_cast<cdm::Host_4*>(host));
|
||||
}
|
||||
|
||||
assert(false); // NOT REACHED
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* GetCdmVersion() {
|
||||
return WV_CDM_VERSION;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "clock.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "cdm_host_clock.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
IClock* HostClock::impl_ = NULL;
|
||||
|
||||
IClock::~IClock() {
|
||||
HostClock::impl_ = NULL;
|
||||
}
|
||||
|
||||
int64_t Clock::GetCurrentTime() {
|
||||
return HostClock::impl_ ?
|
||||
HostClock::impl_->GetCurrentTimeInSeconds() : -1;
|
||||
}
|
||||
|
||||
void HostClock::SetClockInterface(IClock* iclock) {
|
||||
HostClock::impl_ = iclock;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,222 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "cdm_host_file.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
IFileFactory* File::Impl::factory_ = NULL;
|
||||
|
||||
// File::Impl() Section
|
||||
// The file handler for cert.bin, aka DeviceCertificate, and usage.bin,
|
||||
// aka UsageTable, are all we're setting up for now.
|
||||
|
||||
namespace {
|
||||
|
||||
const char* const kDeviceCertificateKey = "DeviceCertificate";
|
||||
const char* const kUsageInfoKey = "UsageInfo";
|
||||
const char* const kUsageTableKey = "MockOemCryptoUsageTable";
|
||||
const char* const kGenerationNumberKey = "MockOemCryptoGenerationNumber";
|
||||
|
||||
const char* GetKeyForFileName(const std::string& name) {
|
||||
if (name == "cert.bin") {
|
||||
return kDeviceCertificateKey;
|
||||
} else if (name == "usage.bin") {
|
||||
return kUsageInfoKey;
|
||||
} else if (name == "UsageTable.dat") {
|
||||
return kUsageTableKey;
|
||||
} else if (name == "GenerationNumber.dat") {
|
||||
return kGenerationNumberKey;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// File1 implmentation.
|
||||
bool File1Impl::Open(const std::string& name) {
|
||||
if (name.empty())
|
||||
return false;
|
||||
fname_ = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t File1Impl::Read(char* buffer, size_t bytes) {
|
||||
std::string key(GetKeyForFileName(fname_));
|
||||
if (key.length() > 0) {
|
||||
std::string value;
|
||||
host_1_->GetPlatformString(key, &value);
|
||||
size_t bytes_to_copy = std::min(bytes, value.size());
|
||||
memcpy(buffer, value.data(), bytes_to_copy);
|
||||
return value.size() ? bytes_to_copy : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool File1Impl::Remove(const std::string& name) {
|
||||
std::string key(GetKeyForFileName(name));
|
||||
if (key.length() > 0) {
|
||||
host_1_->SetPlatformString(key, "");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t File1Impl::FileSize(const std::string& name) {
|
||||
std::string key(GetKeyForFileName(name));
|
||||
if (key.length() > 0) {
|
||||
std::string value;
|
||||
host_1_->GetPlatformString(key, &value);
|
||||
return value.empty() ? -1 : value.size();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t File1Impl::Write(const char* buffer, size_t bytes) {
|
||||
std::string key(GetKeyForFileName(fname_));
|
||||
if (key.length() > 0) {
|
||||
std::string value(buffer, bytes);
|
||||
host_1_->SetPlatformString(key, value);
|
||||
return bytes;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool File1Impl::Close() {
|
||||
fname_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// File 4 Implementation.
|
||||
bool File4Impl::Open(const std::string& name) {
|
||||
return host_4_file_io_client_.Open(name);
|
||||
}
|
||||
|
||||
ssize_t File4Impl::Read(char* buffer, size_t bytes) {
|
||||
if (host_4_file_io_client_.Read(buffer, bytes) &&
|
||||
host_4_file_io_client_.data_size() > 0) {
|
||||
return std::min<size_t>(host_4_file_io_client_.data_size(), bytes);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t File4Impl::Write(const char* buffer, size_t bytes) {
|
||||
if (host_4_file_io_client_.Write(buffer, bytes)) {
|
||||
return bytes;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool File4Impl::Close() {
|
||||
return host_4_file_io_client_.Close();
|
||||
}
|
||||
|
||||
bool File4Impl::Remove(const std::string& name) {
|
||||
Host4FileIOClient file_io_client(host_4_);
|
||||
return file_io_client.Open(name) && file_io_client.Write("", 0);
|
||||
}
|
||||
|
||||
ssize_t File4Impl::FileSize(const std::string& name) {
|
||||
Host4FileIOClient file_io_client(host_4_);
|
||||
if (file_io_client.Open(name) && file_io_client.ReadFileSize() &&
|
||||
file_io_client.data_size() > 0) {
|
||||
return file_io_client.data_size();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Common file implementation.
|
||||
bool File::Impl::Open(const std::string& name) {
|
||||
return file_api_->Open(name);
|
||||
}
|
||||
|
||||
ssize_t File::Impl::Read(char* buffer, size_t bytes) {
|
||||
return file_api_->Read(buffer, bytes);
|
||||
}
|
||||
|
||||
ssize_t File::Impl::Write(const char* buffer, size_t bytes) {
|
||||
return file_api_->Write(buffer, bytes);
|
||||
}
|
||||
|
||||
bool File::Impl::Close() {
|
||||
return file_api_->Close();
|
||||
}
|
||||
|
||||
bool File::Impl::Exists(const std::string& name) {
|
||||
return FileSize(name) > 0;
|
||||
}
|
||||
|
||||
bool File::Impl::Remove(const std::string& name) {
|
||||
return file_api_->Remove(name);
|
||||
}
|
||||
|
||||
ssize_t File::Impl::FileSize(const std::string& name) {
|
||||
return file_api_->FileSize(name);
|
||||
}
|
||||
|
||||
File::File() : impl_(File::Impl::factory_->NewFileImpl()) {}
|
||||
|
||||
File::~File() {
|
||||
Close();
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
bool File::Open(const std::string& name, int flags) {
|
||||
return impl_->Open(name);
|
||||
}
|
||||
|
||||
void File::Close() { impl_->Close(); }
|
||||
|
||||
ssize_t File::Read(char* buffer, size_t bytes) {
|
||||
return impl_->Read(buffer, bytes);
|
||||
}
|
||||
|
||||
ssize_t File::Write(const char* buffer, size_t bytes) {
|
||||
return impl_->Write(buffer, bytes);
|
||||
}
|
||||
|
||||
bool File::Exists(const std::string& path) { return impl_->Exists(path); }
|
||||
|
||||
bool File::Remove(const std::string& path) { return impl_->Remove(path); }
|
||||
|
||||
bool File::Copy(const std::string& from, const std::string& to) {
|
||||
// Required for linkage only - no API implementation is needed by the CDM
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::List(const std::string& path, std::vector<std::string>* files) {
|
||||
// Required for linkage only - no API implementation is needed by the CDM
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::CreateDirectory(std::string path) {
|
||||
// Required for linkage only - no API implementation is needed by the CDM
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::IsDirectory(const std::string& path) {
|
||||
// Required for linkage only - no API implementation is needed by the CDM
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::IsRegularFile(const std::string& path) {
|
||||
// Required for linkage only - no API implementation is needed by the CDM
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t File::FileSize(const std::string& path) {
|
||||
return impl_->FileSize(path);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "host_4_file_io_client.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
Host4FileIOClient::~Host4FileIOClient() {
|
||||
if (file_io_ != NULL) Close();
|
||||
}
|
||||
|
||||
bool Host4FileIOClient::Open(const std::string& name) {
|
||||
if (host_ == NULL) return false;
|
||||
if (file_io_ != NULL) return false;
|
||||
file_io_ = host_->CreateFileIO(this);
|
||||
file_io_->Open(name.data(), name.length());
|
||||
return status_ == kSuccess;
|
||||
}
|
||||
|
||||
bool Host4FileIOClient::Read(char* buffer, size_t buffer_size) {
|
||||
if (file_io_ == NULL) return false;
|
||||
buffer_ = buffer;
|
||||
buffer_size_ = buffer_size;
|
||||
file_io_->Read();
|
||||
return status_ == kSuccess;
|
||||
}
|
||||
|
||||
bool Host4FileIOClient::Write(const char* data, size_t data_size) {
|
||||
if (file_io_ == NULL) return false;
|
||||
file_io_->Write(reinterpret_cast<const uint8_t*>(data), data_size);
|
||||
return status_ == kSuccess;
|
||||
}
|
||||
|
||||
bool Host4FileIOClient::Close() {
|
||||
if (file_io_ == NULL) return false;
|
||||
file_io_->Close();
|
||||
file_io_ = NULL;
|
||||
status_ = kSuccess;
|
||||
data_size_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Host4FileIOClient::OnOpenComplete(Status status) {
|
||||
status_ = status;
|
||||
data_size_ = 0;
|
||||
}
|
||||
|
||||
void Host4FileIOClient::OnReadComplete(Status status, const uint8_t* data,
|
||||
uint32_t data_size) {
|
||||
status_ = status;
|
||||
data_size_ = data_size;
|
||||
if (buffer_ != NULL) {
|
||||
memcpy(buffer_, data, std::min<size_t>(data_size, buffer_size_));
|
||||
buffer_ = NULL;
|
||||
buffer_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Host4FileIOClient::OnWriteComplete(Status status) {
|
||||
status_ = status;
|
||||
data_size_ = 0;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "host_event_listener.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
void HostEventListener::OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) {
|
||||
switch (cdm_event) {
|
||||
case LICENSE_RENEWAL_NEEDED_EVENT: {
|
||||
wvcdm::CdmKeyMessage cdm_message;
|
||||
std::string server_url;
|
||||
CdmResponseType result = cdm_engine_->GenerateRenewalRequest(
|
||||
session_id, &cdm_message, &server_url);
|
||||
if (result == wvcdm::KEY_MESSAGE) {
|
||||
host_->SendKeyMessage(session_id.data(), session_id.length(),
|
||||
cdm_message.data(), cdm_message.length(),
|
||||
server_url.data(), server_url.length());
|
||||
} else {
|
||||
LOGD("Error on Generating a Renewal Request!");
|
||||
host_->SendKeyError(session_id.data(), session_id.size(),
|
||||
cdm::kUnknownError, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LICENSE_EXPIRED_EVENT: {
|
||||
host_->SendKeyError(session_id.data(), session_id.size(),
|
||||
cdm::kUnknownError, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
32
cdm/src/lock.cpp
Normal file
32
cdm/src/lock.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Lock class - provides a simple mutex implementation.
|
||||
|
||||
#include "lock.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
struct Lock::Impl {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
Lock::Lock() : impl_(new Lock::Impl()) {
|
||||
pthread_mutex_init(&impl_->mutex, NULL);
|
||||
}
|
||||
|
||||
Lock::~Lock() {
|
||||
pthread_mutex_destroy(&impl_->mutex);
|
||||
delete impl_;
|
||||
}
|
||||
|
||||
void Lock::Acquire() {
|
||||
pthread_mutex_lock(&impl_->mutex);
|
||||
}
|
||||
|
||||
void Lock::Release() {
|
||||
pthread_mutex_unlock(&impl_->mutex);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
38
cdm/src/log.cpp
Normal file
38
cdm/src/log.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Log - implemented using stderr.
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
LogPriority g_cutoff = LOG_WARN;
|
||||
|
||||
void InitLogging() {}
|
||||
|
||||
void Log(const char* file, int line, LogPriority level, const char* fmt, ...) {
|
||||
const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" };
|
||||
if (level >= sizeof(severities) / sizeof(*severities)) {
|
||||
fprintf(stderr, "[FATAL:%s(%d)] Invalid log priority level: %d\n",
|
||||
file, line, level);
|
||||
return;
|
||||
}
|
||||
if (level > g_cutoff) return;
|
||||
|
||||
fprintf(stderr, "[%s:%s(%d)] ", severities[level], file, line);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
putc('\n', stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
146
cdm/src/properties_ce.cpp
Normal file
146
cdm/src/properties_ce.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#include "properties.h"
|
||||
#include "properties_ce.h"
|
||||
|
||||
#include "cdm_version.h"
|
||||
#include "log.h"
|
||||
|
||||
// This anonymous namespace is shared between both the widevine namespace and
|
||||
// wvcdm namespace objects below.
|
||||
namespace {
|
||||
|
||||
bool use_secure_buffers_ = false;
|
||||
bool use_fifo_ = false;
|
||||
bool use_userspace_buffers_ = true;
|
||||
|
||||
widevine::Cdm::SecureOutputType secure_output_type_ =
|
||||
widevine::Cdm::kNoSecureOutput;
|
||||
widevine::Cdm::ClientInfo client_info_;
|
||||
|
||||
bool GetValue(const std::string& source, std::string* output) {
|
||||
if (!output) {
|
||||
return false;
|
||||
}
|
||||
*output = source;
|
||||
return source.size() != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// static
|
||||
void PropertiesCE::SetSecureOutputType(
|
||||
Cdm::SecureOutputType secure_output_type) {
|
||||
secure_output_type_ = secure_output_type;
|
||||
|
||||
switch (secure_output_type) {
|
||||
case Cdm::kOpaqueHandle:
|
||||
use_secure_buffers_ = true;
|
||||
use_fifo_ = false;
|
||||
use_userspace_buffers_ = false;
|
||||
break;
|
||||
case Cdm::kDirectRender:
|
||||
use_secure_buffers_ = false;
|
||||
use_fifo_ = true;
|
||||
use_userspace_buffers_ = false;
|
||||
break;
|
||||
case Cdm::kNoSecureOutput:
|
||||
default:
|
||||
use_secure_buffers_ = false;
|
||||
use_fifo_ = false;
|
||||
use_userspace_buffers_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
Cdm::SecureOutputType PropertiesCE::GetSecureOutputType() {
|
||||
return secure_output_type_;
|
||||
}
|
||||
|
||||
// static
|
||||
void PropertiesCE::SetClientInfo(const Cdm::ClientInfo& client_info) {
|
||||
client_info_ = client_info;
|
||||
}
|
||||
|
||||
// static
|
||||
Cdm::ClientInfo PropertiesCE::GetClientInfo() {
|
||||
return client_info_;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// static
|
||||
void Properties::Init() {
|
||||
oem_crypto_use_secure_buffers_ = use_secure_buffers_;
|
||||
oem_crypto_use_fifo_ = use_fifo_;
|
||||
oem_crypto_use_userspace_buffers_ = use_userspace_buffers_;
|
||||
use_certificates_as_identification_ = true;
|
||||
security_level_path_backward_compatibility_support_ = false;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetCompanyName(std::string* company_name) {
|
||||
return GetValue(client_info_.company_name, company_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetModelName(std::string* model_name) {
|
||||
return GetValue(client_info_.model_name, model_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetArchitectureName(std::string* arch_name) {
|
||||
return GetValue(client_info_.arch_name, arch_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetDeviceName(std::string* device_name) {
|
||||
return GetValue(client_info_.device_name, device_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetProductName(std::string* product_name) {
|
||||
return GetValue(client_info_.product_name, product_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetBuildInfo(std::string* build_info) {
|
||||
return GetValue(client_info_.build_info, build_info);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetWVCdmVersion(std::string* version) {
|
||||
return GetValue(CDM_VERSION, version);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
||||
std::string* base_path) {
|
||||
// A no-op, but successful.
|
||||
base_path->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
|
||||
// Unused on CE devices.
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetOEMCryptoPath(std::string* library_name) {
|
||||
// Unused on CE devices.
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::AlwaysUseKeySetIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "properties.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
bool Properties::GetCompanyName(std::string* company_name) {
|
||||
if (!company_name) {
|
||||
LOGW("Properties::GetCompanyName: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*company_name = PLATFORM_COMPANY_NAME_WV;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::GetModelName(std::string* model_name) {
|
||||
if (!model_name) {
|
||||
LOGW("Properties::GetModelName: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*model_name = PLATFORM_MODEL_NAME_WV;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Properties::GetArchitectureName(std::string* arch_name) {
|
||||
if (!arch_name) {
|
||||
LOGW("Properties::GetArchitectureName: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*arch_name = PLATFORM_ARCHITECTURE_NAME_WV;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Properties::GetDeviceName(std::string* device_name) {
|
||||
if (!device_name) {
|
||||
LOGW("Properties::GetDeviceName: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*device_name = PLATFORM_DEVICE_NAME_WV;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Properties::GetProductName(std::string* product_name) {
|
||||
if (!product_name) {
|
||||
LOGW("Properties::GetProductName: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*product_name = PLATFORM_PRODUCT_NAME_WV;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Properties::GetBuildInfo(std::string* build_info) {
|
||||
if (!build_info) {
|
||||
LOGW("Properties::GetBuildInfo: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
*build_info = PLATFORM_BUILDINFO_WV;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Properties::GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
||||
std::string* base_path) {
|
||||
// no-op
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
|
||||
if (!keybox) {
|
||||
LOGW("Properties::GetFactoryKeyboxPath: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::GetOEMCryptoPath(std::string* library_name) {
|
||||
LOGE("Properties::GetOEMCryptoPath: Unimplemented property");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,422 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "wv_content_decryption_module_1.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "log.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
// individual error codes
|
||||
kAttachEventListenerError = 0x0001,
|
||||
|
||||
// error classes to be OR'd with cdm engine result values
|
||||
kOpenSessionErrorBase = 0x0100,
|
||||
kGenerateKeyRequestErrorBase = 0x0200,
|
||||
kAddKeyErrorBase = 0x0300,
|
||||
};
|
||||
|
||||
const int kCdmPolicyTimerDurationSeconds = 5;
|
||||
|
||||
// The iso spec only uses the lower 8 bytes of the iv as
|
||||
// the counter.
|
||||
const uint32_t kCencIvSize = 8;
|
||||
const uint32_t kIvSize = 16;
|
||||
|
||||
bool Ctr128Add(size_t block_count, uint8_t* counter) {
|
||||
if (NULL == counter) return false;
|
||||
if (0 == block_count) return true;
|
||||
uint8_t carry = 0;
|
||||
uint8_t n = kIvSize - 1;
|
||||
while (n >= kCencIvSize) {
|
||||
uint32_t temp = block_count & 0xff;
|
||||
temp += counter[n];
|
||||
temp += carry;
|
||||
counter[n] = temp & 0xff;
|
||||
carry = (temp & 0x100) ? 1 : 0;
|
||||
block_count = block_count >> 8;
|
||||
n--;
|
||||
if (!block_count && !carry) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// cdm::ContentDecryptionModule_1 implementation.
|
||||
|
||||
WvContentDecryptionModule_1::WvContentDecryptionModule_1(cdm::Host_1* host)
|
||||
: host_(host),
|
||||
host_event_listener_(host, &cdm_engine_),
|
||||
cdm_engine_(),
|
||||
property_set_(),
|
||||
timer_enabled_(false) {
|
||||
HostClock::SetClockInterface(this);
|
||||
}
|
||||
|
||||
WvContentDecryptionModule_1::~WvContentDecryptionModule_1() {
|
||||
DisablePolicyTimer();
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::GenerateKeyRequest(
|
||||
const char* type, int type_size, const uint8_t* init_data,
|
||||
int init_data_size) {
|
||||
LOGI("WvContentDecryptionModule_1::GenerateKeyRequest()");
|
||||
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
|
||||
init_data_size);
|
||||
std::string type_string(type, type_size);
|
||||
InitializationData initialization_data(type_string,
|
||||
init_data_internal);
|
||||
CdmKeyMessage key_request;
|
||||
CdmSessionId session_id;
|
||||
|
||||
std::string security_level;
|
||||
std::string privacy_mode;
|
||||
std::string service_certificate;
|
||||
|
||||
host_->GetPlatformString("SecurityLevel", &security_level);
|
||||
host_->GetPlatformString("PrivacyOn", &privacy_mode);
|
||||
host_->GetPlatformString("ServiceCertificate", &service_certificate);
|
||||
|
||||
property_set_.set_security_level(security_level);
|
||||
property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0);
|
||||
property_set_.set_service_certificate(service_certificate);
|
||||
|
||||
CdmResponseType result = cdm_engine_.OpenSession("com.widevine.alpha",
|
||||
&property_set_, &session_id);
|
||||
|
||||
if (result == NEED_PROVISIONING) {
|
||||
LOGI("Need to aquire a Device Certificate from the Provisioning Server");
|
||||
return cdm::kNeedsDeviceCertificate;
|
||||
}
|
||||
|
||||
if (result != NO_ERROR) {
|
||||
host_->SendKeyError("", 0, cdm::kClientError,
|
||||
kOpenSessionErrorBase | result);
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
if (!cdm_engine_.AttachEventListener(session_id, &host_event_listener_)) {
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
host_->SendKeyError("", 0, cdm::kClientError, kAttachEventListenerError);
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
CdmAppParameterMap app_parameters; // empty
|
||||
CdmKeySetId key_set_id; // empty
|
||||
std::string server_url;
|
||||
|
||||
result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, key_set_id, initialization_data, kLicenseTypeStreaming,
|
||||
app_parameters, &key_request, &server_url, NULL);
|
||||
if (KEY_MESSAGE != result) {
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
host_->SendKeyError("", 0, cdm::kClientError,
|
||||
kGenerateKeyRequestErrorBase | result);
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
host_->SendKeyMessage(session_id.data(), session_id.length(),
|
||||
key_request.data(), key_request.length(),
|
||||
server_url.data(), server_url.length());
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::AddKey(
|
||||
const char* session_id, int session_id_size, const uint8_t* key,
|
||||
int key_size, const uint8_t* key_id, int key_id_size) {
|
||||
LOGI("WvContentDecryptionModule_1::AddKey()");
|
||||
CdmSessionId session_id_internal(session_id, session_id_size);
|
||||
CdmKeyResponse key_data((const char*)key, key_size);
|
||||
CdmKeySetId key_set_id;
|
||||
|
||||
CdmResponseType response =
|
||||
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
|
||||
|
||||
if (response == KEY_ADDED) {
|
||||
EnablePolicyTimer();
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
host_->SendKeyError(session_id, session_id_size, cdm::kClientError,
|
||||
kAddKeyErrorBase | response);
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
bool WvContentDecryptionModule_1::IsKeyValid(const uint8_t* key_id,
|
||||
int key_id_size) {
|
||||
KeyId key(reinterpret_cast<const char*>(key_id), key_id_size);
|
||||
return cdm_engine_.IsKeyLoaded(key);
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::CloseSession(const char* session_id,
|
||||
int session_id_size) {
|
||||
LOGI("WvContentDecryptionModule_1::CloseSession()");
|
||||
CdmSessionId session_id_internal(session_id, session_id_size);
|
||||
return cdm_engine_.CloseSession(session_id_internal) == NO_ERROR
|
||||
? cdm::kSuccess
|
||||
: cdm::kSessionError;
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_1::TimerExpired(void* context) {
|
||||
LOGI("WvContentDecryptionModule_1::TimerExpired()");
|
||||
if (timer_enabled_) {
|
||||
cdm_engine_.OnTimerEvent();
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::Decrypt(
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
LOGI("WvContentDecryptionModule_1::Decrypt()");
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
std::vector<uint8_t> iv(KEY_IV_SIZE);
|
||||
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
|
||||
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
|
||||
encrypted_buffer.key_id_size);
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
|
||||
}
|
||||
|
||||
// This is the Level 1 API. When the host application calls the CDM's
|
||||
// DecryptDecodeAndRenderFrame(), rather than the CDM's Decrypt(),
|
||||
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no
|
||||
// cleartext in the return.
|
||||
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame(
|
||||
const cdm::InputBuffer& encrypted_buffer) {
|
||||
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame()");
|
||||
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
std::vector<uint8_t> iv(KEY_IV_SIZE);
|
||||
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
|
||||
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
|
||||
encrypted_buffer.key_id_size);
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
}
|
||||
|
||||
// This is the Level 1 API. When the host application calls the CDM's
|
||||
// DecryptDecodeAndRenderSamples(), rather than the CDM's Decrypt(),
|
||||
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no cleartext
|
||||
// in the return.
|
||||
cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples(
|
||||
const cdm::InputBuffer& encrypted_buffer) {
|
||||
LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples()");
|
||||
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
std::vector<uint8_t> iv(KEY_IV_SIZE);
|
||||
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
|
||||
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
|
||||
encrypted_buffer.key_id_size);
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_video = false; // override the default true value for audio.
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_1::Destroy() { delete this; }
|
||||
|
||||
// Provisioning related methods
|
||||
cdm::Status WvContentDecryptionModule_1::GetProvisioningRequest(
|
||||
std::string* request, std::string* provisioning_server_url) {
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
if (cdm_engine_.GetProvisioningRequest(
|
||||
cert_type, cert_authority,
|
||||
static_cast<CdmProvisioningRequest*>(request),
|
||||
provisioning_server_url) == NO_ERROR) {
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::HandleProvisioningResponse(
|
||||
std::string& response) {
|
||||
std::string cert, wrapped_key;
|
||||
if (cdm_engine_.HandleProvisioningResponse(
|
||||
static_cast<CdmProvisioningRequest&>(response), &cert,
|
||||
&wrapped_key) == NO_ERROR) {
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_1::EnablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_1::EnablePolicyTimer()");
|
||||
if (!timer_enabled_) {
|
||||
timer_enabled_ = true;
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_1::DisablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_1::DisablePolicyTimer()");
|
||||
timer_enabled_ = false;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::DoSubsampleDecrypt(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
if (!encrypted_buffer.subsamples)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
size_t output_size = 0;
|
||||
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
|
||||
output_size += subsample.cipher_bytes + subsample.clear_bytes;
|
||||
}
|
||||
assert(output_size ==
|
||||
encrypted_buffer.data_size - encrypted_buffer.data_offset);
|
||||
|
||||
SetSizesAndAllocate(output_size, parameters, decrypted_block);
|
||||
|
||||
size_t offset = 0;
|
||||
size_t encrypted_offset = 0;
|
||||
uint32_t block_ctr = 0;
|
||||
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
|
||||
|
||||
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = subsamples[i];
|
||||
|
||||
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
|
||||
size_t bytes =
|
||||
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
|
||||
if (bytes == 0) continue;
|
||||
|
||||
parameters.is_encrypted = is_encrypted;
|
||||
parameters.subsample_flags =
|
||||
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
|
||||
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
|
||||
|
||||
cdm::Status status =
|
||||
DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
|
||||
bytes, decrypted_block, offset,
|
||||
encrypted_offset, block_ctr);
|
||||
if (status != cdm::kSuccess)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::DoDecrypt(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
assert(!encrypted_buffer.subsamples);
|
||||
size_t output_size =
|
||||
encrypted_buffer.data_size - encrypted_buffer.data_offset;
|
||||
SetSizesAndAllocate(output_size, parameters, decrypted_block);
|
||||
|
||||
size_t offset = 0;
|
||||
size_t encrypted_offset = 0;
|
||||
uint32_t block_ctr = 0;
|
||||
|
||||
parameters.is_encrypted = true;
|
||||
parameters.subsample_flags =
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
|
||||
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
|
||||
output_size, decrypted_block, offset,
|
||||
encrypted_offset, block_ctr);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_1::SetSizesAndAllocate(
|
||||
size_t output_size,
|
||||
CdmDecryptionParameters& parameters,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
parameters.decrypt_buffer_length = output_size;
|
||||
|
||||
if (decrypted_block) {
|
||||
cdm::Buffer* buffer = host_->Allocate(output_size);
|
||||
buffer->SetSize(output_size);
|
||||
decrypted_block->SetDecryptedBuffer(buffer);
|
||||
parameters.decrypt_buffer = buffer->Data();
|
||||
} else {
|
||||
parameters.decrypt_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_1::DecryptAndUpdateCounters(
|
||||
CdmDecryptionParameters& parameters,
|
||||
std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
const size_t bytes,
|
||||
cdm::DecryptedBlock* decrypted_block,
|
||||
size_t& offset,
|
||||
size_t& encrypted_offset,
|
||||
uint32_t& block_ctr) {
|
||||
if (parameters.is_encrypted) {
|
||||
uint32_t counter = encrypted_offset / kIvSize;
|
||||
Ctr128Add(counter - block_ctr, &iv[0]);
|
||||
block_ctr = counter;
|
||||
}
|
||||
|
||||
parameters.encrypt_buffer =
|
||||
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
|
||||
parameters.decrypt_buffer_offset = offset;
|
||||
parameters.encrypt_length = bytes;
|
||||
parameters.block_offset = encrypted_offset % kIvSize;
|
||||
|
||||
offset += bytes;
|
||||
if (parameters.is_encrypted) encrypted_offset += bytes;
|
||||
|
||||
CdmSessionId session_id; // cdm_engine will locate via key_id.
|
||||
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
|
||||
|
||||
switch (status) {
|
||||
case wvcdm::NEED_KEY:
|
||||
return cdm::kNoKey;
|
||||
case wvcdm::NO_ERROR:
|
||||
return cdm::kSuccess;
|
||||
default:
|
||||
return cdm::kDecryptError;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t WvContentDecryptionModule_1::GetCurrentTimeInSeconds() {
|
||||
return host_->GetCurrentWallTimeInSeconds();
|
||||
}
|
||||
|
||||
File::Impl* WvContentDecryptionModule_1::NewFileImpl() {
|
||||
return new File::Impl(host_);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,597 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "wv_content_decryption_module_4.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
// individual error codes
|
||||
kAttachEventListenerError = 0x0001,
|
||||
kLicenseExpiredNotification = 0x0002,
|
||||
kSessionNotFoundError = 0x0003,
|
||||
kSessionAlreadyExistsError = 0x0004,
|
||||
kInvalidParameter = 0x0005,
|
||||
kNotImplemented = 0x0006,
|
||||
|
||||
// error classes to be OR'd with cdm engine result values
|
||||
kOpenSessionErrorBase = 0x0100,
|
||||
kGenerateKeyRequestErrorBase = 0x0200,
|
||||
kGenerateRenewalRequestErrorBase = 0x0300,
|
||||
kUpdateSessionErrorBase = 0x0400,
|
||||
kRestoreKeyErrorBase = 0x0500,
|
||||
kOpenKeySetSessionErrorBase = 0x0600,
|
||||
kGetProvisioningRequestErrorBase = 0x0700,
|
||||
kHandleProvisioningResponseErrorBase = 0x0800,
|
||||
};
|
||||
|
||||
const int kCdmPolicyTimerDurationSeconds = 5;
|
||||
|
||||
// The iso spec only uses the lower 8 bytes of the iv as
|
||||
// the counter.
|
||||
const uint32_t kCencIvSize = 8;
|
||||
const uint32_t kIvSize = 16;
|
||||
|
||||
void Ctr128Add(size_t block_count, uint8_t* counter) {
|
||||
assert(NULL != counter);
|
||||
if (0 == block_count) return;
|
||||
uint8_t carry = 0;
|
||||
uint8_t n = kIvSize - 1;
|
||||
while (n >= kCencIvSize) {
|
||||
uint32_t temp = block_count & 0xff;
|
||||
temp += counter[n];
|
||||
temp += carry;
|
||||
counter[n] = temp & 0xff;
|
||||
carry = (temp & 0x100) ? 1 : 0;
|
||||
block_count = block_count >> 8;
|
||||
n--;
|
||||
if (!block_count && !carry) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// cdm::ContentDecryptionModule_4 implementation.
|
||||
WvContentDecryptionModule_4::WvContentDecryptionModule_4(cdm::Host_4* host)
|
||||
: host_(host), timer_enabled_(false) {
|
||||
HostClock::SetClockInterface(this);
|
||||
}
|
||||
|
||||
WvContentDecryptionModule_4::~WvContentDecryptionModule_4() {
|
||||
DisablePolicyTimer();
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::CreateSession(uint32_t session_id,
|
||||
const char* mime_type,
|
||||
uint32_t mime_type_size,
|
||||
const uint8_t* init_data,
|
||||
uint32_t init_data_size,
|
||||
cdm::SessionType session_type) {
|
||||
if (session_type == cdm::kProvisioning) {
|
||||
// CreateProvisionSession() dispatches its own errors to the host.
|
||||
CreateProvisionSession(session_id);
|
||||
} else {
|
||||
CdmLicenseType license_type;
|
||||
switch (session_type) {
|
||||
case cdm::kTemporary:
|
||||
license_type = kLicenseTypeStreaming;
|
||||
break;
|
||||
case cdm::kPersistent:
|
||||
license_type = kLicenseTypeOffline;
|
||||
break;
|
||||
default:
|
||||
LOGE(
|
||||
"WvContentDecryptionModule_4::CreateSession: Unsupported session "
|
||||
"type ",
|
||||
session_type);
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kInvalidParameter);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CallOpenSession(session_id)) {
|
||||
// CallOpenSession() dispatches its own errors to the host.
|
||||
return;
|
||||
}
|
||||
|
||||
CdmSessionId internal_session_id = session_map_[session_id].webid();
|
||||
CdmKeySetId empty_key_set_id;
|
||||
|
||||
std::string mime_type_string(mime_type, mime_type_size);
|
||||
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
|
||||
init_data_size);
|
||||
InitializationData initialization_data(mime_type_string,
|
||||
init_data_internal);
|
||||
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
CdmKeyMessage key_request;
|
||||
std::string server_url;
|
||||
CdmKeySetId key_set_id;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
internal_session_id, empty_key_set_id, initialization_data,
|
||||
license_type, empty_app_parameters, &key_request, &server_url,
|
||||
&key_set_id);
|
||||
if (KEY_MESSAGE == result) {
|
||||
if (session_type == cdm::kPersistent) {
|
||||
host_->OnSessionCreated(session_id, key_set_id.c_str(),
|
||||
key_set_id.length());
|
||||
} else {
|
||||
host_->OnSessionCreated(session_id, internal_session_id.c_str(),
|
||||
internal_session_id.length());
|
||||
}
|
||||
host_->OnSessionMessage(session_id, key_request.c_str(),
|
||||
key_request.length(), server_url.c_str(),
|
||||
server_url.length());
|
||||
} else {
|
||||
cdm_engine_.CloseSession(internal_session_id);
|
||||
session_map_.erase(session_id);
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kGenerateKeyRequestErrorBase | result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::LoadSession(uint32_t session_id,
|
||||
const char* web_session_id,
|
||||
uint32_t web_session_id_length) {
|
||||
if (!CallOpenSession(session_id)) {
|
||||
// CallOpenSession() dispatches its own errors to the host.
|
||||
return;
|
||||
}
|
||||
CdmSessionId internal_session_id = session_map_[session_id].webid();
|
||||
|
||||
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
|
||||
CdmResponseType result =
|
||||
cdm_engine_.RestoreKey(internal_session_id, key_set_id);
|
||||
if (result != KEY_ADDED) {
|
||||
cdm_engine_.CloseSession(internal_session_id);
|
||||
session_map_.erase(session_id);
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kRestoreKeyErrorBase | result);
|
||||
return;
|
||||
}
|
||||
|
||||
host_->OnSessionCreated(session_id, web_session_id, web_session_id_length);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::UpdateSession(uint32_t session_id,
|
||||
const uint8_t* response,
|
||||
uint32_t response_size) {
|
||||
LOGI("WvContentDecryptionModule_4::UpdateSession()");
|
||||
if (session_map_.count(session_id) == 0) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kSessionNotFoundError);
|
||||
return;
|
||||
}
|
||||
const InternalSession& session = session_map_[session_id];
|
||||
|
||||
if (session.is_provision()) {
|
||||
// UpdateProvisionSession() dispatches its own errors to the host.
|
||||
UpdateProvisionSession(session_id, response, response_size);
|
||||
} else {
|
||||
bool is_release = session.is_release();
|
||||
|
||||
CdmSessionId session_id_internal;
|
||||
CdmKeySetId key_set_id;
|
||||
if (!is_release) {
|
||||
session_id_internal = session.webid();
|
||||
} else {
|
||||
key_set_id = session.webid();
|
||||
}
|
||||
CdmKeyResponse key_data((const char*)response, response_size);
|
||||
|
||||
CdmResponseType status =
|
||||
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
|
||||
|
||||
if (status != KEY_ADDED) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
status | kUpdateSessionErrorBase);
|
||||
return;
|
||||
}
|
||||
if (!is_release) EnablePolicyTimer();
|
||||
host_->OnSessionUpdated(session_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool WvContentDecryptionModule_4::IsKeyValid(const uint8_t* key_id,
|
||||
int key_id_size) {
|
||||
KeyId cdm_key_id(reinterpret_cast<const char*>(key_id), key_id_size);
|
||||
return cdm_engine_.IsKeyLoaded(cdm_key_id);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::ReleaseSession(uint32_t session_id) {
|
||||
if (session_map_.count(session_id) == 0) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kSessionNotFoundError);
|
||||
return;
|
||||
}
|
||||
if (session_map_[session_id].is_release()) {
|
||||
cdm_engine_.CloseKeySetSession(session_map_[session_id].webid());
|
||||
} else if (session_map_[session_id].is_decrypt()) {
|
||||
cdm_engine_.CloseSession(session_map_[session_id].webid());
|
||||
}
|
||||
host_->OnSessionClosed(session_id);
|
||||
session_map_.erase(session_id);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::RemoveSession(
|
||||
uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) {
|
||||
if (session_map_.count(session_id) != 0) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kSessionAlreadyExistsError);
|
||||
return;
|
||||
}
|
||||
|
||||
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
|
||||
CdmResponseType result = cdm_engine_.OpenKeySetSession(key_set_id);
|
||||
if (result != NO_ERROR) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kOpenKeySetSessionErrorBase | result);
|
||||
return;
|
||||
}
|
||||
session_map_[session_id] =
|
||||
InternalSession(key_set_id, InternalSession::kRelease);
|
||||
|
||||
CdmSessionId empty_session_id;
|
||||
InitializationData empty_initialization_data;
|
||||
CdmAppParameterMap empty_app_parameters;
|
||||
CdmKeyMessage key_request;
|
||||
std::string server_url;
|
||||
|
||||
result = cdm_engine_.GenerateKeyRequest(
|
||||
empty_session_id, key_set_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, empty_app_parameters, &key_request, &server_url,
|
||||
NULL);
|
||||
if (KEY_MESSAGE == result) {
|
||||
host_->OnSessionCreated(session_id, key_set_id.c_str(),
|
||||
key_set_id.length());
|
||||
host_->OnSessionMessage(session_id, key_request.c_str(),
|
||||
key_request.length(), server_url.c_str(),
|
||||
server_url.length());
|
||||
} else {
|
||||
cdm_engine_.CloseKeySetSession(key_set_id);
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kGenerateKeyRequestErrorBase | result);
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::UsePrivacyMode() {
|
||||
if (!session_map_.empty()) return cdm::kSessionError;
|
||||
|
||||
property_set_.set_use_privacy_mode(true);
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::SetServerCertificate(
|
||||
const uint8_t* server_certificate_data,
|
||||
uint32_t server_certificate_data_size) {
|
||||
if (!session_map_.empty()) return cdm::kSessionError;
|
||||
|
||||
cdm::Status result = UsePrivacyMode();
|
||||
if (result != cdm::kSuccess) return result;
|
||||
|
||||
std::string server_certificate(
|
||||
reinterpret_cast<const char*>(server_certificate_data),
|
||||
server_certificate_data_size);
|
||||
property_set_.set_service_certificate(server_certificate);
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::TimerExpired(void* context) {
|
||||
LOGI("WvContentDecryptionModule_4::TimerExpired()");
|
||||
if (timer_enabled_) {
|
||||
cdm_engine_.OnTimerEvent();
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::Decrypt(
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
LOGI("WvContentDecryptionModule_4::Decrypt()");
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
std::vector<uint8_t> iv(encrypted_buffer.iv,
|
||||
encrypted_buffer.iv + encrypted_buffer.iv_size);
|
||||
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
|
||||
encrypted_buffer.key_id_size);
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer,
|
||||
decrypted_block);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::DecryptDecodeAndRender(
|
||||
const cdm::InputBuffer& encrypted_buffer, cdm::StreamType stream_type) {
|
||||
LOGI("WvContentDecryptionModule_4::DecryptDecodeAndRender()");
|
||||
|
||||
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
|
||||
return cdm::kDecryptError;
|
||||
|
||||
std::vector<uint8_t> iv(encrypted_buffer.iv,
|
||||
encrypted_buffer.iv + encrypted_buffer.iv_size);
|
||||
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
|
||||
encrypted_buffer.key_id_size);
|
||||
|
||||
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
|
||||
NULL);
|
||||
parameters.is_video = (stream_type == cdm::kStreamTypeVideo);
|
||||
|
||||
if (encrypted_buffer.num_subsamples)
|
||||
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
else
|
||||
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::Destroy() { delete this; }
|
||||
|
||||
File::Impl* WvContentDecryptionModule_4::NewFileImpl() {
|
||||
return new File::Impl(host_);
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::DoSubsampleDecrypt(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
if (!encrypted_buffer.subsamples) return cdm::kDecryptError;
|
||||
|
||||
size_t output_size = 0;
|
||||
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
|
||||
output_size += subsample.cipher_bytes + subsample.clear_bytes;
|
||||
}
|
||||
assert(output_size ==
|
||||
encrypted_buffer.data_size - encrypted_buffer.data_offset);
|
||||
|
||||
SetSizesAndAllocate(output_size, parameters, decrypted_block);
|
||||
|
||||
size_t offset = 0;
|
||||
size_t encrypted_offset = 0;
|
||||
uint32_t block_ctr = 0;
|
||||
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
|
||||
|
||||
for (uint32_t i = 0; i < encrypted_buffer.num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = subsamples[i];
|
||||
|
||||
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
|
||||
size_t bytes =
|
||||
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
|
||||
if (bytes == 0) continue;
|
||||
|
||||
parameters.is_encrypted = is_encrypted;
|
||||
parameters.subsample_flags =
|
||||
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
|
||||
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
|
||||
|
||||
cdm::Status status = DecryptAndUpdateCounters(
|
||||
parameters, iv, encrypted_buffer, bytes, decrypted_block, offset,
|
||||
encrypted_offset, block_ctr);
|
||||
if (status != cdm::kSuccess) return status;
|
||||
}
|
||||
}
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::DoDecrypt(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
assert(!encrypted_buffer.subsamples);
|
||||
size_t output_size =
|
||||
encrypted_buffer.data_size - encrypted_buffer.data_offset;
|
||||
SetSizesAndAllocate(output_size, parameters, decrypted_block);
|
||||
|
||||
size_t offset = 0;
|
||||
size_t encrypted_offset = 0;
|
||||
uint32_t block_ctr = 0;
|
||||
|
||||
parameters.is_encrypted = true;
|
||||
parameters.subsample_flags =
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
|
||||
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer, output_size,
|
||||
decrypted_block, offset, encrypted_offset,
|
||||
block_ctr);
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::SetSizesAndAllocate(
|
||||
size_t output_size, CdmDecryptionParameters& parameters,
|
||||
cdm::DecryptedBlock* decrypted_block) {
|
||||
parameters.decrypt_buffer_length = output_size;
|
||||
|
||||
if (decrypted_block) {
|
||||
cdm::Buffer* buffer = host_->Allocate(output_size);
|
||||
buffer->SetSize(output_size);
|
||||
decrypted_block->SetDecryptedBuffer(buffer);
|
||||
parameters.decrypt_buffer = buffer->Data();
|
||||
} else {
|
||||
parameters.decrypt_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Status WvContentDecryptionModule_4::DecryptAndUpdateCounters(
|
||||
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
|
||||
const cdm::InputBuffer& encrypted_buffer, const size_t bytes,
|
||||
cdm::DecryptedBlock* decrypted_block, size_t& offset,
|
||||
size_t& encrypted_offset, uint32_t& block_ctr) {
|
||||
if (parameters.is_encrypted) {
|
||||
uint32_t counter = encrypted_offset / kIvSize;
|
||||
Ctr128Add(counter - block_ctr, &iv[0]);
|
||||
block_ctr = counter;
|
||||
}
|
||||
|
||||
parameters.encrypt_buffer =
|
||||
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
|
||||
parameters.decrypt_buffer_offset = offset;
|
||||
parameters.encrypt_length = bytes;
|
||||
parameters.block_offset = encrypted_offset % kIvSize;
|
||||
|
||||
offset += bytes;
|
||||
if (parameters.is_encrypted) encrypted_offset += bytes;
|
||||
|
||||
CdmSessionId session_id; // cdm_engine will locate via key_id.
|
||||
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
|
||||
|
||||
switch (status) {
|
||||
case wvcdm::NEED_KEY:
|
||||
return cdm::kNoKey;
|
||||
case wvcdm::NO_ERROR:
|
||||
return cdm::kSuccess;
|
||||
default:
|
||||
return cdm::kDecryptError;
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::EnablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_4::EnablePolicyTimer()");
|
||||
if (!timer_enabled_) {
|
||||
timer_enabled_ = true;
|
||||
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::DisablePolicyTimer() {
|
||||
LOGI("WvContentDecryptionModule_4::DisablePolicyTimer()");
|
||||
timer_enabled_ = false;
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::CreateProvisionSession(uint32_t session_id) {
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
CdmProvisioningRequest request;
|
||||
std::string default_url;
|
||||
CdmResponseType status = cdm_engine_.GetProvisioningRequest(
|
||||
cert_type, cert_authority, &request, &default_url);
|
||||
if (status != NO_ERROR) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kGetProvisioningRequestErrorBase | status);
|
||||
return;
|
||||
}
|
||||
std::string blank_web_id;
|
||||
session_map_[session_id] =
|
||||
InternalSession(blank_web_id, InternalSession::kProvision);
|
||||
host_->OnSessionCreated(session_id, blank_web_id.c_str(),
|
||||
blank_web_id.length());
|
||||
host_->OnSessionMessage(session_id, request.c_str(), request.length(),
|
||||
default_url.c_str(), default_url.length());
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::UpdateProvisionSession(
|
||||
uint32_t session_id, const uint8_t* response, uint32_t response_size) {
|
||||
CdmProvisioningResponse cdm_response(reinterpret_cast<const char*>(response),
|
||||
response_size);
|
||||
std::string cert;
|
||||
std::string wrapped_key;
|
||||
CdmResponseType status =
|
||||
cdm_engine_.HandleProvisioningResponse(cdm_response, &cert, &wrapped_key);
|
||||
if (status != NO_ERROR) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kHandleProvisioningResponseErrorBase | status);
|
||||
return;
|
||||
}
|
||||
host_->OnSessionUpdated(session_id);
|
||||
}
|
||||
|
||||
bool WvContentDecryptionModule_4::CallOpenSession(uint32_t session_id) {
|
||||
CdmSessionId internal_session_id;
|
||||
|
||||
if (session_map_.count(session_id) != 0) {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kSessionAlreadyExistsError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, &internal_session_id);
|
||||
if (result != NO_ERROR) {
|
||||
if (result == NEED_PROVISIONING) {
|
||||
host_->OnSessionError(session_id, cdm::kNeedsDeviceCertificate,
|
||||
kOpenSessionErrorBase | result);
|
||||
} else {
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kOpenSessionErrorBase | result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cdm_engine_.AttachEventListener(internal_session_id, this)) {
|
||||
cdm_engine_.CloseSession(internal_session_id);
|
||||
host_->OnSessionError(session_id, cdm::kSessionError,
|
||||
kAttachEventListenerError);
|
||||
return false;
|
||||
}
|
||||
session_map_[session_id] =
|
||||
InternalSession(internal_session_id, InternalSession::kDecrypt);
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t WvContentDecryptionModule_4::GetCurrentTimeInSeconds() {
|
||||
return host_->GetCurrentWallTimeInSeconds();
|
||||
}
|
||||
|
||||
void WvContentDecryptionModule_4::OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) {
|
||||
bool found = false;
|
||||
std::map<uint32_t, InternalSession>::iterator session_pair_iterator;
|
||||
uint32_t external_session_id = 0;
|
||||
|
||||
for (session_pair_iterator = session_map_.begin();
|
||||
session_pair_iterator != session_map_.end(); ++session_pair_iterator) {
|
||||
if (session_pair_iterator->second.webid() == session_id) {
|
||||
found = true;
|
||||
external_session_id = session_pair_iterator->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOGE("Unmapped Session Event on Session %s", session_id.c_str());
|
||||
host_->OnSessionError(0, cdm::kSessionError, kSessionNotFoundError);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cdm_event) {
|
||||
case LICENSE_RENEWAL_NEEDED_EVENT: {
|
||||
wvcdm::CdmKeyMessage cdm_message;
|
||||
std::string server_url;
|
||||
CdmResponseType result = cdm_engine_.GenerateRenewalRequest(
|
||||
session_id, &cdm_message, &server_url);
|
||||
if (result == wvcdm::KEY_MESSAGE) {
|
||||
LOGI("Renewal created");
|
||||
host_->OnSessionMessage(external_session_id, cdm_message.c_str(),
|
||||
cdm_message.length(), server_url.c_str(),
|
||||
server_url.length());
|
||||
|
||||
} else {
|
||||
LOGD("Error on Generating a Renewal Request!");
|
||||
host_->OnSessionError(external_session_id, cdm::kSessionError,
|
||||
kGenerateRenewalRequestErrorBase | result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LICENSE_EXPIRED_EVENT: {
|
||||
LOGD("License Expired Event");
|
||||
host_->OnSessionError(external_session_id, cdm::kSessionError,
|
||||
kLicenseExpiredNotification);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,709 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This source file provides a basic set of unit tests for the Content
|
||||
// Decryption Module (CDM). It exercises much of the API that will be
|
||||
// required by the host application to get the license and keys for
|
||||
// rendering protected content.
|
||||
|
||||
#include "cdm_test_config.h"
|
||||
#include "test_host_1.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "config_test_env.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "device_cert.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
|
||||
static const int kTestPolicyRenewalDelaySeconds = 180;
|
||||
static const int kDelayWaitToForRenewalMessageSeconds = 2;
|
||||
static const int kHttpOk = 200;
|
||||
static const int kHttpBadGateway = 502;
|
||||
static const int kNumRetries = 5;
|
||||
static const int kRetryBaseDelaySeconds = 3;
|
||||
|
||||
namespace {
|
||||
|
||||
// Default key system identifier.
|
||||
const char kKeySystemWidevine[] = "com.widevine.alpha";
|
||||
|
||||
// Default mime type for key request generation.
|
||||
const char kMimeType[] = "video/mp4";
|
||||
|
||||
// Key ID of key used to encrypt the test content.
|
||||
// This is used to look up the content key.
|
||||
const std::vector<uint8_t> kTestKeyId =
|
||||
wvcdm::a2b_hex("371ea35e1a985d75d198a7f41020dc23");
|
||||
|
||||
// Dummy encrypted data.
|
||||
const std::vector<uint8_t> kInputVector1 = wvcdm::a2b_hex(
|
||||
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
||||
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
||||
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
||||
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
||||
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
||||
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
||||
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
||||
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
|
||||
const std::vector<uint8_t> kIv1 =
|
||||
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
|
||||
|
||||
// Expected output for kInputVector1.
|
||||
const std::vector<uint8_t> kOutputVector1 = wvcdm::a2b_hex(
|
||||
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
||||
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
||||
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
||||
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
||||
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
||||
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
||||
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
||||
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659");
|
||||
|
||||
// Dummy encrypted data. This is a combination of clear and encrypted data.
|
||||
const std::vector<uint8_t> kInputVector2 = wvcdm::a2b_hex(
|
||||
// subsample 0
|
||||
"abcdef"
|
||||
"53cc758763904ea5870458e6b23d36db1e6d7f7aaa2f3eeebb5393a7264991e7"
|
||||
"ce4f57b198326e1a208a821799b2a29c90567ab57321b06e51fc20dc9bc5fc55"
|
||||
"10720a8bb1f5e002c3e50ff70d2d806a9432cad237050d09581f5b0d59b00090"
|
||||
"b3ad69b4087f5a155b17e13c44d33fa007475d207fc4ac2ef3b571ecb9"
|
||||
// subsample 1
|
||||
"0123456789"
|
||||
"f3c852"
|
||||
"ce00dc4806f0c6856ae1732e20308096478e1d822d75c2bb768119565d3bd6e6"
|
||||
"901e36164f4802355ee758fc46ef6cf5f852dd5256c7b1e5f96d29"
|
||||
// subsample 2
|
||||
"deadbeefbaadf00d"
|
||||
"3b20525d5e"
|
||||
"78b8e5aa344d5c4e425e67ddf889ea7c4bb1d49af67eba67718b765e0a940402"
|
||||
"8d306f4ce693ad6dc0a931d507fa14fff4d293d4170280b3e0fca2d628f722e8"
|
||||
);
|
||||
const std::vector<uint8_t> kIv2 =
|
||||
wvcdm::a2b_hex("6ba18dd40f49da7f64c368e4db43fc88");
|
||||
|
||||
// Expected output for kInputVector2.
|
||||
const std::vector<uint8_t> kOutputVector2 = wvcdm::a2b_hex(
|
||||
// subsample 0
|
||||
"abcdef"
|
||||
"52e65334501acadf78e2b26460def3ac973771ed7c64001a2e82917342a7eab3"
|
||||
"047f5e85449692fae8f677be425a47bdea850df5a3ffff17043afb1f2b437ab2"
|
||||
"b1d5e0784c4ed8f97fc24b8f565e85ed63fb7d1365980d9aea7b8b58f488f83c"
|
||||
"1ce80b6096c60f3b113c988ff185b26e798da8fc6f327e4ff00e4b3fbf"
|
||||
// subsample 1
|
||||
"0123456789"
|
||||
"b1ed0a"
|
||||
"a054bce40ccb0ebc70b181d1a12055f46ac55e29c7c2473a29d2a366d240ec48"
|
||||
"7cede274f012813a877f99159e7062b6a37cfc9327a7bc2195814e"
|
||||
// subsample 2
|
||||
"deadbeefbaadf00d"
|
||||
"653b818d1d"
|
||||
"4ab9a9128361d8ca6a9d2766df5c096ee29f4f5204febdf217a94a5b560cd692"
|
||||
"cc36d3e071df789fdeac2fb7ec6dcd7af94bb1f85c22025b25e702e38212b927"
|
||||
);
|
||||
|
||||
// Dummy encrypted data. This will be decrypted with a data_offset
|
||||
// instead of subsamples.
|
||||
const std::vector<uint8_t> kInputVector3 = wvcdm::a2b_hex(
|
||||
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
||||
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
||||
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
||||
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
||||
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
||||
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
||||
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
||||
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
|
||||
const std::vector<uint8_t> kIv3 =
|
||||
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
|
||||
|
||||
// The data_offset for kInputVector3.
|
||||
const uint32_t kInputOffset3 = 9;
|
||||
|
||||
// Expected output for kInputVector3 offset by kInputOffset3.
|
||||
const std::vector<uint8_t> kOutputVector3 = wvcdm::a2b_hex(
|
||||
"19ab304b49908e2395b32f26bf471adf4a4bc92f9e999cca8476d24a257931b4"
|
||||
"c5fd177693ed55e31cd2b85dc196b2b722cd8854eb9334f3dab0b5bd26aa5e66"
|
||||
"a9d1cfbba877c9456b11dc99a6bdc7015ca1544f7ce66171a8179eca19efe515"
|
||||
"8c4c1d0612dff64100387065da108fdbfcc14738202ac3d27520eb48c020ddb7"
|
||||
"714dca22e5e2241aff6932dba1587a97ac1a952827d411d8582dfecc2e9e1494"
|
||||
"644046ca7044bc41c3c5e0a3a405d5551f3f5bdd6f36042e2f0f3693778b9277"
|
||||
"6ed8d106647a7539df7d30288803cd9ca1c274bebe688151c72b451f571a441f"
|
||||
"83d0ff77d8d57dcb395122e175f4944569917627d6c3dc");
|
||||
|
||||
void* GetCdmHost(int host_interface_version, void* user_data) {
|
||||
if (host_interface_version != cdm::Host_1::kVersion) return NULL;
|
||||
return user_data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmApi1Test : public testing::Test {
|
||||
public:
|
||||
CdmApi1Test() : cdm_(NULL) {}
|
||||
~CdmApi1Test() {}
|
||||
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
// Create the Host.
|
||||
host_.reset(new TestHost_1());
|
||||
|
||||
// Set various parameters that the CDM will query.
|
||||
host_->SetPlatformString("SecurityLevel", "L1");
|
||||
host_->SetPlatformString("PrivacyOn", "False");
|
||||
std::string cert(reinterpret_cast<const char*>(kDeviceCert),
|
||||
kDeviceCertSize);
|
||||
host_->SetPlatformString("DeviceCertificate", cert);
|
||||
|
||||
// Initialize the CDM module before creating a CDM instance.
|
||||
InitializeCdmModule();
|
||||
|
||||
// Create the CDM.
|
||||
cdm_ =
|
||||
reinterpret_cast<cdm::ContentDecryptionModule_1*>(::CreateCdmInstance(
|
||||
cdm::ContentDecryptionModule_1::kVersion, kKeySystemWidevine,
|
||||
strlen(kKeySystemWidevine), GetCdmHost, host_.get()));
|
||||
|
||||
// Tell the Host about the CDM.
|
||||
host_->SetCdmPtr(cdm_);
|
||||
}
|
||||
|
||||
cdm::Status GenerateKeyRequest(const std::string& init_data) {
|
||||
cdm::Status status = cdm_->GenerateKeyRequest(
|
||||
kMimeType, strlen(kMimeType),
|
||||
(const uint8_t*)init_data.data(), init_data.length());
|
||||
return status;
|
||||
}
|
||||
|
||||
cdm::Status GenerateKeyRequestWithMimeType(const std::string& mime_type) {
|
||||
cdm::Status status = cdm_->GenerateKeyRequest(
|
||||
mime_type.c_str(), mime_type.length(),
|
||||
(const uint8_t*)g_key_id.data(), g_key_id.length());
|
||||
return status;
|
||||
}
|
||||
|
||||
// posts a request and extracts the drm message from the response
|
||||
std::string GetKeyRequestResponse(const TestHost_1::KeyMessage& key_msg) {
|
||||
std::string url;
|
||||
if (key_msg.default_url.empty()) {
|
||||
url = g_license_server + g_client_auth;
|
||||
} else {
|
||||
// Note that the client auth string is not appended when the CDM tells
|
||||
// us what URL to use.
|
||||
url = key_msg.default_url;
|
||||
}
|
||||
|
||||
int status_code;
|
||||
std::string response;
|
||||
UrlRequest url_request(url);
|
||||
|
||||
for (int retries = 0; retries < kNumRetries; ++retries) {
|
||||
EXPECT_TRUE(url_request.is_connected());
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
url_request.PostRequest(key_msg.message);
|
||||
int resp_bytes = url_request.GetResponse(&response);
|
||||
|
||||
status_code = url_request.GetStatusCode(response);
|
||||
// Sometimes, the server returns "HTTP 502 bad gateway".
|
||||
// If we treat this as a non-fatal error, we reduce test flakiness.
|
||||
if (status_code != kHttpBadGateway) {
|
||||
// Move on with normal processing.
|
||||
break;
|
||||
}
|
||||
|
||||
// Reconnect to the server and try again. Since the server's 502
|
||||
// response could indicate a temporary failure due to load, we use
|
||||
// an exponential backoff. Each time we reconnect, we delay by
|
||||
// exactly twice as long as the last time. This is a simplified
|
||||
// version of the delay strategy recommended by Google in the
|
||||
// internal document "Rate Limiting in Google Applications" under
|
||||
// the heading "Settings for client exponential backoff". We do
|
||||
// not bother to fuzz the delay, since unit tests are not running
|
||||
// simultaneously in large numbers like real clients would be.
|
||||
LOGE("Bad gateway, retrying.");
|
||||
sleep(kRetryBaseDelaySeconds << retries);
|
||||
url_request.Reconnect();
|
||||
}
|
||||
|
||||
// Some license servers return 400 for invalid message, some
|
||||
// return 500; treat anything other than 200 as an invalid message.
|
||||
EXPECT_EQ(kHttpOk, status_code);
|
||||
|
||||
if (status_code != kHttpOk) {
|
||||
return "";
|
||||
} else {
|
||||
std::string drm_msg;
|
||||
LicenseRequest lic_request;
|
||||
lic_request.GetDrmMessage(response, drm_msg);
|
||||
LOGV("drm msg: %u bytes\n%s", drm_msg.size(),
|
||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||
drm_msg.size()).c_str());
|
||||
return drm_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessKeyResponse() {
|
||||
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
|
||||
ASSERT_FALSE(key_msg.message.empty());
|
||||
EXPECT_TRUE(key_msg.default_url.empty());
|
||||
std::string drm_msg = GetKeyRequestResponse(key_msg);
|
||||
EXPECT_EQ(cdm::kSuccess, AddKey(key_msg.session_id, drm_msg));
|
||||
}
|
||||
|
||||
void ProcessKeyRenewalResponse() {
|
||||
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
|
||||
ASSERT_FALSE(key_msg.message.empty());
|
||||
EXPECT_FALSE(key_msg.default_url.empty());
|
||||
std::string drm_msg = GetKeyRequestResponse(key_msg);
|
||||
EXPECT_EQ(cdm::kSuccess, AddKey(key_msg.session_id, drm_msg));
|
||||
}
|
||||
|
||||
void CloseSession(const std::string& session_id) {
|
||||
cdm::Status status =
|
||||
cdm_->CloseSession(session_id.data(), session_id.length());
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
cdm::Status AddKey(const std::string& session_id,
|
||||
const std::string& drm_msg) {
|
||||
cdm::Status status =
|
||||
cdm_->AddKey(session_id.data(), session_id.size(),
|
||||
(const uint8_t*)drm_msg.data(), drm_msg.size(), NULL, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
|
||||
const std::vector<uint8_t>& iv) {
|
||||
cdm::InputBuffer buf;
|
||||
buf.data = &encrypted[0];
|
||||
buf.data_size = encrypted.size();
|
||||
buf.key_id = &kTestKeyId[0];
|
||||
buf.key_id_size = kTestKeyId.size();
|
||||
buf.iv = &iv[0];
|
||||
buf.iv_size = iv.size();
|
||||
buf.data_offset = 0;
|
||||
buf.timestamp = 10;
|
||||
return buf;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(
|
||||
const std::vector<uint8_t>& encrypted,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<cdm::SubsampleEntry>& sub) {
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.subsamples = &sub[0];
|
||||
buf.num_subsamples = sub.size();
|
||||
return buf;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(
|
||||
const std::vector<uint8_t>& encrypted,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const uint32_t offset) {
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.data_offset = offset;
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::vector<cdm::SubsampleEntry> BuildMultipleSubsamples() {
|
||||
std::vector<cdm::SubsampleEntry> sub;
|
||||
sub.push_back(cdm::SubsampleEntry(3, 125));
|
||||
sub.push_back(cdm::SubsampleEntry(5, 62));
|
||||
sub.push_back(cdm::SubsampleEntry(8, 69));
|
||||
return sub;
|
||||
}
|
||||
|
||||
std::vector<cdm::SubsampleEntry> BuildSingleSubsample(size_t size) {
|
||||
std::vector<cdm::SubsampleEntry> sub;
|
||||
sub.push_back(cdm::SubsampleEntry(0, size));
|
||||
return sub;
|
||||
}
|
||||
|
||||
cdm::ContentDecryptionModule_1* cdm_; // owned by host_
|
||||
wvcdm::scoped_ptr<TestHost_1> host_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class DummyCDM : public cdm::ContentDecryptionModule_1 {
|
||||
public:
|
||||
DummyCDM() : timer_fired_(false), last_context_(NULL) {}
|
||||
|
||||
virtual cdm::Status GenerateKeyRequest(const char*, int, const uint8_t*,
|
||||
int) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual cdm::Status AddKey(const char*, int, const uint8_t*, int,
|
||||
const uint8_t*, int) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual bool IsKeyValid(const uint8_t*, int) OVERRIDE { return false; }
|
||||
|
||||
virtual cdm::Status CloseSession(const char*, int) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual void TimerExpired(void* context) OVERRIDE {
|
||||
timer_fired_ = true;
|
||||
last_context_ = context;
|
||||
}
|
||||
|
||||
virtual cdm::Status Decrypt(const cdm::InputBuffer&,
|
||||
cdm::DecryptedBlock*) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRenderFrame(
|
||||
const cdm::InputBuffer&) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRenderSamples(
|
||||
const cdm::InputBuffer&) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual void Destroy() OVERRIDE { delete this; }
|
||||
|
||||
virtual cdm::Status GetProvisioningRequest(std::string*,
|
||||
std::string*) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual cdm::Status HandleProvisioningResponse(std::string&) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
bool TimerFired() const { return timer_fired_; }
|
||||
|
||||
void* LastTimerContext() const { return last_context_; }
|
||||
|
||||
void ResetTimerStatus() {
|
||||
timer_fired_ = false;
|
||||
last_context_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
bool timer_fired_;
|
||||
void* last_context_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(CdmApi1Test, TestHostTimer) {
|
||||
// Validate that the TestHost timers are processed in the correct order.
|
||||
// To do this, we replace the cdm with a dummy that only tracks timers.
|
||||
DummyCDM* cdm = new DummyCDM();
|
||||
|
||||
// The old CDM is destroyed by SetCdmPtr.
|
||||
cdm_ = cdm;
|
||||
host_->SetCdmPtr(cdm);
|
||||
|
||||
const double kTimerDelaySeconds = 1.0;
|
||||
const int64_t kTimerDelayMs = kTimerDelaySeconds * 1000;
|
||||
void* kCtx1 = reinterpret_cast<void*>(0x1);
|
||||
void* kCtx2 = reinterpret_cast<void*>(0x2);
|
||||
|
||||
host_->SetTimer(kTimerDelayMs * 1, kCtx1);
|
||||
host_->SetTimer(kTimerDelayMs * 2, kCtx2);
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_TRUE(cdm->TimerFired());
|
||||
EXPECT_EQ(kCtx1, cdm->LastTimerContext());
|
||||
cdm->ResetTimerStatus();
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_TRUE(cdm->TimerFired());
|
||||
EXPECT_EQ(kCtx2, cdm->LastTimerContext());
|
||||
cdm->ResetTimerStatus();
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_FALSE(cdm->TimerFired());
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, DeviceCertificateTest) {
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
// Clear any existing device cert.
|
||||
host_->SetPlatformString("DeviceCertificate", "");
|
||||
|
||||
ASSERT_EQ(cdm::kNeedsDeviceCertificate, GenerateKeyRequest(g_key_id));
|
||||
|
||||
// The Host must handle the certificate provisioning request.
|
||||
std::string server_url;
|
||||
std::string request;
|
||||
cdm::Status status = cdm_->GetProvisioningRequest(&request, &server_url);
|
||||
ASSERT_EQ(cdm::kSuccess, status);
|
||||
|
||||
UrlRequest url_request(server_url);
|
||||
url_request.PostCertRequestInQueryString(request);
|
||||
|
||||
std::string message;
|
||||
bool ok = url_request.GetResponse(&message);
|
||||
ASSERT_TRUE(ok);
|
||||
|
||||
status = cdm_->HandleProvisioningResponse(message);
|
||||
ASSERT_EQ(cdm::kSuccess, status);
|
||||
|
||||
// Now we are provisioned, so GKR should succeed.
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
} else {
|
||||
LOGI(
|
||||
"Skipping CdmApi1Test::DeviceCertificateTest because this platform "
|
||||
"does not support device certificates.");
|
||||
}
|
||||
}
|
||||
|
||||
// Note that these tests, BaseMessageTest, NormalDecryption and RenewalTest,
|
||||
// are dependent on getting back a license from the license server where the
|
||||
// url for the license server is defined in the conf_test_env.cpp. If these
|
||||
// tests fail immediately, verify that the license server URL is correct
|
||||
// and works in your test environment.
|
||||
|
||||
TEST_F(CdmApi1Test, BaseMessageTest) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, NormalDecryption) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<uint8_t> expected = kOutputVector1;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, NormalSubSampleDecryptionWithSubsampleInfo) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<uint8_t> expected = kOutputVector2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, NormalSubSampleDecryptionWithMissingSubsampleInfo) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<uint8_t> expected = kOutputVector2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
|
||||
// Don't add these subsamples yet!
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.num_subsamples = sub.size();
|
||||
|
||||
TestDecryptedBlock output;
|
||||
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
|
||||
// Add the subsamples pointer and expect success.
|
||||
buf.subsamples = &sub[0];
|
||||
status = cdm_->Decrypt(buf, &output);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, DecryptWithDataOffset) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector3;
|
||||
std::vector<uint8_t> iv = kIv3;
|
||||
std::vector<uint8_t> expected = kOutputVector3;
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, kInputOffset3);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
cdm::Buffer *output_buf = output.DecryptedBuffer();
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(buf.data_size - buf.data_offset, output_buf->Size());
|
||||
EXPECT_EQ(0, memcmp(output.DecryptedBuffer()->Data(), &expected[0],
|
||||
output_buf->Size()));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, DecryptReturnsSizedBuffer) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<uint8_t> expected = kOutputVector1;
|
||||
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
cdm::Buffer* buffer = output.DecryptedBuffer();
|
||||
EXPECT_NE((void*)NULL, buffer);
|
||||
if (buffer) {
|
||||
EXPECT_EQ(expected.size(), output.DecryptedBuffer()->Size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, RenewalTest) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
// We expect that by the time we've added a key, the CDM has set a timer.
|
||||
// Otherwise, it couldn't correctly handle renewal.
|
||||
EXPECT_NE(0, host_->NumTimers());
|
||||
host_->FastForwardTime(kTestPolicyRenewalDelaySeconds +
|
||||
kDelayWaitToForRenewalMessageSeconds);
|
||||
|
||||
// When the timer expired, we should have sent a renewal, so we can
|
||||
// add this renewed key now, assuming things are working as expected.
|
||||
ProcessKeyRenewalResponse();
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, SecureDecryptionLevel1) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
// Level 1 passes encrypted payload straight through. By calling the
|
||||
// CDM's DecryptDecodeAndRenderSamples, and/or DecryptDecodeAndRenderFrame,
|
||||
// OEMCrypto_DecryptCTR will be told to use Direct Rendering.
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRenderSamples(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRenderFrame(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, SecureDecryptionLevel1WithSubsampleInfo) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
// Level 1 passes encrypted payload straight through. By calling the
|
||||
// CDM's DecryptDecodeAndRenderSamples, and/or DecryptDecodeAndRenderFrame,
|
||||
// OEMCrypto_DecryptCTR will be told to use Direct Rendering.
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRenderSamples(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRenderFrame(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, SecureDecryptionLevel1WithMissingSubsampleInfo) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
ProcessKeyResponse();
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
|
||||
// Don't add these subsamples yet!
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.num_subsamples = sub.size();
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRenderSamples(buf);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
status = cdm_->DecryptDecodeAndRenderFrame(buf);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
|
||||
// Add the subsamples pointer and expect success.
|
||||
buf.subsamples = &sub[0];
|
||||
status = cdm_->DecryptDecodeAndRenderSamples(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRenderFrame(buf);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, GenerateKeyRequestFailureSendsKeyError) {
|
||||
// Pass a bogus key id and expect failure.
|
||||
EXPECT_EQ(cdm::kSessionError, GenerateKeyRequest(""));
|
||||
// Expect the CDM to pass a key error back to the host.
|
||||
EXPECT_EQ(1, host_->KeyErrorsSize());
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, AddKeyFailureSendsKeyError) {
|
||||
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
|
||||
|
||||
// Get the message and response.
|
||||
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
|
||||
EXPECT_TRUE(key_msg.default_url.empty());
|
||||
std::string drm_msg = GetKeyRequestResponse(key_msg);
|
||||
|
||||
// Call AddKey with a bad session id and expect failure.
|
||||
EXPECT_EQ(cdm::kSessionError, AddKey("BLAH", drm_msg));
|
||||
|
||||
// Expect the CDM to pass a key error back to the host.
|
||||
EXPECT_EQ(1, host_->KeyErrorsSize());
|
||||
|
||||
// Call AddKey with a bad license and expect failure.
|
||||
EXPECT_EQ(cdm::kSessionError, AddKey(key_msg.session_id, "BLAH"));
|
||||
|
||||
// Expect the CDM to pass one more key error back to the host.
|
||||
EXPECT_EQ(2, host_->KeyErrorsSize());
|
||||
}
|
||||
|
||||
TEST_F(CdmApi1Test, MimeTypeMatters) {
|
||||
cdm::Status status;
|
||||
|
||||
status = GenerateKeyRequestWithMimeType("video/mp4");
|
||||
ASSERT_EQ(cdm::kSuccess, status);
|
||||
|
||||
status = GenerateKeyRequestWithMimeType("video/webm");
|
||||
ASSERT_EQ(cdm::kSuccess, status);
|
||||
|
||||
status = GenerateKeyRequestWithMimeType("video/blah");
|
||||
ASSERT_EQ(cdm::kSessionError, status);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,925 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This source file provides a basic set of unit tests for the Content
|
||||
// Decryption Module (CDM). It exercises much of the API that will be
|
||||
// required by the host application to get the license and keys for
|
||||
// rendering protected content.
|
||||
|
||||
#include "cdm_test_config.h"
|
||||
#include "test_host_4.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cdm_client_property_set.h"
|
||||
#include "clock.h"
|
||||
#include "config_test_env.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "device_cert.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
#include "wv_content_decryption_module_4.h"
|
||||
|
||||
static const int kTestPolicyRenewalDelaySeconds = 180;
|
||||
static const int kDelayWaitToForRenewalMessageSeconds = 2;
|
||||
static const int kHttpOk = 200;
|
||||
static const int kSessionId1 = 1;
|
||||
static const int kSessionId2 = 2;
|
||||
static const int kSessionId3 = 3;
|
||||
|
||||
namespace {
|
||||
|
||||
// Default key system identifier.
|
||||
const char kKeySystemWidevine[] = "com.widevine.alpha";
|
||||
|
||||
// Default mime type for session creation.
|
||||
const char kMimeType[] = "video/mp4";
|
||||
|
||||
// Known filename for certificate manipulation.
|
||||
const std::string kCertFilename = "cert.bin";
|
||||
|
||||
// Key ID of key used to encrypt the test content.
|
||||
// This is used to look up the content key.
|
||||
const std::vector<uint8_t> kTestKeyId =
|
||||
wvcdm::a2b_hex("371ea35e1a985d75d198a7f41020dc23");
|
||||
|
||||
// Dummy encrypted data.
|
||||
const std::vector<uint8_t> kInputVector1 = wvcdm::a2b_hex(
|
||||
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
||||
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
||||
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
||||
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
||||
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
||||
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
||||
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
||||
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
|
||||
const std::vector<uint8_t> kIv1 =
|
||||
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
|
||||
|
||||
// Expected output for kInputVector1.
|
||||
const std::vector<uint8_t> kOutputVector1 = wvcdm::a2b_hex(
|
||||
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
|
||||
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
|
||||
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
|
||||
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
|
||||
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
|
||||
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
|
||||
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
|
||||
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659");
|
||||
|
||||
// Dummy encrypted data. This is a combination of clear and encrypted data.
|
||||
const std::vector<uint8_t> kInputVector2 = wvcdm::a2b_hex(
|
||||
// subsample 0
|
||||
"abcdef"
|
||||
"53cc758763904ea5870458e6b23d36db1e6d7f7aaa2f3eeebb5393a7264991e7"
|
||||
"ce4f57b198326e1a208a821799b2a29c90567ab57321b06e51fc20dc9bc5fc55"
|
||||
"10720a8bb1f5e002c3e50ff70d2d806a9432cad237050d09581f5b0d59b00090"
|
||||
"b3ad69b4087f5a155b17e13c44d33fa007475d207fc4ac2ef3b571ecb9"
|
||||
// subsample 1
|
||||
"0123456789"
|
||||
"f3c852"
|
||||
"ce00dc4806f0c6856ae1732e20308096478e1d822d75c2bb768119565d3bd6e6"
|
||||
"901e36164f4802355ee758fc46ef6cf5f852dd5256c7b1e5f96d29"
|
||||
// subsample 2
|
||||
"deadbeefbaadf00d"
|
||||
"3b20525d5e"
|
||||
"78b8e5aa344d5c4e425e67ddf889ea7c4bb1d49af67eba67718b765e0a940402"
|
||||
"8d306f4ce693ad6dc0a931d507fa14fff4d293d4170280b3e0fca2d628f722e8"
|
||||
);
|
||||
const std::vector<uint8_t> kIv2 =
|
||||
wvcdm::a2b_hex("6ba18dd40f49da7f64c368e4db43fc88");
|
||||
|
||||
// Expected output for kInputVector2.
|
||||
const std::vector<uint8_t> kOutputVector2 = wvcdm::a2b_hex(
|
||||
// subsample 0
|
||||
"abcdef"
|
||||
"52e65334501acadf78e2b26460def3ac973771ed7c64001a2e82917342a7eab3"
|
||||
"047f5e85449692fae8f677be425a47bdea850df5a3ffff17043afb1f2b437ab2"
|
||||
"b1d5e0784c4ed8f97fc24b8f565e85ed63fb7d1365980d9aea7b8b58f488f83c"
|
||||
"1ce80b6096c60f3b113c988ff185b26e798da8fc6f327e4ff00e4b3fbf"
|
||||
// subsample 1
|
||||
"0123456789"
|
||||
"b1ed0a"
|
||||
"a054bce40ccb0ebc70b181d1a12055f46ac55e29c7c2473a29d2a366d240ec48"
|
||||
"7cede274f012813a877f99159e7062b6a37cfc9327a7bc2195814e"
|
||||
// subsample 2
|
||||
"deadbeefbaadf00d"
|
||||
"653b818d1d"
|
||||
"4ab9a9128361d8ca6a9d2766df5c096ee29f4f5204febdf217a94a5b560cd692"
|
||||
"cc36d3e071df789fdeac2fb7ec6dcd7af94bb1f85c22025b25e702e38212b927"
|
||||
);
|
||||
|
||||
// Dummy encrypted data. This will be decrypted with a data_offset
|
||||
// instead of subsamples.
|
||||
const std::vector<uint8_t> kInputVector3 = wvcdm::a2b_hex(
|
||||
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
|
||||
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
|
||||
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
|
||||
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
|
||||
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
|
||||
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
|
||||
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
|
||||
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
|
||||
const std::vector<uint8_t> kIv3 =
|
||||
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
|
||||
|
||||
// The data_offset for kInputVector3.
|
||||
const uint32_t kInputOffset3 = 9;
|
||||
|
||||
// Expected output for kInputVector3 offset by kInputOffset3.
|
||||
const std::vector<uint8_t> kOutputVector3 = wvcdm::a2b_hex(
|
||||
"19ab304b49908e2395b32f26bf471adf4a4bc92f9e999cca8476d24a257931b4"
|
||||
"c5fd177693ed55e31cd2b85dc196b2b722cd8854eb9334f3dab0b5bd26aa5e66"
|
||||
"a9d1cfbba877c9456b11dc99a6bdc7015ca1544f7ce66171a8179eca19efe515"
|
||||
"8c4c1d0612dff64100387065da108fdbfcc14738202ac3d27520eb48c020ddb7"
|
||||
"714dca22e5e2241aff6932dba1587a97ac1a952827d411d8582dfecc2e9e1494"
|
||||
"644046ca7044bc41c3c5e0a3a405d5551f3f5bdd6f36042e2f0f3693778b9277"
|
||||
"6ed8d106647a7539df7d30288803cd9ca1c274bebe688151c72b451f571a441f"
|
||||
"83d0ff77d8d57dcb395122e175f4944569917627d6c3dc");
|
||||
|
||||
// Dummy data used as a server certificate. Not a real cert.
|
||||
const std::vector<uint8_t> kFakeServerCertficiate = wvcdm::a2b_hex(
|
||||
"65393939a0e4a28fb07aa8237bdc9e2fd30fdf763061ed9ed91b368a2b78fc2d"
|
||||
"15f0b6add5d56d05f7e5852aeae67de6127d6b61b39bb5d6e9657f352ba75e72"
|
||||
"c436f334878568504f697ad01aa329efbadebc3b9aaf502ada9b8e6fcf066252"
|
||||
"76690a0f50cd3852dd39f7c5444402f86831d8bb2cbbd7cba11ab1caa35445fe"
|
||||
"35a332529845f2f4b5d5a0b1e2c0855fc2d644443eb967e1f9030fd0d9e6375b"
|
||||
"a5100a997ff8a958606d59a00151d251eaf69f9e00465b5aa4c23ef33a11b05b"
|
||||
"212c1ff830fcd095c681ef25a18db08d7bddfeee16763e9fec06daa8275de2b0"
|
||||
"554a0bec821fb7bb6fbda081d8cecce6c51195e6b6a1c0fcbb2470bcff2a962e"
|
||||
"57e767f83cc8b6c31d41d7c59526128f19cbf625fa4f5f382393a3bd2b76463a"
|
||||
"fe97e4a6f30f631c83308aa5fdccc2d1765c113d474bf2496e03c030c18e5ca8"
|
||||
"84cb98fa120804baa245682966926ccbf555450437de10e549afc088f8c36f63"
|
||||
"8a943178bdd58e4ef1f7d501e2296bbe2df57ce8816d6ae9e7ab18b1e01dac9b"
|
||||
"8c312298356cad58c6ed46b1cd3a895e496c66f5229da39e260a7c1f782653bd"
|
||||
"ade5f6132fc4771bb8caca80eb063abb47144abc44aaba8d23fcc721199291b3"
|
||||
"0b95bc7d310c3f90756916552151a6008feba73ddab87454822914732d6ee78a"
|
||||
"3587f33c698b0ab90f01b38a71abd01660a5ef1473e4556aa8c17d34679065c5"
|
||||
"689dd21026601e94146254346f4ccd979bc378046473b3de64b8654e18406e94"
|
||||
"b673dc13fdcc70213365bd098f212ed7a973ef35da18e16b1c118f8d4eda0b39"
|
||||
"ffb8084ca93a44923df8475e3feec6c0941a2a37d5df514e686dd182dc9ebbff"
|
||||
"41d8d80aa39d059de3b7849d4e5946b09937c6e7a6f6392ea5e9370ba1867553"
|
||||
"b716e31433c2e3ca3eff1e7c08a31b201fd18c363b8daeed59df7afb68ad5166"
|
||||
"659914a2e137c9d2e5940aaa921694c8d84d5ea1c83486e473e3021cb825c0ed"
|
||||
"f778c621598d35e44843cd53e32f90925e74bc8eb469889fb221a2c592b43d94"
|
||||
"2079d6393ab76e47d30dbf837fa5ca07d7186710973c5bb17c04b3207a85a166"
|
||||
"153053f6b0ec35c6b124efd43be274cdf8300234806a72231272d13b331cafd3"
|
||||
"dab01002b5e1961a5f3221d4c589fac3e42c1d1de9f244090e31a08999d3434d"
|
||||
"15a5159f09fa3307e0d9466ac40af63c0a62f8d2719e1df80bb24d4d7ab256f7"
|
||||
"0906ee342a47a9bd6805d796cf928f0d5251701ba8e6888675b1b6fd03e77df0"
|
||||
"8150495c778cb7942d8c060ace4b080ad22e44854988d5e2232f5dbcf2db559f"
|
||||
"24bae8667c33cea77f1c6a58d9dd010363c233cff6d5d26f5f77230ee681456c"
|
||||
"35a8f90d37c2fc0ab07a2a795431f829cca574fd37d8822e04fc500ba468f08a"
|
||||
"556e53ba8992dd1ccbd44559a7b93bebc27cec81a834755cf110bce183481e42");
|
||||
|
||||
void* GetCdmHost(int host_interface_version, void* user_data) {
|
||||
if (host_interface_version != cdm::Host_4::kVersion) return NULL;
|
||||
return user_data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmApi4Test : public testing::Test {
|
||||
public:
|
||||
CdmApi4Test() : cdm_(NULL) {}
|
||||
~CdmApi4Test() {}
|
||||
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
// Create the Host.
|
||||
host_.reset(new TestHost_4());
|
||||
|
||||
// Load the device cert that was already "saved" to the device.
|
||||
std::string cert(reinterpret_cast<const char*>(kDeviceCert),
|
||||
kDeviceCertSize);
|
||||
host_->file_store[kCertFilename] = cert;
|
||||
|
||||
// Initialize the CDM module before creating a CDM instance.
|
||||
InitializeCdmModule();
|
||||
|
||||
// Create the CDM.
|
||||
cdm_ =
|
||||
reinterpret_cast<cdm::ContentDecryptionModule_4*>(::CreateCdmInstance(
|
||||
cdm::ContentDecryptionModule_4::kVersion, kKeySystemWidevine,
|
||||
strlen(kKeySystemWidevine), GetCdmHost, host_.get()));
|
||||
|
||||
if (cdm_ == NULL) {
|
||||
FAIL() << "Fatal CDM creation error!";
|
||||
}
|
||||
|
||||
// Tell the Host about the CDM.
|
||||
host_->SetCdmPtr(cdm_);
|
||||
}
|
||||
|
||||
void CreateSession(const uint32_t session_id, const std::string& init_data,
|
||||
cdm::SessionType session_type) {
|
||||
cdm_->CreateSession(session_id, kMimeType, strlen(kMimeType),
|
||||
reinterpret_cast<const uint8_t*>(init_data.data()),
|
||||
init_data.length(), session_type);
|
||||
}
|
||||
|
||||
void CreateSessionWithMimeType(const uint32_t session_id,
|
||||
const std::string& mime_type) {
|
||||
cdm_->CreateSession(session_id, mime_type.c_str(), mime_type.length(),
|
||||
reinterpret_cast<const uint8_t*>(g_key_id.data()),
|
||||
g_key_id.length(), cdm::kTemporary);
|
||||
}
|
||||
|
||||
void LoadSession(uint32_t session_id, const std::string& web_session_id) {
|
||||
cdm_->LoadSession(session_id, web_session_id.data(),
|
||||
web_session_id.length());
|
||||
}
|
||||
|
||||
// posts a request and extracts the drm message from the response
|
||||
std::string GetKeyRequestResponse(
|
||||
const TestHost_4::SessionMessage& session_msg,
|
||||
const std::string& default_url, bool is_provision) {
|
||||
std::string url;
|
||||
if (session_msg.default_url.empty()) {
|
||||
url = default_url;
|
||||
} else {
|
||||
// Note that the client auth string is assumed to already be appended when
|
||||
// the CDM tells us what URL to use.
|
||||
url = session_msg.default_url;
|
||||
}
|
||||
|
||||
UrlRequest url_request(url);
|
||||
EXPECT_TRUE(url_request.is_connected());
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!is_provision) {
|
||||
url_request.PostRequest(session_msg.message);
|
||||
} else {
|
||||
url_request.PostCertRequestInQueryString(session_msg.message);
|
||||
}
|
||||
std::string response;
|
||||
int resp_bytes = url_request.GetResponse(&response);
|
||||
|
||||
// Some license servers return 400 for invalid message, some
|
||||
// return 500; treat anything other than 200 as an invalid message.
|
||||
int status_code = url_request.GetStatusCode(response);
|
||||
EXPECT_EQ(kHttpOk, status_code);
|
||||
|
||||
if (status_code != kHttpOk) {
|
||||
return "";
|
||||
} else {
|
||||
std::string drm_msg;
|
||||
LicenseRequest lic_request;
|
||||
lic_request.GetDrmMessage(response, drm_msg);
|
||||
LOGV("drm msg: %u bytes\n%s", drm_msg.size(),
|
||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||
drm_msg.size()).c_str());
|
||||
return drm_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessKeyResponse(bool expect_url_in_message) {
|
||||
ProcessKeyResponse(expect_url_in_message, g_license_server + g_client_auth);
|
||||
}
|
||||
|
||||
void ProcessKeyResponse(bool expect_url_in_message,
|
||||
const std::string& default_url) {
|
||||
ProcessServerResponse(expect_url_in_message, false, default_url);
|
||||
}
|
||||
|
||||
void ProcessProvisionResponse() { ProcessServerResponse(true, true, ""); }
|
||||
|
||||
void ProcessServerResponse(bool expect_url_in_message, bool is_provision,
|
||||
const std::string& default_url) {
|
||||
TestHost_4::SessionMessage session_msg = host_->GetLastSessionMessage();
|
||||
ASSERT_FALSE(session_msg.message.empty());
|
||||
// Use EXPECT_bool instead of EXPECT_EQ to get better error printing.
|
||||
if (expect_url_in_message) {
|
||||
EXPECT_FALSE(session_msg.default_url.empty());
|
||||
} else {
|
||||
EXPECT_TRUE(session_msg.default_url.empty());
|
||||
}
|
||||
std::string drm_msg =
|
||||
GetKeyRequestResponse(session_msg, default_url, is_provision);
|
||||
UpdateSession(session_msg.session_id, (const uint8_t*)drm_msg.c_str(),
|
||||
drm_msg.length());
|
||||
}
|
||||
|
||||
void ReleaseSession(uint32_t session_id) { cdm_->ReleaseSession(session_id); }
|
||||
|
||||
void RemoveSession(uint32_t session_id, const std::string& web_session_id) {
|
||||
cdm_->RemoveSession(session_id, web_session_id.data(),
|
||||
web_session_id.length());
|
||||
}
|
||||
|
||||
void UpdateSession(uint32_t session_id, const uint8_t* response,
|
||||
uint32_t response_size) {
|
||||
cdm_->UpdateSession(session_id, response, response_size);
|
||||
}
|
||||
|
||||
cdm::Status UsePrivacyMode() { return cdm_->UsePrivacyMode(); }
|
||||
|
||||
cdm::Status SetServerCertificate(const std::vector<uint8_t>& cert) {
|
||||
return cdm_->SetServerCertificate(&cert[0], cert.size());
|
||||
}
|
||||
|
||||
bool SessionErrorPresent(uint32_t session_id) {
|
||||
TestHost_4::SessionError serr = host_->GetLastSessionError();
|
||||
if (serr.session_id == session_id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SessionErrorPresent(uint32_t session_id, cdm::Status error_code) {
|
||||
TestHost_4::SessionError serr = host_->GetLastSessionError();
|
||||
if (serr.session_id == session_id && serr.error_code == error_code) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
|
||||
const std::vector<uint8_t>& iv) {
|
||||
cdm::InputBuffer buf;
|
||||
buf.data = &encrypted[0];
|
||||
buf.data_size = encrypted.size();
|
||||
buf.key_id = &kTestKeyId[0];
|
||||
buf.key_id_size = kTestKeyId.size();
|
||||
buf.iv = &iv[0];
|
||||
buf.iv_size = iv.size();
|
||||
buf.data_offset = 0;
|
||||
buf.timestamp = 10;
|
||||
return buf;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(
|
||||
const std::vector<uint8_t>& encrypted, const std::vector<uint8_t>& iv,
|
||||
const std::vector<cdm::SubsampleEntry>& sub) {
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.subsamples = &sub[0];
|
||||
buf.num_subsamples = sub.size();
|
||||
return buf;
|
||||
}
|
||||
|
||||
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const uint32_t data_offset) {
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.data_offset = data_offset;
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::vector<cdm::SubsampleEntry> BuildMultipleSubsamples() {
|
||||
std::vector<cdm::SubsampleEntry> sub;
|
||||
sub.push_back(cdm::SubsampleEntry(3, 125));
|
||||
sub.push_back(cdm::SubsampleEntry(5, 62));
|
||||
sub.push_back(cdm::SubsampleEntry(8, 69));
|
||||
return sub;
|
||||
}
|
||||
|
||||
std::vector<cdm::SubsampleEntry> BuildSingleSubsample(size_t size) {
|
||||
std::vector<cdm::SubsampleEntry> sub;
|
||||
sub.push_back(cdm::SubsampleEntry(0, size));
|
||||
return sub;
|
||||
}
|
||||
|
||||
void GetOfflineConfiguration(std::string* key_id, std::string* license_server,
|
||||
std::string* client_auth) {
|
||||
// This method compares three different places settings could be found:
|
||||
// 1) A configuration object using the default settings.
|
||||
// 2) The global variables prefixed with g_.
|
||||
// 3) A configuration object configured for offline playback.
|
||||
// The desire is to use 3 unless the user customized the settings on the
|
||||
// command line, in which case we want to defer to them and use 2. The "if"
|
||||
// statements check if 1 and 2 are the same. If so, we know the user
|
||||
// did not customize the settings on the command line, and therefore we can
|
||||
// use 3. Otherwise, we use 2. This will never return the values from 1;
|
||||
// 1 is only used to check if 2 has been altered by the user.
|
||||
ConfigTestEnv std_config(kLicenseServerId);
|
||||
// TODO (juce): Switch this to kLicenseServerId once
|
||||
// kContentProtectionServer is the default.
|
||||
ConfigTestEnv config(wvcdm::kContentProtectionServer, false);
|
||||
|
||||
if (g_key_id.compare(a2bs_hex(std_config.key_id())) == 0)
|
||||
key_id->assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
else
|
||||
key_id->assign(g_key_id);
|
||||
|
||||
if (g_license_server.compare(std_config.license_server()) == 0)
|
||||
license_server->assign(config.license_server());
|
||||
else
|
||||
license_server->assign(g_license_server);
|
||||
|
||||
if (g_client_auth.compare(std_config.client_auth()) == 0)
|
||||
client_auth->assign(config.client_auth());
|
||||
else
|
||||
client_auth->assign(g_client_auth);
|
||||
}
|
||||
|
||||
cdm::ContentDecryptionModule_4* cdm_; // owned by host_
|
||||
wvcdm::scoped_ptr<TestHost_4> host_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class DummyCDM : public cdm::ContentDecryptionModule_4 {
|
||||
public:
|
||||
DummyCDM() : timer_fired_(false), last_context_(NULL) {}
|
||||
|
||||
virtual void CreateSession(uint32_t session_id,
|
||||
const char* mime_type, uint32_t mime_type_size,
|
||||
const uint8_t* init_data, uint32_t init_data_size,
|
||||
cdm::SessionType session_type) OVERRIDE {}
|
||||
|
||||
virtual void LoadSession(uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) OVERRIDE {}
|
||||
|
||||
virtual void UpdateSession(uint32_t session_id, const uint8_t* response,
|
||||
uint32_t response_size) OVERRIDE {}
|
||||
|
||||
virtual bool IsKeyValid(const uint8_t*, int) OVERRIDE { return false; }
|
||||
|
||||
virtual void ReleaseSession(uint32_t session_id) OVERRIDE {}
|
||||
|
||||
virtual void RemoveSession(uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) {}
|
||||
|
||||
virtual cdm::Status UsePrivacyMode() OVERRIDE { return cdm::kSuccess; }
|
||||
|
||||
virtual cdm::Status SetServerCertificate(
|
||||
const uint8_t* server_certificate_data,
|
||||
uint32_t server_certificate_data_size) OVERRIDE {
|
||||
return cdm::kSuccess;
|
||||
}
|
||||
|
||||
virtual void TimerExpired(void* context) OVERRIDE {
|
||||
timer_fired_ = true;
|
||||
last_context_ = context;
|
||||
}
|
||||
|
||||
virtual cdm::Status Decrypt(const cdm::InputBuffer&,
|
||||
cdm::DecryptedBlock*) OVERRIDE {
|
||||
return cdm::kSessionError;
|
||||
}
|
||||
|
||||
virtual cdm::Status DecryptDecodeAndRender(
|
||||
const cdm::InputBuffer& encrypted_buffer,
|
||||
cdm::StreamType stream_type) OVERRIDE {
|
||||
return cdm::kDecryptError;
|
||||
}
|
||||
|
||||
virtual void Destroy() OVERRIDE { delete this; }
|
||||
|
||||
bool TimerFired() const { return timer_fired_; }
|
||||
|
||||
void* LastTimerContext() const { return last_context_; }
|
||||
|
||||
void ResetTimerStatus() {
|
||||
timer_fired_ = false;
|
||||
last_context_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
bool timer_fired_;
|
||||
void* last_context_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(CdmApi4Test, TestHostTimer) {
|
||||
// Validate that the TestHost timers are processed in the correct order.
|
||||
// To do this, we replace the cdm with a dummy that only tracks timers.
|
||||
DummyCDM* cdm = new DummyCDM();
|
||||
|
||||
// The old CDM is destroyed by SetCdmPtr.
|
||||
cdm_ = cdm;
|
||||
host_->SetCdmPtr(cdm);
|
||||
|
||||
const double kTimerDelaySeconds = 1.0;
|
||||
const int64_t kTimerDelayMs = kTimerDelaySeconds * 1000;
|
||||
void* kCtx1 = reinterpret_cast<void*>(0x1);
|
||||
void* kCtx2 = reinterpret_cast<void*>(0x2);
|
||||
|
||||
host_->SetTimer(kTimerDelayMs * 1, kCtx1);
|
||||
host_->SetTimer(kTimerDelayMs * 2, kCtx2);
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_TRUE(cdm->TimerFired());
|
||||
EXPECT_EQ(kCtx1, cdm->LastTimerContext());
|
||||
cdm->ResetTimerStatus();
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_TRUE(cdm->TimerFired());
|
||||
EXPECT_EQ(kCtx2, cdm->LastTimerContext());
|
||||
cdm->ResetTimerStatus();
|
||||
|
||||
host_->FastForwardTime(kTimerDelaySeconds);
|
||||
EXPECT_FALSE(cdm->TimerFired());
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, DeviceCertificateTest) {
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
// Clear any existing certificates
|
||||
host_->file_store.erase(kCertFilename);
|
||||
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ASSERT_TRUE(SessionErrorPresent(kSessionId1, cdm::kNeedsDeviceCertificate));
|
||||
|
||||
// The Host must handle the certificate provisioning request.
|
||||
CreateSession(kSessionId2, "", cdm::kProvisioning);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
|
||||
ProcessProvisionResponse();
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
|
||||
|
||||
CreateSession(kSessionId3, g_key_id, cdm::kTemporary);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId3));
|
||||
} else {
|
||||
LOGI(
|
||||
"Skipping CdmApi4Test::DeviceCertificateTest because this platform "
|
||||
"does not support device certificates.");
|
||||
}
|
||||
}
|
||||
|
||||
// Note that these tests, BaseMessageTest, NormalDecryption, TimeTest, and
|
||||
// others are dependent on getting back a license from the license server where
|
||||
// the url for the license server is defined in the conf_test_env.cpp. If these
|
||||
// tests fail immediately, verify that the license server URL is correct
|
||||
// and works in your test environment.
|
||||
|
||||
TEST_F(CdmApi4Test, BaseMessageTest) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, NormalDecryption) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<uint8_t> expected = kOutputVector1;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, NormalSubSampleDecryptionWithSubsampleInfo) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<uint8_t> expected = kOutputVector2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, NormalSubSampleDecryptionWithMissingSubsampleInfo) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<uint8_t> expected = kOutputVector2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
|
||||
// Don't add these subsamples yet!
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.num_subsamples = sub.size();
|
||||
|
||||
TestDecryptedBlock output;
|
||||
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
|
||||
// Add the subsamples pointer and expect success.
|
||||
buf.subsamples = &sub[0];
|
||||
status = cdm_->Decrypt(buf, &output);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(
|
||||
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, DecryptWithDataOffset) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector3;
|
||||
std::vector<uint8_t> iv = kIv3;
|
||||
std::vector<uint8_t> expected = kOutputVector3;
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, kInputOffset3);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
cdm::Buffer* output_buf = output.DecryptedBuffer();
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
EXPECT_EQ(buf.data_size - buf.data_offset, output_buf->Size());
|
||||
EXPECT_EQ(0, memcmp(output.DecryptedBuffer()->Data(), &expected[0],
|
||||
output_buf->Size()));
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, DecryptReturnsSizedBuffer) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<uint8_t> expected = kOutputVector1;
|
||||
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
TestDecryptedBlock output;
|
||||
cdm::Status status = cdm_->Decrypt(buf, &output);
|
||||
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
cdm::Buffer* buffer = output.DecryptedBuffer();
|
||||
EXPECT_NE((void*)NULL, buffer);
|
||||
if (buffer) {
|
||||
EXPECT_EQ(expected.size(), output.DecryptedBuffer()->Size());
|
||||
}
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
// This exercises both timers and renewal.
|
||||
TEST_F(CdmApi4Test, RenewalTest) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
// We expect that by the time we've added a key, the CDM has set a timer.
|
||||
// Otherwise, it couldn't correctly handle renewal.
|
||||
EXPECT_NE(0, host_->NumTimers());
|
||||
host_->FastForwardTime(kTestPolicyRenewalDelaySeconds +
|
||||
kDelayWaitToForRenewalMessageSeconds);
|
||||
// When the timer expired, we should have sent a renewal, so we can
|
||||
// add this renewed key now, assuming things are working as expected.
|
||||
ProcessKeyResponse(true);
|
||||
// And the key should have been added.
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, SecureDecryptionLevel1) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
// Level 1 passes encrypted payload straight through. By calling the CDM's
|
||||
// DecryptDecodeAndRender, OEMCrypto_DecryptCTR will be told to use Direct
|
||||
// Rendering.
|
||||
std::vector<uint8_t> encrypted = kInputVector1;
|
||||
std::vector<uint8_t> iv = kIv1;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, SecureDecryptionLevel1WithSubsampleInfo) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
// Level 1 passes encrypted payload straight through. By calling the
|
||||
// CDM's DecryptDecodeAndRender, OEMCrypto_DecryptCTR will be told
|
||||
// to use Direct Rendering.
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, SecureDecryptionLevel1WithMissingSubsampleInfo) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
std::vector<uint8_t> encrypted = kInputVector2;
|
||||
std::vector<uint8_t> iv = kIv2;
|
||||
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
|
||||
|
||||
// Don't add these subsamples yet!
|
||||
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
|
||||
buf.num_subsamples = sub.size();
|
||||
|
||||
cdm::Status status;
|
||||
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
|
||||
EXPECT_EQ(cdm::kDecryptError, status);
|
||||
|
||||
// Add the subsamples pointer and expect success.
|
||||
buf.subsamples = &sub[0];
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
|
||||
EXPECT_EQ(cdm::kSuccess, status);
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, GenerateKeyRequestFailureSendsKeyError) {
|
||||
// Pass a bogus key id and expect failure.
|
||||
// Expect the CDM to pass a key error back to the host.
|
||||
CreateSession(kSessionId1, g_wrong_key_id, cdm::kTemporary);
|
||||
EXPECT_EQ(1, host_->SessionErrorsSize());
|
||||
EXPECT_EQ(0, host_->SessionMessagesSize());
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, UpdateSessionFailureSendsKeyError) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
TestHost_4::SessionMessage session_msg = host_->GetLastSessionMessage();
|
||||
EXPECT_TRUE(session_msg.default_url.empty());
|
||||
std::string drm_msg = GetKeyRequestResponse(
|
||||
session_msg, g_license_server + g_client_auth, false);
|
||||
|
||||
// Call UpdateSession with a bad session id, and expect the CDM to pass a key
|
||||
// error back to the host.
|
||||
UpdateSession(kSessionId2, reinterpret_cast<const uint8_t*>(drm_msg.c_str()),
|
||||
drm_msg.length());
|
||||
EXPECT_EQ(1, host_->SessionErrorsSize());
|
||||
|
||||
// Call UpdateSession with a bad license, and expect the CDM to pass a key
|
||||
// error back to the host.
|
||||
static const std::string kBadLicense = "!kGoodLicense";
|
||||
UpdateSession(kSessionId1,
|
||||
reinterpret_cast<const uint8_t*>(kBadLicense.c_str()),
|
||||
kBadLicense.length());
|
||||
EXPECT_EQ(2, host_->SessionErrorsSize());
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, IsKeyValidDetectsValidKey) {
|
||||
EXPECT_FALSE(cdm_->IsKeyValid(
|
||||
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ProcessKeyResponse(false);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
EXPECT_TRUE(cdm_->IsKeyValid(
|
||||
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
|
||||
ReleaseSession(kSessionId1);
|
||||
EXPECT_FALSE(cdm_->IsKeyValid(
|
||||
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, OfflineLicense) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id;
|
||||
std::string license_server;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
|
||||
|
||||
CreateSession(kSessionId1, key_id, cdm::kPersistent);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ProcessKeyResponse(false, license_server + client_auth);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, RestoreOfflineLicense) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id;
|
||||
std::string license_server;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
|
||||
|
||||
CreateSession(kSessionId1, key_id, cdm::kPersistent);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ProcessKeyResponse(false, license_server + client_auth);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
|
||||
std::string web_session_id = host_->session_map[kSessionId1];
|
||||
EXPECT_FALSE(web_session_id.empty());
|
||||
ReleaseSession(kSessionId1);
|
||||
|
||||
LoadSession(kSessionId2, web_session_id);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
|
||||
ReleaseSession(kSessionId2);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, ReleaseOfflineLicense) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id;
|
||||
std::string license_server;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
|
||||
|
||||
CreateSession(kSessionId1, key_id, cdm::kPersistent);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ProcessKeyResponse(false, license_server + client_auth);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
std::string web_session_id = host_->session_map[kSessionId1];
|
||||
EXPECT_FALSE(web_session_id.empty());
|
||||
ReleaseSession(kSessionId1);
|
||||
|
||||
RemoveSession(kSessionId2, web_session_id);
|
||||
ProcessKeyResponse(true, license_server + client_auth);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, MimeTypeMatters) {
|
||||
CreateSessionWithMimeType(kSessionId1, "video/mp4");
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ReleaseSession(kSessionId1);
|
||||
|
||||
CreateSessionWithMimeType(kSessionId1, "video/webm");
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
ReleaseSession(kSessionId1);
|
||||
|
||||
CreateSessionWithMimeType(kSessionId1, "video/blah");
|
||||
ASSERT_TRUE(SessionErrorPresent(kSessionId1));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, UsePrivacyMode) {
|
||||
ASSERT_EQ(cdm::kSuccess, UsePrivacyMode());
|
||||
|
||||
const CdmClientPropertySet& property_set =
|
||||
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
|
||||
EXPECT_TRUE(property_set.use_privacy_mode());
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, UsePrivacyModeFailsWithOpenSessions) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
|
||||
EXPECT_NE(cdm::kSuccess, UsePrivacyMode());
|
||||
const CdmClientPropertySet& property_set =
|
||||
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
|
||||
EXPECT_FALSE(property_set.use_privacy_mode());
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, SetExplicitServerCertificate) {
|
||||
ASSERT_EQ(cdm::kSuccess, SetServerCertificate(kFakeServerCertficiate));
|
||||
|
||||
const CdmClientPropertySet& property_set =
|
||||
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
|
||||
EXPECT_TRUE(property_set.use_privacy_mode());
|
||||
const std::string& set_cert = property_set.service_certificate();
|
||||
ASSERT_EQ(kFakeServerCertficiate.size(), set_cert.size());
|
||||
EXPECT_EQ(0,
|
||||
memcmp(&kFakeServerCertficiate[0], &set_cert[0], set_cert.size()));
|
||||
}
|
||||
|
||||
TEST_F(CdmApi4Test, SetServerCertificateFailsWithOpenSessions) {
|
||||
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
|
||||
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
|
||||
|
||||
EXPECT_NE(cdm::kSuccess, SetServerCertificate(kFakeServerCertficiate));
|
||||
const CdmClientPropertySet& property_set =
|
||||
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
|
||||
EXPECT_FALSE(property_set.use_privacy_mode());
|
||||
|
||||
ReleaseSession(kSessionId1);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
1078
cdm/test/cdm_test.cpp
Normal file
1078
cdm/test/cdm_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_
|
||||
#define WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "config_test_env.h"
|
||||
|
||||
extern std::string g_client_auth;
|
||||
extern std::string g_key_id;
|
||||
extern std::string g_license_server;
|
||||
extern std::string g_wrong_key_id;
|
||||
|
||||
static const wvcdm::LicenseServerId kLicenseServerId =
|
||||
wvcdm::kContentProtectionServer;
|
||||
|
||||
#endif // WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_
|
||||
@@ -1,96 +1,87 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "cdm_test_config.h"
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
#if defined(__linux__)
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
// Default license server, can be configured using --server command line option
|
||||
// Default key id (pssh), can be configured using --keyid command line option
|
||||
std::string g_client_auth;
|
||||
std::string g_key_id;
|
||||
std::string g_license_server;
|
||||
std::string g_wrong_key_id;
|
||||
#include "cdm.h"
|
||||
#include "device_cert.h"
|
||||
#include "override.h"
|
||||
#include "test_host.h"
|
||||
|
||||
#if defined(OEMCRYPTO_TESTS)
|
||||
# include "oemcrypto_test.h"
|
||||
#endif
|
||||
|
||||
using namespace widevine;
|
||||
|
||||
TestHost* g_host = NULL;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Init gtest and let it consume arguments.
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config(kLicenseServerId);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
|
||||
// The following variables are configurable through command line options.
|
||||
g_license_server.assign(config.license_server());
|
||||
g_key_id.assign(config.key_id());
|
||||
std::string license_server(g_license_server);
|
||||
|
||||
// Parse arguments.
|
||||
int show_usage = 0;
|
||||
static const struct option long_options[] = {
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"server", required_argument, NULL, 's'},
|
||||
{NULL, 0, NULL, '\0'}};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
|
||||
&option_index)) != -1) {
|
||||
int opt;
|
||||
int verbosity = 0;
|
||||
while ((opt = getopt_long(argc, argv, "v", NULL, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'k': {
|
||||
g_key_id.clear();
|
||||
g_key_id.assign(optarg);
|
||||
case 'v':
|
||||
++verbosity;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
// This option has already been consumed by wvcdm::InitLogging() above.
|
||||
// We only tell getopt about it so that it is not an error. We ignore
|
||||
// the option here when seen.
|
||||
// TODO: Stop passing argv to InitLogging, and instead set the log
|
||||
// level here through the logging API. We should keep all command-line
|
||||
// parsing at the application level, rather than split between various
|
||||
// apps and various platform-specific logging implementations.
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
case '?':
|
||||
show_usage = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage) {
|
||||
std::cout << std::endl;
|
||||
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --server=<server_url>";
|
||||
std::cout
|
||||
<< "configure the license server url, please include http[s] in the url"
|
||||
<< std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
std::cout << "default: " << license_server << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
|
||||
std::cout << "configure the key id or pssh, in hex format" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " default keyid:";
|
||||
std::cout << g_key_id << std::endl;
|
||||
fprintf(stderr, "Usage: %s [-v|-vv|-vvv]\n\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
|
||||
// Set up a Host so that tests and initialize the library. This makes these
|
||||
// services available to the tests. We would do this in the test suite
|
||||
// itself, but the core & OEMCrypto tests don't know they depend on this
|
||||
// for storage.
|
||||
g_host = new TestHost();
|
||||
Cdm::ClientInfo client_info;
|
||||
// Set client info that denotes this as the test suite:
|
||||
client_info.product_name = "CE cdm tests";
|
||||
client_info.company_name = "www";
|
||||
client_info.model_name = "www";
|
||||
#if defined(__linux__)
|
||||
client_info.device_name = "Linux";
|
||||
{
|
||||
struct utsname name;
|
||||
if (uname(&name) == 0) {
|
||||
client_info.arch_name = name.machine;
|
||||
}
|
||||
}
|
||||
#else
|
||||
client_info.device_name = "unknown";
|
||||
#endif
|
||||
client_info.build_info = __DATE__;
|
||||
|
||||
g_key_id = wvcdm::a2bs_hex(g_key_id);
|
||||
config.set_license_server(g_license_server);
|
||||
config.set_key_id(g_key_id);
|
||||
Cdm::DeviceCertificateRequest cert_request;
|
||||
Cdm::Status status = Cdm::initialize(
|
||||
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host, &cert_request,
|
||||
static_cast<Cdm::LogLevel>(verbosity));
|
||||
assert(status == Cdm::kSuccess);
|
||||
assert(cert_request.needed == false);
|
||||
|
||||
#if defined(OEMCRYPTO_TESTS)
|
||||
// Set up the OEMCrypto test harness.
|
||||
wvoec::global_features.Initialize(false /* is_cast_receiver */,
|
||||
false /* force_load_test_keybox */);
|
||||
::testing::GTEST_FLAG(filter)
|
||||
= wvoec::global_features.RestrictFilter(::testing::GTEST_FLAG(filter));
|
||||
#endif
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
67
cdm/test/cdm_test_printers.cpp
Normal file
67
cdm/test/cdm_test_printers.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "cdm_test_printers.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
void PrintTo(const Cdm::MessageType& value, ::std::ostream* os) {
|
||||
switch (value) {
|
||||
case Cdm::kLicenseRequest: *os << "Cdm::kLicenseRequest";
|
||||
break;
|
||||
case Cdm::kLicenseRenewal: *os << "Cdm::kLicenseRenewal";
|
||||
break;
|
||||
case Cdm::kLicenseRelease: *os << "Cdm::kLicenseRelease";
|
||||
break;
|
||||
case Cdm::kIndividualizationRequest:
|
||||
*os << "Cdm::kIndividualizationRequest";
|
||||
break;
|
||||
default: *os << "Unknown Cdm::MessageType value " << value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTo(const Cdm::Status& value, ::std::ostream* os) {
|
||||
switch (value) {
|
||||
case Cdm::kSuccess: *os << "Cdm::kSuccess";
|
||||
break;
|
||||
case Cdm::kNeedsDeviceCertificate: *os << "Cdm::kNeedsDeviceCertificate";
|
||||
break;
|
||||
case Cdm::kSessionNotFound: *os << "Cdm::kSessionNotFound";
|
||||
break;
|
||||
case Cdm::kDecryptError: *os << "Cdm::kDecryptError";
|
||||
break;
|
||||
case Cdm::kNoKey: *os << "Cdm::kNoKey";
|
||||
break;
|
||||
case Cdm::kInvalidAccess: *os << "Cdm::kInvalidAccess";
|
||||
break;
|
||||
case Cdm::kNotSupported: *os << "Cdm::kNotSupported";
|
||||
break;
|
||||
case Cdm::kInvalidState: *os << "Cdm::kInvalidState";
|
||||
break;
|
||||
case Cdm::kQuotaExceeded: *os << "Cdm::kQuotaExceeded";
|
||||
break;
|
||||
case Cdm::kUnexpectedError: *os << "Cdm::kUnexpectedError";
|
||||
break;
|
||||
default: *os << "Unknown Cdm::Status value " << value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTo(const Cdm::KeyStatus& value, ::std::ostream* os) {
|
||||
switch (value) {
|
||||
case Cdm::kUsable: *os << "Cdm::kUsable";
|
||||
break;
|
||||
case Cdm::kExpired: *os << "Cdm::kExpired";
|
||||
break;
|
||||
case Cdm::kOutputNotAllowed: *os << "Cdm::kOutputNotAllowed";
|
||||
break;
|
||||
case Cdm::kStatusPending: *os << "Cdm::kStatusPending";
|
||||
break;
|
||||
case Cdm::kInternalError: *os << "Cdm::kInternalError";
|
||||
break;
|
||||
default: *os << "Unknown Cdm::KeyStatus value " << value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
19
cdm/test/cdm_test_printers.h
Normal file
19
cdm/test/cdm_test_printers.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
// This file adds some print methods so that when unit tests fail, the
|
||||
// will print the name of an enumeration instead of the numeric value.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_
|
||||
#define WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_
|
||||
|
||||
#include <iostream>
|
||||
#include "cdm.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
void PrintTo(const Cdm::MessageType& value, ::std::ostream* os);
|
||||
void PrintTo(const Cdm::Status& value, ::std::ostream* os);
|
||||
void PrintTo(const Cdm::KeyStatus& value, ::std::ostream* os);
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_
|
||||
@@ -5,77 +5,77 @@
|
||||
const uint8_t kDeviceCert[] = {
|
||||
0x0A, 0x9A, 0x14, 0x08, 0x01, 0x10, 0x01, 0x1A,
|
||||
0x93, 0x14, 0x0A, 0xED, 0x09, 0x0A, 0xAE, 0x02,
|
||||
0x08, 0x02, 0x12, 0x10, 0x19, 0x77, 0x50, 0x4D,
|
||||
0x66, 0x1C, 0x28, 0xBE, 0x41, 0xD3, 0xAA, 0x33,
|
||||
0x98, 0x3D, 0xB5, 0x46, 0x18, 0x8D, 0xA3, 0x81,
|
||||
0x9E, 0x05, 0x22, 0x8E, 0x02, 0x30, 0x82, 0x01,
|
||||
0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB2, 0xA8,
|
||||
0x6A, 0x68, 0x01, 0x84, 0xC2, 0x62, 0x08, 0x8D,
|
||||
0xFB, 0x5E, 0xA7, 0x5B, 0xC2, 0x70, 0x6E, 0xF0,
|
||||
0xE1, 0xF7, 0xCE, 0x0D, 0x06, 0x29, 0x8A, 0x8C,
|
||||
0x11, 0x6C, 0xB8, 0xBD, 0xEC, 0xA5, 0x9F, 0x60,
|
||||
0x65, 0x9F, 0x89, 0x52, 0xC0, 0x72, 0xF8, 0x2A,
|
||||
0x30, 0xBD, 0xB8, 0x52, 0xFF, 0x2F, 0x6C, 0xAE,
|
||||
0x36, 0x1E, 0xED, 0xC2, 0x0E, 0x00, 0x64, 0x57,
|
||||
0x0F, 0xFD, 0xA9, 0xEE, 0xCE, 0x62, 0xB0, 0x23,
|
||||
0xB8, 0x68, 0x2B, 0xED, 0xDE, 0x3B, 0xCA, 0xCD,
|
||||
0x07, 0x55, 0xDB, 0xF7, 0x77, 0xCE, 0x6B, 0x16,
|
||||
0xE0, 0x17, 0x6C, 0xE6, 0xBE, 0x76, 0xBE, 0x31,
|
||||
0xC8, 0x62, 0x98, 0xF3, 0x62, 0x3B, 0x24, 0x73,
|
||||
0x43, 0x19, 0x0A, 0x0A, 0xE9, 0xA4, 0x76, 0xA8,
|
||||
0x27, 0x12, 0xBD, 0xF5, 0x8A, 0x28, 0x99, 0x54,
|
||||
0x8A, 0x55, 0x56, 0x6B, 0x92, 0x57, 0xA2, 0x6C,
|
||||
0x7E, 0xD9, 0xEE, 0xF7, 0xCE, 0xEC, 0xB6, 0xA4,
|
||||
0xC2, 0xC2, 0x4F, 0x60, 0xF9, 0x6C, 0x96, 0x1E,
|
||||
0x5A, 0x1C, 0xC6, 0x14, 0x2D, 0x6D, 0xA8, 0x83,
|
||||
0x8C, 0x80, 0x07, 0x5F, 0x20, 0xFC, 0xAF, 0x08,
|
||||
0x98, 0xAE, 0x94, 0x10, 0x13, 0x49, 0xD3, 0xDF,
|
||||
0x94, 0x8A, 0x51, 0xE2, 0x1D, 0xD6, 0x9E, 0xA6,
|
||||
0xE1, 0x56, 0x23, 0x0D, 0x69, 0x4D, 0x1A, 0x14,
|
||||
0xEF, 0x5D, 0x33, 0xF8, 0x5D, 0x7A, 0xEF, 0xD8,
|
||||
0x02, 0x9E, 0xF2, 0xFB, 0xB3, 0x75, 0x04, 0xAF,
|
||||
0x9C, 0x19, 0x73, 0xEC, 0xA3, 0x01, 0x8F, 0xD7,
|
||||
0x03, 0xB7, 0x58, 0xD4, 0x2A, 0xD4, 0xA6, 0x21,
|
||||
0xC8, 0x39, 0x58, 0xCA, 0x2F, 0x40, 0x19, 0x12,
|
||||
0x35, 0x58, 0x4D, 0xA8, 0x60, 0x06, 0xEE, 0x90,
|
||||
0x17, 0xE3, 0xB8, 0xA2, 0xE2, 0xDC, 0xEB, 0x74,
|
||||
0x50, 0x0E, 0xCD, 0x34, 0x44, 0x04, 0x09, 0xC1,
|
||||
0xC6, 0xE5, 0x55, 0xAF, 0xFD, 0x14, 0xB6, 0x27,
|
||||
0x4F, 0xA7, 0x2C, 0xB4, 0xE1, 0x3D, 0x02, 0x03,
|
||||
0x08, 0x02, 0x12, 0x10, 0x7F, 0x6F, 0x31, 0x50,
|
||||
0xB3, 0x73, 0x26, 0x2C, 0xBD, 0xC3, 0xB2, 0xDA,
|
||||
0xDE, 0x07, 0x5D, 0xBB, 0x18, 0xF3, 0xFA, 0xB0,
|
||||
0xA9, 0x05, 0x22, 0x8E, 0x02, 0x30, 0x82, 0x01,
|
||||
0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBF, 0xAF,
|
||||
0x84, 0xAC, 0x9C, 0x2D, 0x57, 0x4C, 0xDE, 0x10,
|
||||
0xB9, 0x94, 0xF0, 0x51, 0x4F, 0xBE, 0xA0, 0x0D,
|
||||
0xDC, 0x7E, 0x44, 0x38, 0x76, 0xD7, 0xB7, 0xBF,
|
||||
0x0B, 0x7C, 0x1A, 0x7A, 0x15, 0x9B, 0x66, 0xD1,
|
||||
0xE4, 0x67, 0x83, 0xA8, 0xE5, 0xFA, 0x11, 0xE3,
|
||||
0xEA, 0xB8, 0x84, 0xD8, 0x32, 0x9C, 0x8B, 0xA7,
|
||||
0xCC, 0x60, 0xBE, 0x68, 0x7C, 0x13, 0x29, 0xBD,
|
||||
0xF5, 0x8A, 0x6E, 0xFC, 0x7B, 0xAA, 0x4A, 0x72,
|
||||
0xB8, 0xB2, 0x52, 0xD8, 0xEA, 0x03, 0x87, 0x75,
|
||||
0xE4, 0xE4, 0x52, 0xD9, 0x83, 0x72, 0x7A, 0xA5,
|
||||
0x9E, 0x0B, 0x27, 0xD8, 0xA1, 0x6A, 0x23, 0xF4,
|
||||
0x59, 0xAB, 0xE8, 0xD5, 0x5B, 0xDD, 0x64, 0xC8,
|
||||
0xFD, 0x30, 0x28, 0x5B, 0x0D, 0x0E, 0xCE, 0x7F,
|
||||
0x1E, 0x4C, 0xC0, 0x36, 0x17, 0xC5, 0xC1, 0xD0,
|
||||
0x19, 0x0C, 0x5A, 0x71, 0xCA, 0x3B, 0xCA, 0x41,
|
||||
0xD9, 0x67, 0x36, 0x0A, 0x23, 0xA3, 0xDC, 0x9B,
|
||||
0x86, 0x53, 0xF6, 0xAC, 0x1E, 0xD9, 0x41, 0x34,
|
||||
0x73, 0x04, 0xE1, 0x68, 0x1C, 0x38, 0xA8, 0x2A,
|
||||
0xE8, 0x0D, 0x88, 0xF9, 0xA9, 0xD4, 0x64, 0x4E,
|
||||
0xFF, 0xF8, 0x94, 0x56, 0x99, 0xDB, 0xA2, 0x6C,
|
||||
0x15, 0xF2, 0xA1, 0x34, 0xF6, 0x51, 0xC0, 0xAB,
|
||||
0x2A, 0x99, 0xB9, 0xF3, 0x3D, 0x2D, 0x13, 0xCD,
|
||||
0x8B, 0x6B, 0xAD, 0x82, 0x10, 0x73, 0x4E, 0x42,
|
||||
0x61, 0x78, 0x69, 0x2F, 0x19, 0x09, 0x06, 0x65,
|
||||
0x0F, 0x7E, 0xA9, 0xB1, 0xB3, 0xC2, 0x30, 0x86,
|
||||
0xE9, 0x0B, 0x29, 0xC0, 0xB6, 0x08, 0x73, 0x7C,
|
||||
0x3B, 0xFA, 0x39, 0x5A, 0x5A, 0xB1, 0xD5, 0x6E,
|
||||
0xFF, 0x85, 0x41, 0xF3, 0xBF, 0xE9, 0xD9, 0x45,
|
||||
0xE6, 0xDD, 0xB1, 0x35, 0x39, 0x56, 0xA5, 0xCE,
|
||||
0xBD, 0x6C, 0xC3, 0xFD, 0xEA, 0x47, 0xE9, 0x65,
|
||||
0x58, 0x21, 0x62, 0xFF, 0x00, 0x9A, 0xFE, 0xD3,
|
||||
0x5B, 0x15, 0xC2, 0xC9, 0x64, 0x3F, 0x02, 0x03,
|
||||
0x01, 0x00, 0x01, 0x28, 0x99, 0x20, 0x12, 0x80,
|
||||
0x02, 0x6B, 0x7E, 0x26, 0xBC, 0x2C, 0x1F, 0x19,
|
||||
0x55, 0x2B, 0xBF, 0x88, 0x3F, 0x48, 0xDE, 0xE2,
|
||||
0x8E, 0x82, 0xCC, 0x77, 0x72, 0x31, 0x34, 0x2B,
|
||||
0x4E, 0x2C, 0xDF, 0xFB, 0xF6, 0xA1, 0x6F, 0x94,
|
||||
0xCA, 0x01, 0x45, 0xAE, 0x42, 0x0A, 0xFF, 0x51,
|
||||
0xE9, 0xB8, 0x6C, 0xDF, 0xB1, 0x1E, 0x35, 0x81,
|
||||
0x1D, 0xF9, 0xB7, 0x51, 0x75, 0xE4, 0xF4, 0xD6,
|
||||
0xEA, 0x7D, 0xBC, 0x96, 0x2F, 0x29, 0x3D, 0xC1,
|
||||
0x89, 0x1C, 0x5C, 0xF9, 0x08, 0x67, 0x7C, 0x0E,
|
||||
0xBB, 0xC2, 0x9D, 0x28, 0x6F, 0x38, 0x2E, 0x57,
|
||||
0x41, 0xA1, 0x96, 0x37, 0xCE, 0x7D, 0xD4, 0x24,
|
||||
0xFC, 0x26, 0xB2, 0x5E, 0xDF, 0x63, 0x33, 0xBA,
|
||||
0x3E, 0x3F, 0x92, 0xB6, 0x79, 0xC7, 0xE7, 0x14,
|
||||
0x86, 0xC5, 0xEE, 0x96, 0xF3, 0x2F, 0xFB, 0x4B,
|
||||
0xF3, 0xC4, 0xCA, 0xF3, 0x54, 0xDB, 0x56, 0x6C,
|
||||
0x7F, 0x4E, 0xC7, 0x81, 0x42, 0x87, 0x5D, 0x22,
|
||||
0xDF, 0x80, 0x9C, 0xE0, 0x9C, 0xE1, 0x9F, 0x0C,
|
||||
0xBC, 0xB7, 0x58, 0x44, 0x84, 0xE8, 0x3F, 0x97,
|
||||
0x0C, 0x79, 0x5A, 0x89, 0x09, 0x3D, 0x77, 0xB7,
|
||||
0xEF, 0xCC, 0x45, 0xB9, 0xC8, 0x20, 0xBE, 0x0A,
|
||||
0x2F, 0x7A, 0x30, 0x3F, 0x7D, 0x07, 0x33, 0xF9,
|
||||
0x5D, 0xD0, 0x23, 0x11, 0xD8, 0x21, 0xC9, 0x42,
|
||||
0xCC, 0x7A, 0x25, 0xD1, 0xEA, 0x22, 0x6B, 0x65,
|
||||
0x34, 0xF2, 0x66, 0xE7, 0xB9, 0x1B, 0xF7, 0x19,
|
||||
0xCA, 0xA3, 0x28, 0x8B, 0x06, 0x99, 0x88, 0xFB,
|
||||
0xD4, 0xA5, 0x24, 0xDC, 0x5D, 0x1F, 0x57, 0xC6,
|
||||
0xBB, 0xB4, 0x1B, 0x16, 0x31, 0x8E, 0xC2, 0x4E,
|
||||
0xA1, 0x54, 0xDE, 0x19, 0x77, 0x3A, 0x1B, 0xE6,
|
||||
0x3F, 0xAF, 0x91, 0x47, 0x0B, 0xED, 0x59, 0xD8,
|
||||
0xCD, 0x47, 0xF0, 0x62, 0xE0, 0xFA, 0xF3, 0xB1,
|
||||
0x60, 0xE5, 0xAD, 0x98, 0x12, 0x71, 0x90, 0x58,
|
||||
0xA9, 0xD7, 0x55, 0x92, 0x62, 0xD3, 0xEA, 0x5B,
|
||||
0xF8, 0x1A, 0xB6, 0x05, 0x0A, 0xB0, 0x02, 0x08,
|
||||
0x02, 0x72, 0xAA, 0xE9, 0xCD, 0xE2, 0xF8, 0xFC,
|
||||
0x8B, 0x85, 0x52, 0xFE, 0x30, 0xAD, 0x49, 0x94,
|
||||
0x3F, 0x56, 0x5A, 0x60, 0xFD, 0xDD, 0x2D, 0x79,
|
||||
0xDA, 0xB4, 0x5F, 0x3A, 0x87, 0x10, 0x82, 0x5F,
|
||||
0x0B, 0xBD, 0x20, 0x17, 0xF1, 0x59, 0xB0, 0x3F,
|
||||
0xDE, 0x24, 0x3A, 0x13, 0x43, 0xBC, 0xC6, 0xBC,
|
||||
0xC0, 0xFD, 0xEF, 0xCF, 0x3B, 0x19, 0xBF, 0x07,
|
||||
0xD7, 0xD9, 0xF0, 0x1E, 0x1B, 0xDF, 0xF9, 0x35,
|
||||
0x82, 0x69, 0x3F, 0x3F, 0xF3, 0x71, 0x65, 0xB5,
|
||||
0x86, 0xF8, 0xCB, 0x44, 0x2E, 0xEE, 0x80, 0xE3,
|
||||
0x53, 0x6D, 0xA8, 0x50, 0xDB, 0x87, 0x1A, 0x3B,
|
||||
0x49, 0x78, 0x6E, 0x61, 0x3D, 0x60, 0x61, 0xFD,
|
||||
0xE8, 0xBE, 0x05, 0x77, 0xFF, 0x2B, 0x0F, 0x0D,
|
||||
0xE4, 0x53, 0x29, 0xB8, 0x55, 0x52, 0x2E, 0x7F,
|
||||
0xA3, 0x54, 0x0F, 0x66, 0x93, 0x4B, 0x17, 0xFB,
|
||||
0xE1, 0x82, 0x33, 0x59, 0x21, 0x9E, 0x44, 0x02,
|
||||
0x27, 0x4E, 0xEC, 0x86, 0xC4, 0x6C, 0x5E, 0xF9,
|
||||
0xD0, 0x31, 0xDD, 0xFA, 0xB8, 0x5D, 0x05, 0x2E,
|
||||
0xBC, 0xE9, 0x48, 0xB8, 0xC2, 0xF4, 0x15, 0x2C,
|
||||
0xDF, 0x51, 0x6B, 0x75, 0x62, 0x7D, 0x99, 0x67,
|
||||
0x25, 0x6F, 0x0D, 0xA9, 0x5A, 0x96, 0x01, 0x3D,
|
||||
0x8F, 0x14, 0x13, 0x36, 0x9D, 0x8D, 0x34, 0xF1,
|
||||
0xEC, 0xFC, 0xFE, 0xEA, 0xBA, 0xAD, 0xFC, 0x7C,
|
||||
0xCE, 0xA3, 0x36, 0x80, 0xFF, 0xB8, 0x9D, 0x60,
|
||||
0x70, 0x78, 0xE1, 0x42, 0x7F, 0xD7, 0x99, 0x61,
|
||||
0xBB, 0x51, 0x70, 0xC7, 0xE5, 0xA3, 0x74, 0xFA,
|
||||
0x18, 0x02, 0x4B, 0x2A, 0x5D, 0xC4, 0x40, 0x1E,
|
||||
0x6A, 0x22, 0xA9, 0xC5, 0x37, 0xF3, 0xD9, 0x7F,
|
||||
0x8B, 0x1A, 0xD4, 0x8B, 0x3B, 0x00, 0x78, 0xB3,
|
||||
0xAA, 0x7C, 0x04, 0xBB, 0x96, 0x31, 0x33, 0x65,
|
||||
0xFA, 0xF2, 0xEB, 0x87, 0x2D, 0xF1, 0x8A, 0xFA,
|
||||
0xEE, 0x7B, 0x1F, 0xD4, 0x67, 0xBD, 0x3F, 0x0F,
|
||||
0x61, 0x1A, 0xB6, 0x05, 0x0A, 0xB0, 0x02, 0x08,
|
||||
0x01, 0x12, 0x10, 0x08, 0x4D, 0x60, 0xBC, 0x65,
|
||||
0x93, 0x7D, 0xB6, 0xAC, 0x7F, 0x99, 0x2B, 0x41,
|
||||
0xEC, 0xF5, 0xF2, 0x18, 0xA2, 0xD3, 0x8A, 0x8C,
|
||||
@@ -162,175 +162,175 @@ const uint8_t kDeviceCert[] = {
|
||||
0xD2, 0xB9, 0x60, 0x79, 0x2D, 0x11, 0xDA, 0x50,
|
||||
0x86, 0xAB, 0x8C, 0xCF, 0xF4, 0x61, 0x80, 0xC2,
|
||||
0x43, 0xC3, 0x95, 0xD2, 0x2F, 0x1A, 0x9E, 0x4F,
|
||||
0x6B, 0x02, 0x12, 0xA0, 0x0A, 0x2D, 0x2C, 0x09,
|
||||
0x70, 0xC7, 0xEE, 0xCF, 0x10, 0x8A, 0x67, 0x80,
|
||||
0x6B, 0xC0, 0x7D, 0x66, 0x49, 0x90, 0x15, 0xFC,
|
||||
0x96, 0xC1, 0x9E, 0xB9, 0x33, 0x15, 0xAA, 0x16,
|
||||
0x29, 0x79, 0x3C, 0x79, 0x15, 0x49, 0x8B, 0x6C,
|
||||
0x1F, 0x76, 0x65, 0xFB, 0xFC, 0x7F, 0xFE, 0x34,
|
||||
0x1D, 0x97, 0xDB, 0x80, 0xD2, 0x15, 0x20, 0x35,
|
||||
0x85, 0x45, 0x51, 0x2F, 0x37, 0x87, 0xB0, 0xA6,
|
||||
0xFC, 0xF4, 0x35, 0x7B, 0x05, 0xB8, 0xC0, 0x1B,
|
||||
0x30, 0x21, 0x5E, 0xE0, 0x1B, 0x79, 0x66, 0xF8,
|
||||
0x77, 0x33, 0x11, 0x8D, 0x23, 0xD4, 0x4E, 0xB3,
|
||||
0x06, 0xAE, 0x09, 0xF6, 0x72, 0x32, 0xDF, 0xA3,
|
||||
0x99, 0x78, 0x19, 0xD8, 0xFC, 0xB6, 0xB3, 0xAF,
|
||||
0x46, 0xBB, 0xB2, 0x52, 0x50, 0x4D, 0xDE, 0xF7,
|
||||
0x9D, 0xD4, 0xB8, 0x7C, 0xEB, 0xD9, 0x94, 0x75,
|
||||
0x90, 0x4E, 0x83, 0x06, 0x17, 0x0C, 0x33, 0x31,
|
||||
0x09, 0x73, 0x7B, 0xED, 0x05, 0xFE, 0xE3, 0xC1,
|
||||
0x4C, 0xA9, 0x25, 0x3F, 0x33, 0x27, 0x17, 0xD0,
|
||||
0x03, 0x4F, 0x8A, 0x0E, 0x03, 0xBF, 0xE1, 0xD6,
|
||||
0x5D, 0x0E, 0xBA, 0x51, 0x61, 0x7F, 0x43, 0xA5,
|
||||
0x46, 0xC7, 0x9E, 0xA3, 0x62, 0xAC, 0xCA, 0xDA,
|
||||
0x20, 0xE5, 0x4C, 0xB3, 0x3D, 0x9D, 0xF2, 0x0E,
|
||||
0xF4, 0x08, 0xDF, 0x9C, 0xC0, 0x99, 0xC6, 0xFF,
|
||||
0xD0, 0x8E, 0x5F, 0x81, 0x72, 0x73, 0x6A, 0xEF,
|
||||
0xE1, 0xEE, 0x4C, 0x03, 0x77, 0xFF, 0x28, 0xD3,
|
||||
0x66, 0x19, 0x7D, 0x08, 0xD4, 0x6E, 0x61, 0xC7,
|
||||
0x59, 0x69, 0x9A, 0xF8, 0xC8, 0xB2, 0xAB, 0xE0,
|
||||
0xB4, 0x05, 0x45, 0xC2, 0x01, 0xDF, 0x94, 0x7D,
|
||||
0xC2, 0xA8, 0xC9, 0x9B, 0x30, 0x1A, 0x4E, 0x1B,
|
||||
0xB2, 0xB2, 0x1C, 0xCE, 0x06, 0x47, 0x9B, 0xCF,
|
||||
0xA2, 0xF5, 0x56, 0x97, 0x18, 0x96, 0xC0, 0xFE,
|
||||
0x08, 0x63, 0x75, 0x9C, 0xC0, 0xE6, 0xBC, 0x37,
|
||||
0x36, 0x13, 0x15, 0x34, 0x60, 0xDB, 0xAF, 0xD1,
|
||||
0x9F, 0xA7, 0xDD, 0x1D, 0xD2, 0x25, 0x2F, 0xB6,
|
||||
0x72, 0xC2, 0x13, 0x65, 0x12, 0x08, 0x0F, 0x20,
|
||||
0x54, 0x2B, 0x7F, 0x33, 0xC2, 0xE6, 0xA9, 0x1D,
|
||||
0xB6, 0x6C, 0xC5, 0xCA, 0xD6, 0xCC, 0x1B, 0x90,
|
||||
0xF4, 0x81, 0xD6, 0xDE, 0xB1, 0x87, 0x52, 0xF7,
|
||||
0x24, 0xD3, 0x46, 0xA2, 0xB4, 0x8F, 0xF9, 0xD3,
|
||||
0x81, 0xA3, 0x24, 0x79, 0xC0, 0x28, 0xE9, 0xF7,
|
||||
0x67, 0x2D, 0x8A, 0x6B, 0x05, 0x2D, 0xD9, 0xEE,
|
||||
0xD2, 0x54, 0x5B, 0x30, 0xA5, 0xC0, 0xC7, 0x85,
|
||||
0x94, 0x1B, 0xF5, 0xD0, 0xDC, 0x0D, 0x69, 0x10,
|
||||
0xE7, 0x3A, 0xA3, 0xDB, 0x0F, 0x9B, 0x77, 0x10,
|
||||
0x75, 0x11, 0x57, 0x64, 0xBB, 0x69, 0x4A, 0x87,
|
||||
0xC7, 0x3F, 0xD7, 0xB4, 0x74, 0xE4, 0x96, 0xFC,
|
||||
0xB7, 0x6A, 0xE5, 0x99, 0xBE, 0xD4, 0x36, 0x9D,
|
||||
0xF4, 0x51, 0xDE, 0xDE, 0xF6, 0x71, 0x72, 0xE4,
|
||||
0x46, 0x79, 0x4C, 0x04, 0x0F, 0x4D, 0x8C, 0xCB,
|
||||
0x21, 0xA4, 0x9C, 0xE3, 0xF1, 0x4D, 0x4A, 0xE7,
|
||||
0xF7, 0x37, 0x1E, 0x91, 0xC5, 0x97, 0x1D, 0xC0,
|
||||
0x75, 0x21, 0xB0, 0xA1, 0xB5, 0xCF, 0xD5, 0x0A,
|
||||
0x54, 0x00, 0x81, 0x61, 0x72, 0xC8, 0x1D, 0xBF,
|
||||
0x0B, 0x16, 0xE7, 0xE0, 0x7D, 0x7E, 0x91, 0xB8,
|
||||
0x8C, 0x1D, 0xCF, 0x32, 0xB7, 0xCE, 0xB3, 0x4C,
|
||||
0xE7, 0xD0, 0x53, 0xC5, 0x52, 0x91, 0xDC, 0x04,
|
||||
0xEC, 0x39, 0xE5, 0x9F, 0x21, 0x0B, 0x36, 0xD7,
|
||||
0x11, 0x30, 0x89, 0x96, 0x72, 0x3F, 0x9D, 0x3A,
|
||||
0x7F, 0xC9, 0x9C, 0x51, 0x38, 0x1C, 0xC4, 0x12,
|
||||
0x9E, 0x8C, 0x59, 0x3D, 0x1F, 0x37, 0x66, 0xF8,
|
||||
0x42, 0x61, 0xB1, 0xC0, 0x3D, 0xA8, 0xEB, 0xF5,
|
||||
0xBD, 0x83, 0x31, 0x8F, 0xF9, 0x17, 0x6B, 0xAE,
|
||||
0xD3, 0x6E, 0x5A, 0x0D, 0x7E, 0x40, 0x82, 0xA1,
|
||||
0x5B, 0x52, 0xA4, 0x94, 0x76, 0x2E, 0x9D, 0x27,
|
||||
0x51, 0x0E, 0xEA, 0xD5, 0xC6, 0x65, 0x61, 0x47,
|
||||
0x8F, 0x67, 0x55, 0x4B, 0x9A, 0x35, 0xBA, 0x40,
|
||||
0x31, 0x52, 0xFD, 0x7E, 0xE1, 0xE6, 0x7D, 0xFB,
|
||||
0xBB, 0xCA, 0x33, 0xF3, 0xFA, 0x23, 0xB7, 0x17,
|
||||
0x5B, 0xE7, 0xD2, 0x93, 0xEB, 0x25, 0x60, 0x73,
|
||||
0x0B, 0x09, 0x27, 0x48, 0x6C, 0x8E, 0xF6, 0xE7,
|
||||
0xDF, 0x6D, 0x0D, 0xD7, 0x04, 0xAB, 0x04, 0x64,
|
||||
0x56, 0x86, 0x79, 0x49, 0x8B, 0xF8, 0xD2, 0xE7,
|
||||
0x7F, 0x59, 0xAB, 0x90, 0xDE, 0xA1, 0x04, 0x8C,
|
||||
0x15, 0x24, 0x4C, 0xFA, 0x87, 0x51, 0x1F, 0x12,
|
||||
0xA2, 0xB3, 0x66, 0x3A, 0x8F, 0x31, 0x9A, 0x08,
|
||||
0xF4, 0x2C, 0xE2, 0x04, 0x02, 0x12, 0xB3, 0xFF,
|
||||
0xBF, 0x7E, 0xE0, 0x04, 0x4E, 0x85, 0x51, 0x52,
|
||||
0xF3, 0x08, 0x09, 0x77, 0x22, 0x54, 0x1E, 0xEC,
|
||||
0xE8, 0x5B, 0x41, 0x52, 0x43, 0x4D, 0xBF, 0x24,
|
||||
0x03, 0xAF, 0x4F, 0x3D, 0x58, 0xEE, 0xCD, 0x0E,
|
||||
0x91, 0x1F, 0x03, 0x55, 0xCA, 0xF5, 0x28, 0x40,
|
||||
0xB3, 0xA8, 0xAA, 0x7F, 0x5E, 0xD1, 0x67, 0x3D,
|
||||
0x68, 0xD1, 0xB7, 0x4E, 0xF7, 0x9E, 0xB4, 0x96,
|
||||
0xED, 0xDF, 0xCA, 0x50, 0x4D, 0x4C, 0xD2, 0xC2,
|
||||
0xC7, 0x87, 0x5D, 0x1D, 0x85, 0x76, 0xCA, 0x34,
|
||||
0x1C, 0x47, 0x8B, 0x8F, 0xB2, 0x63, 0x74, 0xDB,
|
||||
0xB8, 0x99, 0x8B, 0x96, 0x47, 0x87, 0x8A, 0xA7,
|
||||
0x3A, 0xEB, 0x17, 0x17, 0xEF, 0x59, 0xC1, 0xF6,
|
||||
0x15, 0x00, 0x78, 0x09, 0x98, 0xB5, 0x52, 0xC1,
|
||||
0x78, 0x4C, 0xA3, 0x0B, 0xAE, 0xA8, 0xF9, 0xEA,
|
||||
0xC1, 0xC8, 0x0D, 0x75, 0x91, 0xE5, 0xB4, 0xF7,
|
||||
0x9D, 0xA8, 0x6B, 0xDA, 0xFA, 0x63, 0xC3, 0xB6,
|
||||
0x36, 0x9F, 0xB3, 0x78, 0x7D, 0xF0, 0x27, 0xED,
|
||||
0x45, 0x3F, 0xD9, 0x5D, 0xB3, 0xE7, 0xB3, 0xB7,
|
||||
0xB0, 0xE6, 0x9E, 0x4F, 0x08, 0xA0, 0xF7, 0x09,
|
||||
0x22, 0x7D, 0x7D, 0x9F, 0x9C, 0x61, 0xB8, 0xAE,
|
||||
0x9B, 0x9C, 0xCD, 0x84, 0x9F, 0xBF, 0xEE, 0x8D,
|
||||
0x40, 0xF4, 0x9A, 0x72, 0x8F, 0xBF, 0xC2, 0xEF,
|
||||
0xC8, 0x70, 0x8E, 0xAB, 0x10, 0xAC, 0x5B, 0xDA,
|
||||
0xDC, 0x9F, 0x4E, 0xA0, 0xBA, 0x78, 0xA3, 0x2C,
|
||||
0x81, 0xF4, 0x2D, 0xCD, 0x35, 0x1E, 0x46, 0xEF,
|
||||
0x05, 0x24, 0x3A, 0x46, 0xE7, 0x1B, 0xD0, 0xF4,
|
||||
0x94, 0x5C, 0x58, 0x7B, 0xAC, 0xB4, 0x9E, 0xFF,
|
||||
0xBD, 0x8D, 0xB0, 0xFB, 0xEB, 0xAC, 0x02, 0x2D,
|
||||
0xFA, 0x17, 0x6D, 0x92, 0x54, 0x9F, 0x70, 0x4E,
|
||||
0x7E, 0x30, 0xED, 0x70, 0x42, 0x4B, 0x38, 0x76,
|
||||
0x82, 0x63, 0x18, 0x77, 0x5F, 0x10, 0x94, 0x6F,
|
||||
0xD6, 0xA2, 0x3F, 0x27, 0xC0, 0xDC, 0xC9, 0xAA,
|
||||
0x64, 0xE5, 0x8C, 0x11, 0x31, 0x31, 0x22, 0x40,
|
||||
0xEB, 0x1D, 0x4E, 0xA6, 0x8D, 0xCF, 0x68, 0x78,
|
||||
0x8F, 0x84, 0xEF, 0xCB, 0x3D, 0x96, 0x56, 0x9F,
|
||||
0x1D, 0xFC, 0xF4, 0xFB, 0xBF, 0xB4, 0x9E, 0x39,
|
||||
0xAB, 0x1B, 0x7F, 0x62, 0xDD, 0x7E, 0x03, 0x8E,
|
||||
0x3A, 0x16, 0x45, 0xF0, 0x56, 0x9B, 0xAE, 0x9A,
|
||||
0xFB, 0x3C, 0xC4, 0x81, 0x2B, 0xBA, 0x35, 0xE8,
|
||||
0x85, 0x20, 0x48, 0x8B, 0xB0, 0x4C, 0x44, 0x85,
|
||||
0xFA, 0xDB, 0xC2, 0xAE, 0x4E, 0x2D, 0xE5, 0x30,
|
||||
0x62, 0x6F, 0xC6, 0xEC, 0xD2, 0x1C, 0xE4, 0xF0,
|
||||
0x1E, 0x71, 0xA3, 0x35, 0x41, 0xF4, 0x3E, 0x2F,
|
||||
0xDD, 0x36, 0x14, 0x4F, 0x8C, 0xD5, 0x3E, 0x46,
|
||||
0x81, 0x24, 0x51, 0x93, 0x2B, 0xB0, 0x00, 0x03,
|
||||
0x93, 0xFE, 0x45, 0x0B, 0x11, 0x6C, 0x36, 0xEA,
|
||||
0xF3, 0x06, 0xB5, 0xC7, 0xD5, 0x49, 0x1A, 0xB5,
|
||||
0xD4, 0xBD, 0x97, 0x38, 0x35, 0xE2, 0x84, 0xAC,
|
||||
0xFC, 0xDE, 0x8C, 0x50, 0xB6, 0x2D, 0x37, 0x21,
|
||||
0xA8, 0xAE, 0x06, 0x31, 0x70, 0x87, 0x1E, 0xB6,
|
||||
0x55, 0xB7, 0x5B, 0x28, 0x07, 0x00, 0x00, 0xC7,
|
||||
0x55, 0xAD, 0xE7, 0xF5, 0xB6, 0xA6, 0x0C, 0xA3,
|
||||
0x1A, 0x36, 0xAA, 0xC5, 0x37, 0x55, 0x24, 0x9F,
|
||||
0xD9, 0x1A, 0x75, 0xD3, 0x5C, 0x5E, 0x9A, 0x43,
|
||||
0xE9, 0x1A, 0xA2, 0x93, 0xAD, 0xD6, 0x3B, 0xD2,
|
||||
0x2E, 0x82, 0x09, 0xE4, 0x1C, 0x0A, 0x7B, 0x0E,
|
||||
0x50, 0xCD, 0xAF, 0x00, 0xE6, 0xF4, 0xCF, 0x54,
|
||||
0xE0, 0xE9, 0xB8, 0x93, 0xF7, 0xD7, 0x64, 0x1A,
|
||||
0x76, 0x0C, 0x2F, 0x85, 0x67, 0xE6, 0x7C, 0xFA,
|
||||
0x3C, 0xB8, 0xC7, 0x42, 0xF6, 0x3D, 0x40, 0xDD,
|
||||
0xBD, 0x2D, 0xC1, 0x0C, 0x2D, 0x17, 0x98, 0xDC,
|
||||
0x37, 0x7D, 0x02, 0xD7, 0x23, 0x72, 0x09, 0xA5,
|
||||
0xF2, 0x4C, 0xA3, 0xC8, 0xC9, 0x87, 0x89, 0xE4,
|
||||
0xCF, 0x88, 0xD8, 0x6F, 0x8F, 0xD7, 0x1E, 0x83,
|
||||
0x8B, 0x80, 0x11, 0xFE, 0xA8, 0x93, 0xE2, 0xCD,
|
||||
0x38, 0x8A, 0x88, 0x1E, 0x66, 0x69, 0x3C, 0x98,
|
||||
0x43, 0xC1, 0x0D, 0x98, 0x8F, 0x34, 0x33, 0x29,
|
||||
0xC0, 0x33, 0xD0, 0xDD, 0xC1, 0x99, 0x80, 0x47,
|
||||
0x5B, 0x02, 0xC3, 0x59, 0x04, 0x3F, 0x96, 0xC4,
|
||||
0x84, 0x8D, 0xE2, 0xF7, 0x04, 0xCD, 0x94, 0xAB,
|
||||
0x38, 0x82, 0x19, 0x45, 0x69, 0x21, 0x4E, 0x39,
|
||||
0xB6, 0xF7, 0x78, 0xD5, 0x37, 0x0E, 0x8B, 0x07,
|
||||
0xD3, 0xFA, 0x60, 0xE7, 0x41, 0x4B, 0xD0, 0x1A,
|
||||
0xB6, 0x76, 0x61, 0xC0, 0x94, 0x21, 0xD1, 0x47,
|
||||
0x4D, 0xCC, 0x7E, 0x50, 0x67, 0xA4, 0x2E, 0x93,
|
||||
0x4D, 0xC5, 0xFE, 0x08, 0x51, 0x60, 0x98, 0x91,
|
||||
0x65, 0x11, 0x7E, 0x32, 0xA4, 0x15, 0xA0, 0xC6,
|
||||
0x35, 0xE8, 0xDA, 0xE2, 0xDB, 0x62, 0x8F, 0x9C,
|
||||
0x5F, 0xF3, 0xF4, 0x27, 0xC0, 0x25, 0x10, 0x74,
|
||||
0x76, 0x9F, 0x16, 0x13, 0xEC, 0xBF, 0x4A, 0xCE,
|
||||
0x5D, 0x60, 0xFB, 0xFE, 0x14, 0xF8, 0x18, 0x70,
|
||||
0xC2, 0x2D, 0xBD, 0x92, 0xAF, 0xEC, 0xAF, 0xE3,
|
||||
0xAB, 0xA4, 0xBD, 0x7B, 0xDE, 0x47, 0x12, 0x3D,
|
||||
0x1F, 0x8C, 0x1D, 0x74, 0x66, 0x2B, 0x2E, 0x7B,
|
||||
0x0C, 0x08, 0xF3, 0x29, 0xCB, 0x89, 0x8D, 0x22,
|
||||
0x38, 0x70, 0x7A, 0x24, 0xC5, 0x44, 0x28, 0xF7,
|
||||
0x90, 0x9E, 0xD3, 0xFA, 0x6B, 0x4B, 0xDB, 0x32,
|
||||
0xE0, 0xBE, 0x65, 0xBF, 0x37, 0x87, 0xF2, 0xA7,
|
||||
0x6F, 0xB5, 0x48, 0xC4, 0xDE, 0x12, 0x20, 0x2C,
|
||||
0x88, 0x46, 0x18, 0x74, 0xC2, 0x4A, 0x8A, 0x9F,
|
||||
0xDF, 0x09, 0x4E, 0xBE, 0x94, 0xB8, 0x94, 0xED,
|
||||
0xA6, 0x95, 0xCC, 0x54, 0x82, 0xB5, 0x8B, 0xF3,
|
||||
0xE6, 0xBD, 0xA1, 0xD8, 0xE8, 0x19, 0xB8,
|
||||
0x6B, 0x02, 0x12, 0xA0, 0x0A, 0x92, 0x1C, 0xDD,
|
||||
0x85, 0x4C, 0xEB, 0x5E, 0x8E, 0xF0, 0x48, 0x26,
|
||||
0x1F, 0xB8, 0x9B, 0xE1, 0x79, 0x96, 0xBF, 0x44,
|
||||
0x0D, 0x88, 0x67, 0xF6, 0x48, 0x65, 0xD8, 0xE5,
|
||||
0x33, 0x93, 0x05, 0x07, 0xAC, 0x0C, 0x0B, 0x60,
|
||||
0xE5, 0xDB, 0x60, 0xD0, 0x3F, 0x42, 0x58, 0x8E,
|
||||
0x73, 0x9D, 0x91, 0x6F, 0xD7, 0xD3, 0x14, 0x97,
|
||||
0x75, 0xCA, 0xDC, 0xF2, 0xBA, 0x8C, 0x20, 0x13,
|
||||
0xD8, 0xD2, 0x07, 0x74, 0xAA, 0xC2, 0x07, 0x89,
|
||||
0xFF, 0x95, 0x27, 0x52, 0x63, 0x94, 0xA5, 0xF1,
|
||||
0xA9, 0x20, 0x61, 0xEE, 0x56, 0x81, 0xA0, 0xDD,
|
||||
0xF7, 0xF0, 0xAF, 0xD6, 0xF3, 0x83, 0x88, 0x9C,
|
||||
0xD2, 0x50, 0xE5, 0x43, 0xB8, 0xD6, 0xF0, 0x93,
|
||||
0x7D, 0x11, 0x52, 0x82, 0xFD, 0xD8, 0xCA, 0xF5,
|
||||
0xC4, 0xD1, 0xE7, 0x78, 0x1C, 0xEC, 0x5C, 0x34,
|
||||
0x82, 0x8D, 0x82, 0x88, 0xB3, 0x84, 0xBF, 0xCD,
|
||||
0x82, 0xA5, 0x8D, 0xE7, 0xCA, 0xAD, 0x39, 0x9F,
|
||||
0x98, 0x03, 0xD1, 0x05, 0xE0, 0xA3, 0x0A, 0x88,
|
||||
0xA6, 0x5E, 0xBA, 0x8D, 0xC3, 0xF7, 0xB9, 0x07,
|
||||
0x75, 0x14, 0x54, 0xE8, 0x3A, 0x96, 0x02, 0x94,
|
||||
0xB5, 0x21, 0x7B, 0xDA, 0x30, 0xE8, 0xC6, 0x25,
|
||||
0x1F, 0xE2, 0x53, 0x1D, 0xDA, 0xE5, 0xE1, 0x2A,
|
||||
0xDD, 0x74, 0xDE, 0x03, 0xDA, 0x73, 0xCB, 0x1D,
|
||||
0x1B, 0x1F, 0xCA, 0xFE, 0x23, 0x42, 0xFA, 0x47,
|
||||
0x1E, 0xB3, 0x1E, 0x43, 0x55, 0xB4, 0x1B, 0x67,
|
||||
0x51, 0x3C, 0x20, 0x42, 0x36, 0x57, 0xCA, 0x83,
|
||||
0x5F, 0xD5, 0x1F, 0x7E, 0xC5, 0x80, 0x1E, 0x04,
|
||||
0xCE, 0xD0, 0xDA, 0x72, 0xAF, 0xEA, 0x91, 0x60,
|
||||
0xC2, 0xC5, 0x4E, 0xA1, 0x99, 0x55, 0x93, 0x09,
|
||||
0xA8, 0x6F, 0xCE, 0x78, 0x71, 0xE1, 0x56, 0x01,
|
||||
0x17, 0x76, 0xA1, 0x37, 0x20, 0x21, 0x42, 0xE9,
|
||||
0xF5, 0xB7, 0x77, 0xC9, 0xF7, 0x18, 0x2A, 0xB4,
|
||||
0x12, 0xD0, 0x0A, 0x60, 0x14, 0x02, 0x6A, 0xE5,
|
||||
0x63, 0x39, 0x5B, 0xD4, 0xB9, 0xBE, 0xA9, 0x4D,
|
||||
0x28, 0x86, 0xA3, 0x87, 0xB6, 0x91, 0xCF, 0x61,
|
||||
0xEB, 0x83, 0x0F, 0xE7, 0x4E, 0x42, 0xC2, 0xDC,
|
||||
0xDC, 0xF3, 0x2F, 0x31, 0x78, 0xC7, 0x73, 0x5A,
|
||||
0xB2, 0x5C, 0x69, 0xDC, 0x89, 0x82, 0xDF, 0x2A,
|
||||
0xEF, 0xC5, 0x36, 0x89, 0x8F, 0xF1, 0x1F, 0x2B,
|
||||
0x8B, 0x56, 0x28, 0x2A, 0x68, 0x34, 0xBE, 0x69,
|
||||
0x58, 0x89, 0x09, 0x19, 0x3D, 0xDE, 0xBC, 0xD3,
|
||||
0x8F, 0x73, 0x30, 0xAA, 0xA5, 0x5F, 0xDF, 0x65,
|
||||
0x91, 0xC2, 0xC3, 0x70, 0xE5, 0xF8, 0x9D, 0x26,
|
||||
0x47, 0xBA, 0xAB, 0x90, 0x41, 0xD6, 0xE5, 0x84,
|
||||
0xF7, 0xF8, 0x10, 0x0E, 0x09, 0x75, 0x4D, 0xCC,
|
||||
0x45, 0x8B, 0x9A, 0x58, 0xB3, 0xD4, 0x58, 0xC1,
|
||||
0x7C, 0x8E, 0x78, 0x11, 0xF9, 0x4A, 0xAA, 0xB5,
|
||||
0xB5, 0x32, 0x04, 0x2E, 0x63, 0x78, 0x5A, 0x0C,
|
||||
0xD0, 0xB2, 0xE7, 0x1F, 0x16, 0xD0, 0xDB, 0x21,
|
||||
0x3A, 0xC5, 0xEE, 0xBF, 0x8F, 0xE8, 0x50, 0xF0,
|
||||
0x93, 0x9B, 0xAF, 0x7C, 0x95, 0x5A, 0x9F, 0x61,
|
||||
0xB0, 0xFC, 0x98, 0xE8, 0x9D, 0x99, 0xB8, 0xEA,
|
||||
0x86, 0xDD, 0x4B, 0x84, 0x79, 0xD8, 0xEC, 0xCE,
|
||||
0xE9, 0x66, 0xDC, 0x4F, 0xF8, 0x4E, 0x3D, 0x64,
|
||||
0x28, 0xCB, 0xE0, 0x3E, 0x28, 0x81, 0x11, 0xB6,
|
||||
0x4D, 0xFE, 0x20, 0x2C, 0xF5, 0xC4, 0xD0, 0x5E,
|
||||
0x12, 0x05, 0x25, 0xEE, 0x6D, 0x81, 0xDE, 0x87,
|
||||
0x5F, 0x45, 0xC8, 0x1B, 0x68, 0x33, 0xE2, 0xC8,
|
||||
0xF9, 0x13, 0x0F, 0xF4, 0x6B, 0xFF, 0x75, 0x02,
|
||||
0xF7, 0xAC, 0x25, 0xC3, 0xB7, 0x24, 0xC3, 0x88,
|
||||
0xEF, 0xD1, 0xDC, 0xAC, 0xF6, 0x57, 0x5B, 0x4E,
|
||||
0xC4, 0x74, 0x21, 0x89, 0x04, 0x44, 0xCA, 0x11,
|
||||
0x74, 0xA7, 0xB2, 0x29, 0x43, 0x45, 0x2D, 0xBD,
|
||||
0x2E, 0xB0, 0xA7, 0x81, 0x70, 0x78, 0xD4, 0x7C,
|
||||
0xE9, 0x22, 0x30, 0x48, 0xF2, 0xF0, 0x4B, 0xD4,
|
||||
0x9F, 0x49, 0x18, 0xF2, 0x82, 0xC9, 0x2C, 0xF0,
|
||||
0xCD, 0xB6, 0x94, 0x86, 0x24, 0x14, 0xBC, 0x3F,
|
||||
0x07, 0x9A, 0x54, 0x76, 0xDE, 0x53, 0x10, 0x2B,
|
||||
0xAD, 0x54, 0x06, 0xD8, 0xFA, 0xE6, 0x27, 0x49,
|
||||
0x5E, 0xB7, 0x9C, 0x68, 0xAE, 0x3C, 0x1E, 0x66,
|
||||
0x7E, 0x88, 0xAD, 0xF2, 0x2E, 0xAE, 0x5E, 0xDE,
|
||||
0xAC, 0x89, 0xCE, 0xED, 0x7D, 0x8C, 0x7D, 0x7E,
|
||||
0x68, 0x49, 0x7F, 0x41, 0x79, 0xF4, 0x34, 0x28,
|
||||
0x1E, 0xCC, 0x83, 0xE1, 0xE5, 0xCB, 0x29, 0xCA,
|
||||
0x41, 0x36, 0xCD, 0x35, 0x84, 0x34, 0x63, 0x15,
|
||||
0xFF, 0x7E, 0x3E, 0xEE, 0x9A, 0x3B, 0x52, 0x16,
|
||||
0x3B, 0xAF, 0x4B, 0xE7, 0x84, 0xF2, 0x5B, 0x0A,
|
||||
0x9B, 0x9F, 0x35, 0x4D, 0xCE, 0xA6, 0xBE, 0xB4,
|
||||
0xF5, 0xAE, 0xBF, 0xA8, 0x9E, 0xEF, 0xA8, 0x2F,
|
||||
0x58, 0x22, 0x74, 0xC1, 0x02, 0xB4, 0x34, 0x96,
|
||||
0x57, 0xC4, 0x96, 0x88, 0x24, 0xA1, 0x81, 0xB5,
|
||||
0x33, 0xA6, 0x9E, 0x18, 0x43, 0x3F, 0x35, 0xD3,
|
||||
0x66, 0x03, 0x53, 0x47, 0xF7, 0x4B, 0x10, 0x61,
|
||||
0x58, 0x06, 0xEE, 0x3B, 0xB1, 0x0F, 0x29, 0x4F,
|
||||
0xFD, 0x62, 0x5C, 0x4B, 0x23, 0x52, 0x4E, 0x16,
|
||||
0x5A, 0x2E, 0x06, 0x4E, 0xCB, 0x00, 0xCA, 0xD6,
|
||||
0xF1, 0x06, 0x9D, 0x6D, 0x93, 0x72, 0x1D, 0x1D,
|
||||
0x2F, 0x09, 0xBC, 0x66, 0xC6, 0x87, 0xC6, 0xD4,
|
||||
0xF3, 0xB7, 0xD6, 0xFA, 0xFA, 0x67, 0xFD, 0xFF,
|
||||
0x9A, 0x20, 0x1D, 0x24, 0xF5, 0xCD, 0xC3, 0xD6,
|
||||
0xEC, 0x28, 0xB9, 0x9A, 0x93, 0x67, 0xFF, 0x9E,
|
||||
0x19, 0x96, 0x70, 0xB9, 0x61, 0x26, 0x75, 0x58,
|
||||
0x29, 0x52, 0x52, 0xDB, 0x75, 0xCC, 0x2E, 0xB2,
|
||||
0x2F, 0xEB, 0x34, 0x7D, 0x83, 0x82, 0x23, 0xA2,
|
||||
0x61, 0x4F, 0xED, 0x19, 0x64, 0xDC, 0xCD, 0x1C,
|
||||
0x0E, 0xB8, 0x32, 0xF6, 0x5C, 0x68, 0x94, 0xA9,
|
||||
0xDF, 0xBC, 0x23, 0xB4, 0x95, 0x3D, 0x75, 0x18,
|
||||
0x14, 0xBE, 0xE1, 0x67, 0x44, 0xDD, 0x39, 0x7E,
|
||||
0x10, 0x00, 0xFE, 0x90, 0xF4, 0x56, 0x15, 0x40,
|
||||
0xCF, 0x8E, 0xBC, 0x95, 0xF9, 0x53, 0xA9, 0xD3,
|
||||
0xFA, 0xB3, 0xCD, 0xF2, 0x82, 0xEC, 0xC5, 0xA3,
|
||||
0xE2, 0xDD, 0xDD, 0xCD, 0xA3, 0xC7, 0x1D, 0x55,
|
||||
0xEA, 0x7B, 0x4E, 0x93, 0xE9, 0xBE, 0x15, 0x34,
|
||||
0xF2, 0x77, 0xA8, 0x44, 0x12, 0x22, 0x81, 0xF0,
|
||||
0x82, 0xE0, 0x89, 0x71, 0xBE, 0xD3, 0x12, 0x04,
|
||||
0x27, 0xFC, 0xB1, 0xC6, 0x16, 0x7C, 0x3C, 0x6D,
|
||||
0xA4, 0xD5, 0x6C, 0xBA, 0x6B, 0x78, 0x03, 0xA9,
|
||||
0x77, 0xCB, 0xD6, 0x69, 0xB4, 0x8F, 0xB6, 0xEA,
|
||||
0xE9, 0xAA, 0x54, 0xA6, 0x8A, 0x2B, 0x75, 0x8A,
|
||||
0x54, 0x3C, 0xE0, 0x24, 0xBD, 0x2D, 0xF4, 0xA7,
|
||||
0x88, 0x84, 0x72, 0x2E, 0x3D, 0x49, 0x01, 0x6E,
|
||||
0xBB, 0x01, 0x4C, 0x0D, 0x17, 0xDB, 0x7F, 0xF0,
|
||||
0xF7, 0x67, 0x0F, 0xA3, 0x2F, 0x75, 0x39, 0x5D,
|
||||
0x59, 0xEE, 0xD4, 0x0D, 0x12, 0x5A, 0xCA, 0x7A,
|
||||
0x2B, 0xD7, 0xBF, 0x5D, 0xB7, 0xF4, 0xE0, 0x6F,
|
||||
0x18, 0x29, 0x75, 0x13, 0xC7, 0x97, 0x2E, 0xEE,
|
||||
0xF3, 0x28, 0x3B, 0x13, 0xC1, 0x34, 0x70, 0xA1,
|
||||
0x94, 0x00, 0x5C, 0xA5, 0x5C, 0x41, 0x81, 0xB5,
|
||||
0xC9, 0x6D, 0xD4, 0xE7, 0x1C, 0xC0, 0xC0, 0x4C,
|
||||
0x23, 0x44, 0x9B, 0xDC, 0x24, 0xCB, 0x72, 0x9D,
|
||||
0xE7, 0xDD, 0x80, 0x34, 0xFD, 0x55, 0xE7, 0xA6,
|
||||
0xD0, 0x78, 0x3C, 0x0D, 0x00, 0x96, 0xC6, 0xBB,
|
||||
0x33, 0x95, 0x35, 0xD8, 0x8D, 0x78, 0x8B, 0x64,
|
||||
0x76, 0x55, 0x2B, 0xE0, 0x0E, 0xD7, 0x59, 0xC0,
|
||||
0x21, 0x41, 0xF5, 0x66, 0x83, 0x56, 0xF4, 0x56,
|
||||
0xB2, 0x07, 0x70, 0xE7, 0x71, 0x49, 0x42, 0xF5,
|
||||
0xED, 0x09, 0x30, 0x88, 0x6F, 0xC3, 0x0D, 0x21,
|
||||
0xD1, 0xF8, 0xC3, 0xD7, 0x2C, 0xCE, 0x40, 0x96,
|
||||
0x5B, 0x30, 0xB3, 0x79, 0xF9, 0x0F, 0x0F, 0x07,
|
||||
0xA7, 0x73, 0xC1, 0x96, 0x0E, 0xD8, 0x3E, 0x2D,
|
||||
0xF6, 0x04, 0xF4, 0xDE, 0x50, 0x50, 0xC8, 0x4F,
|
||||
0x8A, 0xE2, 0xDC, 0xED, 0x89, 0x5D, 0x2A, 0x07,
|
||||
0xFE, 0xD9, 0x6E, 0xDE, 0xED, 0x2F, 0xDE, 0xC5,
|
||||
0xE9, 0x18, 0x48, 0xE1, 0xAD, 0xC0, 0x27, 0xE7,
|
||||
0xC5, 0xB2, 0xE8, 0xC8, 0x58, 0x2C, 0x52, 0x23,
|
||||
0x78, 0x1B, 0x9D, 0xC4, 0xA6, 0x01, 0x07, 0xE6,
|
||||
0xBE, 0xE2, 0x26, 0x17, 0x81, 0x5F, 0x49, 0x3A,
|
||||
0xE6, 0xDB, 0x51, 0xC6, 0x06, 0xD7, 0x2C, 0x36,
|
||||
0x13, 0xB0, 0x2B, 0x04, 0xE4, 0x29, 0xDD, 0xFE,
|
||||
0x6F, 0x9B, 0xA3, 0xFD, 0x68, 0x79, 0x56, 0x24,
|
||||
0x8A, 0x28, 0x6D, 0x69, 0xD6, 0x54, 0x5C, 0xD7,
|
||||
0xF3, 0x28, 0x45, 0x77, 0x78, 0x27, 0x23, 0x35,
|
||||
0xCA, 0x68, 0xBB, 0x1A, 0x47, 0x41, 0x51, 0x8E,
|
||||
0x9A, 0x01, 0x49, 0xBD, 0xE8, 0xB6, 0x3A, 0x26,
|
||||
0xFE, 0x4A, 0x43, 0xE5, 0xF7, 0xCB, 0x3B, 0x21,
|
||||
0xBC, 0xF3, 0xD4, 0xEF, 0x38, 0x89, 0x06, 0x0A,
|
||||
0xA2, 0x3D, 0xA1, 0xF6, 0xEC, 0x84, 0x0E, 0xF4,
|
||||
0xAA, 0x1E, 0xD3, 0x4B, 0x5E, 0x91, 0x6C, 0xD9,
|
||||
0x83, 0x5F, 0xB3, 0x0B, 0x47, 0x3E, 0x85, 0x18,
|
||||
0xE9, 0x2D, 0x8A, 0x33, 0xAE, 0x34, 0xF6, 0x58,
|
||||
0x54, 0x11, 0xDC, 0xD8, 0xC4, 0x6D, 0x7C, 0xAA,
|
||||
0x15, 0xCD, 0xCC, 0x17, 0x7A, 0xF2, 0x77, 0x57,
|
||||
0x5F, 0x40, 0xF8, 0x58, 0xF4, 0x96, 0xDF, 0x6E,
|
||||
0xFC, 0xB9, 0x70, 0x17, 0x08, 0x5B, 0x43, 0x29,
|
||||
0x4D, 0xD4, 0xA7, 0x6C, 0xDD, 0x8E, 0xC7, 0xFD,
|
||||
0x8D, 0xE9, 0xE1, 0xBA, 0x7E, 0xFF, 0x39, 0xE5,
|
||||
0x74, 0x79, 0x5E, 0x9A, 0x29, 0x2F, 0xC3, 0x0D,
|
||||
0xDD, 0x65, 0x1C, 0x2F, 0xBB, 0x3C, 0x50, 0x8F,
|
||||
0x5B, 0x47, 0x9A, 0x91, 0xEF, 0xC4, 0x0F, 0x7F,
|
||||
0xCD, 0x85, 0xBC, 0x7C, 0x41, 0x9C, 0x96, 0x59,
|
||||
0x17, 0x9B, 0x95, 0x3E, 0x26, 0x41, 0xDD, 0xE5,
|
||||
0xD1, 0x72, 0x85, 0x10, 0x05, 0x9C, 0xBF, 0x88,
|
||||
0x26, 0x29, 0xBE, 0x96, 0x6C, 0x0C, 0x36, 0x76,
|
||||
0xE8, 0x06, 0xC5, 0x04, 0xD9, 0x06, 0xA8, 0x79,
|
||||
0xF8, 0xEA, 0xF0, 0x60, 0xAF, 0x12, 0x20, 0xA8,
|
||||
0x98, 0xAD, 0x74, 0xB9, 0x6F, 0x94, 0xCA, 0xCB,
|
||||
0xDD, 0x9C, 0x4E, 0x62, 0xDF, 0xD1, 0x59, 0x3E,
|
||||
0x58, 0xEE, 0x82, 0xB9, 0xAD, 0x9E, 0xB7, 0x45,
|
||||
0x2C, 0x1F, 0x8C, 0x15, 0x77, 0xCC, 0xD2
|
||||
};
|
||||
|
||||
const size_t kDeviceCertSize = sizeof(kDeviceCert);
|
||||
|
||||
93
cdm/test/test_host.cpp
Normal file
93
cdm/test/test_host.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#include "test_host.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "device_cert.h"
|
||||
#include "log.h"
|
||||
|
||||
using namespace widevine;
|
||||
|
||||
TestHost::TestHost() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void TestHost::Reset() {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
gettimeofday(&tv, NULL);
|
||||
now_ = (tv.tv_sec * 1000LL) + (tv.tv_usec / 1000LL);
|
||||
|
||||
// Surprisingly, std::priority_queue has no clear().
|
||||
while (!timers_.empty()) {
|
||||
timers_.pop();
|
||||
}
|
||||
|
||||
files_.clear();
|
||||
files_["cert.bin"] =
|
||||
std::string((const char*)kDeviceCert, kDeviceCertSize);
|
||||
}
|
||||
|
||||
void TestHost::ElapseTime(int64_t milliseconds) {
|
||||
int64_t goal_time = now_ + milliseconds;
|
||||
while (now_ < goal_time) {
|
||||
if (timers_.empty()) {
|
||||
now_ = goal_time;
|
||||
} else {
|
||||
Timer t = timers_.top();
|
||||
timers_.pop();
|
||||
ASSERT_GE(t.expiry_time, now_);
|
||||
now_ = t.expiry_time;
|
||||
t.client->onTimerExpired(t.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TestHost::NumTimers() const { return timers_.size(); }
|
||||
|
||||
bool TestHost::read(const std::string& name,
|
||||
std::string* data) {
|
||||
StorageMap::iterator it = files_.find(name);
|
||||
bool ok = it != files_.end();
|
||||
LOGD("read file: %s: %s", name.c_str(), ok ? "ok" : "fail");
|
||||
if (!ok) return false;
|
||||
*data = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestHost::write(const std::string& name,
|
||||
const std::string& data) {
|
||||
LOGD("write file: %s", name.c_str());
|
||||
files_[name] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestHost::exists(const std::string& name) {
|
||||
StorageMap::iterator it = files_.find(name);
|
||||
bool ok = it != files_.end();
|
||||
LOGD("exists? %s: %s", name.c_str(), ok ? "true" : "false");
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool TestHost::remove(const std::string& name) {
|
||||
files_.erase(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t TestHost::size(const std::string& name) {
|
||||
StorageMap::iterator it = files_.find(name);
|
||||
if (it == files_.end()) return -1;
|
||||
return it->second.size();
|
||||
}
|
||||
|
||||
int64_t TestHost::now() {
|
||||
return now_;
|
||||
}
|
||||
|
||||
void TestHost::setTimeout(int64_t delay_ms,
|
||||
IClient* client,
|
||||
void* context) {
|
||||
int64_t expiry_time = now_ + delay_ms;
|
||||
timers_.push(Timer(expiry_time, client, context));
|
||||
}
|
||||
60
cdm/test/test_host.h
Normal file
60
cdm/test/test_host.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#ifndef WVCDM_CDM_TEST_TEST_HOST_H_
|
||||
#define WVCDM_CDM_TEST_TEST_HOST_H_
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "cdm.h"
|
||||
#include "override.h"
|
||||
|
||||
class TestHost : public widevine::Cdm::IStorage,
|
||||
public widevine::Cdm::IClock,
|
||||
public widevine::Cdm::ITimer {
|
||||
public:
|
||||
TestHost();
|
||||
void Reset();
|
||||
|
||||
void ElapseTime(int64_t milliseconds);
|
||||
int NumTimers() const;
|
||||
|
||||
virtual bool read(const std::string& name,
|
||||
std::string* data) OVERRIDE;
|
||||
virtual bool write(const std::string& name,
|
||||
const std::string& data) OVERRIDE;
|
||||
virtual bool exists(const std::string& name) OVERRIDE;
|
||||
virtual bool remove(const std::string& name) OVERRIDE;
|
||||
virtual int32_t size(const std::string& name) OVERRIDE;
|
||||
|
||||
virtual int64_t now() OVERRIDE;
|
||||
|
||||
virtual void setTimeout(int64_t delay_ms,
|
||||
IClient* client,
|
||||
void* context) OVERRIDE;
|
||||
|
||||
private:
|
||||
struct Timer {
|
||||
Timer(int64_t expiry_time, IClient* client, void* context)
|
||||
: expiry_time(expiry_time), client(client), context(context) {}
|
||||
|
||||
bool operator<(const Timer& other) const {
|
||||
// We want to reverse the order so that the smallest expiry times go to
|
||||
// the top of the priority queue.
|
||||
return expiry_time > other.expiry_time;
|
||||
}
|
||||
|
||||
int64_t expiry_time;
|
||||
IClient* client;
|
||||
void* context;
|
||||
};
|
||||
|
||||
int64_t now_;
|
||||
std::priority_queue<Timer> timers_;
|
||||
|
||||
typedef std::map<std::string, std::string> StorageMap;
|
||||
StorageMap files_;
|
||||
};
|
||||
|
||||
// Owned and managed by the test runner.
|
||||
extern TestHost* g_host;
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_HOST_H_
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
// Review the TestHost_1 class below to observe how the CDM interfaces with
|
||||
// the host application.
|
||||
|
||||
#include "test_host_1.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
static double GetCurrentTime() {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + (tv.tv_usec / (1000.0 * 1000.0));
|
||||
}
|
||||
|
||||
TestHost_1::TestHost_1()
|
||||
: current_time_(GetCurrentTime()),
|
||||
has_new_key_message_(false),
|
||||
has_new_key_error_(false),
|
||||
cdm_(NULL) {
|
||||
}
|
||||
|
||||
TestHost_1::~TestHost_1() {
|
||||
if (cdm_)
|
||||
cdm_->Destroy();
|
||||
}
|
||||
|
||||
cdm::Buffer* TestHost_1::Allocate(int32_t capacity) {
|
||||
return TestBuffer::Create(capacity);
|
||||
}
|
||||
|
||||
void TestHost_1::SetTimer(int64_t delay_ms, void* context) {
|
||||
double expiry_time = current_time_ + (delay_ms / 1000.0);
|
||||
timers_.push(Timer(expiry_time, context));
|
||||
}
|
||||
|
||||
double TestHost_1::GetCurrentWallTimeInSeconds() {
|
||||
return current_time_;
|
||||
}
|
||||
|
||||
void TestHost_1::SendKeyMessage(const char* session_id,
|
||||
int32_t session_id_length, const char* message,
|
||||
int32_t message_length, const char* default_url,
|
||||
int32_t default_url_length) {
|
||||
KeyMessage key_message;
|
||||
key_message.session_id.assign(session_id, session_id_length);
|
||||
key_message.message.assign(message, message_length);
|
||||
key_message.default_url.assign(default_url, default_url_length);
|
||||
key_messages_.push_back(key_message);
|
||||
has_new_key_message_ = true;
|
||||
}
|
||||
|
||||
void TestHost_1::SendKeyError(const char* session_id, int32_t session_id_length,
|
||||
cdm::MediaKeyError error_code,
|
||||
uint32_t system_code) {
|
||||
KeyError key_error;
|
||||
key_error.session_id.assign(session_id, session_id_length);
|
||||
key_error.error_code = error_code;
|
||||
key_error.system_code = system_code;
|
||||
key_errors_.push_back(key_error);
|
||||
has_new_key_error_ = true;
|
||||
}
|
||||
|
||||
void TestHost_1::FastForwardTime(double seconds) {
|
||||
double goal_time = current_time_ + seconds;
|
||||
while (current_time_ < goal_time) {
|
||||
if (timers_.empty()) {
|
||||
current_time_ = goal_time;
|
||||
} else {
|
||||
Timer t = timers_.top();
|
||||
timers_.pop();
|
||||
ASSERT_GE(t.expiry_time, current_time_);
|
||||
current_time_ = t.expiry_time;
|
||||
cdm_->TimerExpired(t.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestHost_1::GetPlatformString(const std::string& name,
|
||||
std::string* value) {
|
||||
*value = platform_strings_[name];
|
||||
}
|
||||
|
||||
void TestHost_1::SetPlatformString(const std::string& name,
|
||||
const std::string& value) {
|
||||
platform_strings_[name] = value;
|
||||
}
|
||||
|
||||
int TestHost_1::KeyMessagesSize() const { return key_messages_.size(); }
|
||||
|
||||
int TestHost_1::KeyErrorsSize() const { return key_errors_.size(); }
|
||||
|
||||
int TestHost_1::NumTimers() const { return timers_.size(); }
|
||||
|
||||
TestHost_1::KeyMessage TestHost_1::GetLastKeyMessage() {
|
||||
if (!has_new_key_message_) {
|
||||
return KeyMessage();
|
||||
}
|
||||
|
||||
if (key_messages_.empty()) {
|
||||
return KeyMessage();
|
||||
}
|
||||
|
||||
has_new_key_message_ = false;
|
||||
return key_messages_.back();
|
||||
}
|
||||
|
||||
TestHost_1::KeyError TestHost_1::GetLastKeyError() {
|
||||
if (!has_new_key_error_) return KeyError();
|
||||
|
||||
if (key_errors_.empty()) return KeyError();
|
||||
|
||||
has_new_key_error_ = false;
|
||||
return key_errors_.back();
|
||||
}
|
||||
|
||||
TestHost_1::KeyMessage TestHost_1::GetKeyMessage(int index) const {
|
||||
return key_messages_[index];
|
||||
}
|
||||
|
||||
TestHost_1::KeyError TestHost_1::GetKeyError(int index) const {
|
||||
return key_errors_[index];
|
||||
}
|
||||
|
||||
void TestHost_1::SetCdmPtr(cdm::ContentDecryptionModule_1* cdm) {
|
||||
if (cdm_) {
|
||||
cdm_->Destroy();
|
||||
}
|
||||
cdm_ = cdm;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_TEST_HOST_1_H_
|
||||
#define WVCDM_CDM_TEST_TEST_HOST_1_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
class TestHost_1 : public cdm::Host_1 {
|
||||
public:
|
||||
// These structs are used to store the KeyMessages and KeyErrors passed to
|
||||
// this class' objects.
|
||||
struct KeyMessage {
|
||||
std::string session_id;
|
||||
std::string message;
|
||||
std::string default_url;
|
||||
};
|
||||
|
||||
struct KeyError {
|
||||
KeyError() : error_code(cdm::kUnknownError), system_code(0) {}
|
||||
std::string session_id;
|
||||
cdm::MediaKeyError error_code;
|
||||
uint32_t system_code;
|
||||
};
|
||||
|
||||
TestHost_1();
|
||||
virtual ~TestHost_1();
|
||||
|
||||
// cdm::Host implementation.
|
||||
virtual cdm::Buffer* Allocate(int32_t capacity) OVERRIDE;
|
||||
|
||||
virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE;
|
||||
|
||||
virtual double GetCurrentWallTimeInSeconds() OVERRIDE;
|
||||
|
||||
virtual void SendKeyMessage(const char* session_id, int32_t session_id_length,
|
||||
const char* message, int32_t message_length,
|
||||
const char* default_url,
|
||||
int32_t default_url_length) OVERRIDE;
|
||||
|
||||
virtual void SendKeyError(const char* session_id, int32_t session_id_length,
|
||||
cdm::MediaKeyError error_code,
|
||||
uint32_t system_code) OVERRIDE;
|
||||
|
||||
virtual void GetPlatformString(const std::string& name,
|
||||
std::string* value) OVERRIDE;
|
||||
|
||||
virtual void SetPlatformString(const std::string& name,
|
||||
const std::string& value) OVERRIDE;
|
||||
|
||||
// Methods only for this test.
|
||||
void FastForwardTime(double seconds);
|
||||
int KeyMessagesSize() const;
|
||||
int KeyErrorsSize() const;
|
||||
int NumTimers() const;
|
||||
|
||||
// Returns Key{Message,Error} (replace Message with Error for KeyError). It
|
||||
// returns the most recent message passed to SendKeyMessage(). Another call
|
||||
// to this method without a new SendKeyMessage() call will return an empty
|
||||
// KeyMessage struct.
|
||||
KeyMessage GetLastKeyMessage();
|
||||
KeyError GetLastKeyError();
|
||||
|
||||
KeyMessage GetKeyMessage(int index) const;
|
||||
KeyError GetKeyError(int index) const;
|
||||
|
||||
void SetCdmPtr(cdm::ContentDecryptionModule_1* cdm);
|
||||
|
||||
private:
|
||||
struct Timer {
|
||||
Timer(double expiry_time, void* context)
|
||||
: expiry_time(expiry_time), context(context) {}
|
||||
|
||||
bool operator<(const Timer& other) const {
|
||||
// We want to reverse the order so that the smallest expiry times go to
|
||||
// the top of the priority queue.
|
||||
return expiry_time > other.expiry_time;
|
||||
}
|
||||
|
||||
double expiry_time;
|
||||
void* context;
|
||||
};
|
||||
|
||||
double current_time_;
|
||||
std::priority_queue<Timer> timers_;
|
||||
|
||||
std::vector<KeyMessage> key_messages_;
|
||||
std::vector<KeyError> key_errors_;
|
||||
|
||||
bool has_new_key_message_;
|
||||
bool has_new_key_error_;
|
||||
|
||||
std::map<std::string, std::string> platform_strings_;
|
||||
|
||||
cdm::ContentDecryptionModule_1* cdm_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_1);
|
||||
};
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_HOST_1_H_
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
// Review the TestHost_4 class below to observe how the CDM interfaces with
|
||||
// the host application.
|
||||
|
||||
#include "test_host_4.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "test_host_4_file_io.h"
|
||||
#include "test_util.h"
|
||||
|
||||
static double GetCurrentTime() {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + (tv.tv_usec / (1000.0 * 1000.0));
|
||||
}
|
||||
|
||||
TestHost_4::TestHost_4()
|
||||
: current_time_(GetCurrentTime()),
|
||||
has_new_session_message_(false),
|
||||
has_new_session_error_(false),
|
||||
cdm_(NULL) {}
|
||||
|
||||
TestHost_4::~TestHost_4() {
|
||||
if (cdm_) cdm_->Destroy();
|
||||
}
|
||||
|
||||
cdm::Buffer* TestHost_4::Allocate(uint32_t capacity) {
|
||||
return TestBuffer::Create(capacity);
|
||||
}
|
||||
|
||||
void TestHost_4::SetTimer(int64_t delay_ms, void* context) {
|
||||
double expiry_time = current_time_ + (delay_ms / 1000.0);
|
||||
timers_.push(Timer(expiry_time, context));
|
||||
}
|
||||
|
||||
double TestHost_4::GetCurrentWallTimeInSeconds() { return current_time_; }
|
||||
|
||||
void TestHost_4::FastForwardTime(double seconds) {
|
||||
double goal_time = current_time_ + seconds;
|
||||
while (current_time_ < goal_time) {
|
||||
if (timers_.empty()) {
|
||||
current_time_ = goal_time;
|
||||
} else {
|
||||
Timer t = timers_.top();
|
||||
timers_.pop();
|
||||
ASSERT_GE(t.expiry_time, current_time_);
|
||||
current_time_ = t.expiry_time;
|
||||
cdm_->TimerExpired(t.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TestHost_4::SessionMessagesSize() const { return session_messages_.size(); }
|
||||
|
||||
int TestHost_4::SessionErrorsSize() const { return session_errors_.size(); }
|
||||
|
||||
int TestHost_4::NumTimers() const { return timers_.size(); }
|
||||
|
||||
TestHost_4::SessionMessage TestHost_4::GetLastSessionMessage() {
|
||||
if (!has_new_session_message_) {
|
||||
return SessionMessage();
|
||||
}
|
||||
|
||||
if (session_messages_.empty()) {
|
||||
return SessionMessage();
|
||||
}
|
||||
|
||||
has_new_session_message_ = false;
|
||||
return session_messages_.back();
|
||||
}
|
||||
|
||||
TestHost_4::SessionError TestHost_4::GetLastSessionError() {
|
||||
if (!has_new_session_error_) return SessionError();
|
||||
|
||||
if (session_errors_.empty()) return SessionError();
|
||||
|
||||
has_new_session_error_ = false;
|
||||
return session_errors_.back();
|
||||
}
|
||||
|
||||
TestHost_4::SessionMessage TestHost_4::GetSessionMessage(int index) const {
|
||||
return session_messages_[index];
|
||||
}
|
||||
|
||||
TestHost_4::SessionError TestHost_4::GetSessionError(int index) const {
|
||||
return session_errors_[index];
|
||||
}
|
||||
|
||||
void TestHost_4::SetCdmPtr(cdm::ContentDecryptionModule_4* cdm) {
|
||||
if (cdm_) {
|
||||
cdm_->Destroy();
|
||||
}
|
||||
cdm_ = cdm;
|
||||
}
|
||||
|
||||
void TestHost_4::OnSessionCreated(uint32_t session_id,
|
||||
const char* web_session_id,
|
||||
uint32_t web_session_id_length) {
|
||||
std::string webid(web_session_id, web_session_id_length);
|
||||
session_map[session_id] = webid; // keep a parallel map with cdm.
|
||||
}
|
||||
|
||||
void TestHost_4::OnSessionMessage(uint32_t session_id, const char* message,
|
||||
uint32_t message_length,
|
||||
const char* destination_url,
|
||||
uint32_t destination_url_length) {
|
||||
SessionMessage session_message;
|
||||
session_message.session_id = session_id;
|
||||
session_message.message.assign(message, message_length);
|
||||
session_message.default_url.assign(destination_url, destination_url_length);
|
||||
session_messages_.push_back(session_message);
|
||||
has_new_session_message_ = true;
|
||||
}
|
||||
|
||||
void TestHost_4::OnSessionUpdated(uint32_t session_id) {}
|
||||
|
||||
void TestHost_4::OnSessionClosed(uint32_t session_id) {
|
||||
session_map.erase(session_id);
|
||||
}
|
||||
|
||||
void TestHost_4::OnSessionError(uint32_t session_id, cdm::Status error_code,
|
||||
uint32_t system_code) {
|
||||
SessionError session_error;
|
||||
session_error.session_id = session_id;
|
||||
session_error.error_code = error_code;
|
||||
session_error.system_code = system_code;
|
||||
session_errors_.push_back(session_error);
|
||||
has_new_session_error_ = true;
|
||||
}
|
||||
|
||||
cdm::FileIO* TestHost_4::CreateFileIO(cdm::FileIOClient* client) {
|
||||
return new TestHost_4_FileIO(this, client);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_TEST_HOST_4_H_
|
||||
#define WVCDM_CDM_TEST_TEST_HOST_4_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include <queue>
|
||||
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
class TestHost_4 : public cdm::Host_4 {
|
||||
public:
|
||||
// These structs are used to store the SessionMessages and SessionErrors
|
||||
// passed to this class' objects.
|
||||
|
||||
struct SessionMessage {
|
||||
uint32_t session_id;
|
||||
std::string message;
|
||||
std::string default_url;
|
||||
};
|
||||
|
||||
struct SessionError {
|
||||
uint32_t session_id;
|
||||
cdm::Status error_code;
|
||||
uint32_t system_code;
|
||||
};
|
||||
|
||||
TestHost_4();
|
||||
virtual ~TestHost_4();
|
||||
|
||||
// cdm::Host implementation.
|
||||
virtual cdm::Buffer* Allocate(uint32_t capacity) OVERRIDE;
|
||||
|
||||
virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE;
|
||||
|
||||
virtual double GetCurrentWallTimeInSeconds() OVERRIDE;
|
||||
|
||||
virtual void OnSessionCreated(uint32_t session_id, const char* web_session_id,
|
||||
uint32_t web_session_id_length) OVERRIDE;
|
||||
|
||||
virtual void OnSessionMessage(uint32_t session_id, const char* message,
|
||||
uint32_t message_length,
|
||||
const char* destination_url,
|
||||
uint32_t destination_url_length) OVERRIDE;
|
||||
|
||||
virtual void OnSessionUpdated(uint32_t session_id) OVERRIDE;
|
||||
|
||||
virtual void OnSessionClosed(uint32_t session_id) OVERRIDE;
|
||||
|
||||
virtual void OnSessionError(uint32_t session_id, cdm::Status error_code,
|
||||
uint32_t system_code) OVERRIDE;
|
||||
|
||||
virtual cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) OVERRIDE;
|
||||
|
||||
// Methods only for this test.
|
||||
void FastForwardTime(double seconds);
|
||||
int SessionMessagesSize() const;
|
||||
int SessionErrorsSize() const;
|
||||
int NumTimers() const;
|
||||
|
||||
// Returns Key{Message,Error} (replace Message with Error for SessionError).
|
||||
// It returns the most recent message passed to SendSessionMessage(). Another
|
||||
// call to this method without a new SendSessionMessage() call will return an
|
||||
// empty SessionMessage struct.
|
||||
SessionMessage GetLastSessionMessage();
|
||||
SessionError GetLastSessionError();
|
||||
|
||||
SessionMessage GetSessionMessage(int index) const;
|
||||
SessionError GetSessionError(int index) const;
|
||||
|
||||
void SetCdmPtr(cdm::ContentDecryptionModule_4* cdm);
|
||||
std::map<uint32_t, std::string> session_map;
|
||||
|
||||
// Accessed by all FileIO objects.
|
||||
std::map<std::string, std::string> file_store;
|
||||
|
||||
private:
|
||||
struct Timer {
|
||||
Timer(double expiry_time, void* context)
|
||||
: expiry_time(expiry_time), context(context) {}
|
||||
|
||||
bool operator<(const Timer& other) const {
|
||||
// We want to reverse the order so that the smallest expiry times go to
|
||||
// the top of the priority queue.
|
||||
return expiry_time > other.expiry_time;
|
||||
}
|
||||
|
||||
double expiry_time;
|
||||
void* context;
|
||||
};
|
||||
|
||||
double current_time_;
|
||||
std::priority_queue<Timer> timers_;
|
||||
|
||||
std::vector<SessionMessage> session_messages_;
|
||||
std::vector<SessionError> session_errors_;
|
||||
|
||||
bool has_new_session_message_;
|
||||
bool has_new_session_error_;
|
||||
|
||||
cdm::ContentDecryptionModule_4* cdm_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_4);
|
||||
};
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_HOST_4_H_
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
// Review the TestHost_4 class below to observe how the CDM interfaces with
|
||||
// the host application.
|
||||
|
||||
#include "test_host_4_file_io.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
void TestHost_4_FileIO::Open(const char* file_name, uint32_t file_name_size) {
|
||||
ASSERT_EQ(0, file_name_.size());
|
||||
file_name_.assign(file_name, file_name_size);
|
||||
client_->OnOpenComplete(cdm::FileIOClient::kSuccess);
|
||||
}
|
||||
|
||||
void TestHost_4_FileIO::Read() {
|
||||
ASSERT_NE(0, file_name_.size());
|
||||
const std::string& data = host_->file_store[file_name_];
|
||||
client_->OnReadComplete(cdm::FileIOClient::kSuccess,
|
||||
reinterpret_cast<const uint8_t*>(data.data()),
|
||||
data.size());
|
||||
}
|
||||
|
||||
void TestHost_4_FileIO::Write(const uint8_t* data, uint32_t data_size) {
|
||||
ASSERT_NE(0, file_name_.size());
|
||||
host_->file_store[file_name_].assign(reinterpret_cast<const char*>(data),
|
||||
data_size);
|
||||
client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
|
||||
}
|
||||
|
||||
void TestHost_4_FileIO::Close() { delete this; }
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_
|
||||
#define WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "test_host_4.h"
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
class TestHost_4_FileIO : public cdm::FileIO {
|
||||
public:
|
||||
TestHost_4_FileIO(TestHost_4* host, cdm::FileIOClient* client)
|
||||
: host_(host), client_(client) {}
|
||||
virtual ~TestHost_4_FileIO() {}
|
||||
|
||||
// cdm::FileIO implementation.
|
||||
virtual void Open(const char* file_name, uint32_t file_name_size) OVERRIDE;
|
||||
virtual void Read() OVERRIDE;
|
||||
virtual void Write(const uint8_t* data, uint32_t data_size) OVERRIDE;
|
||||
virtual void Close() OVERRIDE;
|
||||
|
||||
private:
|
||||
TestHost_4* host_;
|
||||
cdm::FileIOClient* client_;
|
||||
|
||||
std::string file_name_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_4_FileIO);
|
||||
};
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
TestBuffer* TestBuffer::Create(uint32_t capacity) {
|
||||
return new TestBuffer(capacity);
|
||||
}
|
||||
|
||||
void TestBuffer::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
int32_t TestBuffer::Capacity() const { return capacity_; }
|
||||
|
||||
uint8_t* TestBuffer::Data() { return buffer_; }
|
||||
|
||||
void TestBuffer::SetSize(int32_t size) { size_ = size; }
|
||||
|
||||
int32_t TestBuffer::Size() const { return size_; }
|
||||
|
||||
TestBuffer::TestBuffer(uint32_t capacity)
|
||||
: buffer_(new uint8_t[capacity]),
|
||||
capacity_(capacity) {}
|
||||
|
||||
TestBuffer::~TestBuffer() {
|
||||
if (buffer_) {
|
||||
delete[] buffer_;
|
||||
buffer_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
TestDecryptedBlock::TestDecryptedBlock() : buffer_(NULL), timestamp_(0) {}
|
||||
|
||||
TestDecryptedBlock::~TestDecryptedBlock() {
|
||||
if (buffer_) {
|
||||
buffer_->Destroy();
|
||||
buffer_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TestDecryptedBlock::SetDecryptedBuffer(cdm::Buffer* buffer) {
|
||||
if (buffer_) buffer_->Destroy();
|
||||
buffer_ = buffer;
|
||||
}
|
||||
|
||||
cdm::Buffer* TestDecryptedBlock::DecryptedBuffer() { return buffer_; }
|
||||
|
||||
void TestDecryptedBlock::SetTimestamp(int64_t timestamp) {
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
int64_t TestDecryptedBlock::Timestamp() const { return timestamp_; }
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CDM_TEST_TEST_UTIL_H_
|
||||
#define WVCDM_CDM_TEST_TEST_UTIL_H_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
|
||||
#include "wv_cdm_common.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
// These classes below are naive implementation of the abstract classes defined
|
||||
// in the CDM interface (content_decryptiom_module.h), which are used for tests
|
||||
// only.
|
||||
|
||||
class TestBuffer : public cdm::Buffer {
|
||||
public:
|
||||
static TestBuffer* Create(uint32_t capacity);
|
||||
|
||||
virtual void Destroy() OVERRIDE;
|
||||
|
||||
virtual int32_t Capacity() const OVERRIDE;
|
||||
virtual uint8_t* Data() OVERRIDE;
|
||||
virtual void SetSize(int32_t size) OVERRIDE;
|
||||
virtual int32_t Size() const OVERRIDE;
|
||||
|
||||
private:
|
||||
// TestBuffer can only be created by calling Create().
|
||||
explicit TestBuffer(uint32_t capacity);
|
||||
|
||||
// TestBuffer can only be destroyed by calling Destroy().
|
||||
virtual ~TestBuffer();
|
||||
|
||||
uint8_t* buffer_;
|
||||
int32_t capacity_;
|
||||
int32_t size_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(TestBuffer);
|
||||
};
|
||||
|
||||
class TestDecryptedBlock : public cdm::DecryptedBlock {
|
||||
public:
|
||||
TestDecryptedBlock();
|
||||
virtual ~TestDecryptedBlock();
|
||||
|
||||
virtual void SetDecryptedBuffer(cdm::Buffer* buffer) OVERRIDE;
|
||||
virtual cdm::Buffer* DecryptedBuffer() OVERRIDE;
|
||||
|
||||
virtual void SetTimestamp(int64_t timestamp) OVERRIDE;
|
||||
virtual int64_t Timestamp() const OVERRIDE;
|
||||
|
||||
private:
|
||||
cdm::Buffer* buffer_;
|
||||
int64_t timestamp_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(TestDecryptedBlock);
|
||||
};
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_UTIL_H_
|
||||
@@ -3,9 +3,9 @@
|
||||
#ifndef WVCDM_CORE_CDM_CLIENT_PROPERTY_SET_H_
|
||||
#define WVCDM_CORE_CDM_CLIENT_PROPERTY_SET_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -16,9 +16,11 @@ class CdmClientPropertySet {
|
||||
virtual const std::string& security_level() const = 0;
|
||||
virtual bool use_privacy_mode() const = 0;
|
||||
virtual const std::string& service_certificate() const = 0;
|
||||
virtual void set_service_certificate(const std::string& cert) = 0;
|
||||
virtual bool is_session_sharing_enabled() const = 0;
|
||||
virtual uint32_t session_sharing_id() const = 0;
|
||||
virtual void set_session_sharing_id(uint32_t id) = 0;
|
||||
virtual const std::string& app_id() const = 0;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
#ifndef WVCDM_CORE_CDM_ENGINE_H_
|
||||
#define WVCDM_CORE_CDM_ENGINE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "crypto_session.h"
|
||||
#include "initialization_data.h"
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
@@ -14,6 +18,7 @@ namespace wvcdm {
|
||||
class CdmClientPropertySet;
|
||||
class CdmSession;
|
||||
class CryptoEngine;
|
||||
class UsagePropertySet;
|
||||
class WvCdmEventListener;
|
||||
|
||||
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
||||
@@ -26,11 +31,17 @@ class CdmEngine {
|
||||
|
||||
// Session related methods
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id);
|
||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
virtual bool IsOpenSession(const CdmSessionId& session_id);
|
||||
|
||||
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
||||
virtual CdmResponseType OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
@@ -49,6 +60,9 @@ class CdmEngine {
|
||||
// and renewal requests.
|
||||
// key_request: This must be non-null and point to a CdmKeyMessage. The buffer
|
||||
// will have its contents replaced with the key request.
|
||||
// key_request_type: May be null. If it is non-null, it will be filled with
|
||||
// key request type, whether it is an initial request,
|
||||
// renewal request or release request etc.
|
||||
// server_url: This must be non-null and point to a string. The string will
|
||||
// have its contents replaced with the default URL (if one is
|
||||
// known) to send this key request to.
|
||||
@@ -56,11 +70,14 @@ class CdmEngine {
|
||||
// will have its contents replaced with the key set ID of the
|
||||
// session. Note that for non-offline license requests, the
|
||||
// key set ID is empty, so the CdmKeySetId will be cleared.
|
||||
// TODO(kqyang): Consider refactor GenerateKeyRequest to reduce the number of
|
||||
// parameters.
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out);
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
@@ -82,33 +99,50 @@ class CdmEngine {
|
||||
const CdmKeyResponse& key_data);
|
||||
|
||||
// Query system information
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* info);
|
||||
virtual CdmResponseType QueryStatus(SecurityLevel security_level,
|
||||
CdmQueryMap* info);
|
||||
|
||||
// Query session information
|
||||
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
virtual bool IsReleaseSession(const CdmSessionId& session_id);
|
||||
virtual bool IsOfflineSession(const CdmSessionId& session_id);
|
||||
|
||||
// Query license information
|
||||
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Query seesion control information
|
||||
// Query session control information
|
||||
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Provisioning related methods
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url);
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
|
||||
virtual CdmResponseType HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
||||
virtual bool IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
// id.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
CdmUsageInfo* usage_info);
|
||||
// Retrieve the usage info for the specified pst.
|
||||
// Returns UNKNOWN_ERROR if no usage info was found.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info);
|
||||
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id);
|
||||
virtual CdmResponseType ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message);
|
||||
|
||||
@@ -123,32 +157,50 @@ class CdmEngine {
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||
|
||||
// Event listener related methods
|
||||
virtual bool AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
virtual bool DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
// Used for notifying the Max-Res Engine of resolution changes
|
||||
virtual void NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
// Timer expiration method
|
||||
// Timer expiration method. This method is not re-entrant -- there can be
|
||||
// only one timer.
|
||||
// This method triggers appropriate event callbacks from |event_listener_|,
|
||||
// which is assumed to be asynchronous -- i.e. an event should be dispatched
|
||||
// to another thread which does the actual work. In particular, if a
|
||||
// synchronous listener calls OpenSession or CloseSession, the thread will
|
||||
// dead lock.
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
private:
|
||||
// private methods
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
SecurityLevel requested_security_level,
|
||||
CdmUsageInfo* usage_info);
|
||||
|
||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
|
||||
|
||||
// instance variables
|
||||
CdmSessionMap sessions_;
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
CertificateProvisioning cert_provisioning_;
|
||||
scoped_ptr<CertificateProvisioning> cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
// usage related variables
|
||||
scoped_ptr<CdmSession> usage_session_;
|
||||
int64_t last_usage_information_update_time;
|
||||
scoped_ptr<UsagePropertySet> usage_property_set_;
|
||||
int64_t last_usage_information_update_time_;
|
||||
|
||||
// Locks the list of sessions, |sessions_|, for the event timer. It will be
|
||||
// locked in OpenSession, CloseSession. It is also locked in OnTimerEvent and
|
||||
// OnKeyReleaseEvent while the list of event listeners is being generated.
|
||||
// The layer above the CDM implementation is expected to handle thread
|
||||
// synchronization to make sure other functions that access sessions do not
|
||||
// occur simultaneously with OpenSession or CloseSession.
|
||||
Lock session_list_lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define WVCDM_CORE_CDM_SESSION_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
@@ -21,7 +22,9 @@ class WvCdmEventListener;
|
||||
|
||||
class CdmSession {
|
||||
public:
|
||||
explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set);
|
||||
CdmSession(CdmClientPropertySet* cdm_client_property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id);
|
||||
virtual ~CdmSession();
|
||||
|
||||
virtual CdmResponseType Init();
|
||||
@@ -34,9 +37,10 @@ class CdmSession {
|
||||
virtual const CdmSessionId& session_id() { return session_id_; }
|
||||
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id);
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
@@ -74,24 +78,33 @@ class CdmSession {
|
||||
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
virtual bool AttachEventListener(WvCdmEventListener* listener);
|
||||
virtual bool DetachEventListener(WvCdmEventListener* listener);
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
|
||||
virtual void OnTimerEvent();
|
||||
virtual void OnTimerEvent(bool update_usage);
|
||||
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
virtual void GetApplicationId(std::string* app_id);
|
||||
virtual SecurityLevel GetRequestedSecurityLevel() {
|
||||
return requested_security_level_;
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; }
|
||||
|
||||
// Delete usage information for the list of tokens, |provider_session_tokens|.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
|
||||
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
|
||||
virtual bool is_usage_update_needed() { return is_usage_update_needed_; }
|
||||
virtual void reset_is_usage_update_needed() {
|
||||
virtual void reset_usage_flags() {
|
||||
is_initial_usage_update_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
}
|
||||
|
||||
virtual bool is_release() { return is_release_; }
|
||||
virtual bool is_offline() { return is_offline_; }
|
||||
|
||||
// ReleaseCrypto() - Closes the underlying crypto session but leaves this
|
||||
// object alive. It is invalid to call any method that requires a crypto
|
||||
// session after calling this. Since calling this renders this object mostly
|
||||
@@ -99,11 +112,10 @@ class CdmSession {
|
||||
// release the underlying crypto session) rather than call this method.
|
||||
virtual CdmResponseType ReleaseCrypto();
|
||||
|
||||
bool DeleteLicense();
|
||||
|
||||
private:
|
||||
// Internal constructor
|
||||
void Create(CdmLicense* license_parser, CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set);
|
||||
friend class CdmSessionTest;
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
@@ -111,11 +123,17 @@ class CdmSession {
|
||||
|
||||
CdmResponseType StoreLicense();
|
||||
bool StoreLicense(DeviceFiles::LicenseState state);
|
||||
bool DeleteLicense();
|
||||
|
||||
// These setters are for testing only. Takes ownership of the pointers.
|
||||
void set_license_parser(CdmLicense* license_parser);
|
||||
void set_crypto_session(CryptoSession* crypto_session);
|
||||
void set_policy_engine(PolicyEngine* policy_engine);
|
||||
void set_file_handle(DeviceFiles* file_handle);
|
||||
|
||||
// instance variables
|
||||
bool initialized_;
|
||||
CdmSessionId session_id_;
|
||||
const std::string origin_;
|
||||
scoped_ptr<CdmLicense> license_parser_;
|
||||
scoped_ptr<CryptoSession> crypto_session_;
|
||||
scoped_ptr<PolicyEngine> policy_engine_;
|
||||
@@ -123,11 +141,15 @@ class CdmSession {
|
||||
bool license_received_;
|
||||
bool is_offline_;
|
||||
bool is_release_;
|
||||
bool is_usage_update_needed_;
|
||||
bool is_initial_decryption_;
|
||||
|
||||
CdmSecurityLevel security_level_;
|
||||
SecurityLevel requested_security_level_;
|
||||
CdmAppParameterMap app_parameters_;
|
||||
|
||||
// decryption and usage flags
|
||||
bool is_initial_decryption_;
|
||||
bool has_decrypted_since_last_report_; // ... last report to policy engine.
|
||||
bool is_initial_usage_update_;
|
||||
bool is_usage_update_needed_;
|
||||
|
||||
// information useful for offline and usage scenarios
|
||||
CdmKeyMessage key_request_;
|
||||
@@ -142,18 +164,6 @@ class CdmSession {
|
||||
// license type release and offline related information
|
||||
CdmKeySetId key_set_id_;
|
||||
|
||||
std::set<WvCdmEventListener*> listeners_;
|
||||
|
||||
// For testing only
|
||||
// Takes ownership of license_parser, crypto_session, policy_engine
|
||||
// and device_files
|
||||
CdmSession(CdmLicense* license_parser, CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set);
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmSessionTest;
|
||||
#endif
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#ifndef WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
#define WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "wv_cdm_types.h"
|
||||
@@ -20,11 +22,14 @@ class CertificateProvisioning {
|
||||
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
const std::string& origin,
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
CdmResponseType HandleProvisioningResponse(
|
||||
const std::string& origin,
|
||||
const CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
private:
|
||||
void ComposeJsonRequestAsQueryString(const std::string& message,
|
||||
|
||||
@@ -19,6 +19,6 @@ class Clock {
|
||||
virtual int64_t GetCurrentTime();
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CLOCK_H_
|
||||
|
||||
@@ -35,6 +35,6 @@ class CryptoKey {
|
||||
std::string key_control_iv_;
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CRYPTO_KEY_H_
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#ifndef WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
#define WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -18,17 +18,13 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
|
||||
|
||||
class CryptoSession {
|
||||
public:
|
||||
// This enum should be kept in sync with the values specified for
|
||||
// HDCP capabilities in OEMCryptoCENC.h. (See comments for
|
||||
// OEMCrypto_GetHDCPCapability)
|
||||
typedef OEMCrypto_HDCP_Capability HdcpCapability;
|
||||
typedef enum {
|
||||
kOemCryptoHdcpNotSupported = 0,
|
||||
kOemCryptoHdcpVersion1 = 1,
|
||||
kOemCryptoHdcpVersion2 = 2,
|
||||
kOemCryptoHdcpVersion2_1 = 3,
|
||||
kOemCryptoHdcpVersion2_2 = 4,
|
||||
kOemCryptoNoHdcpDeviceAttached = 0xff,
|
||||
} OemCryptoHdcpVersion;
|
||||
kUsageDurationsInvalid = 0,
|
||||
kUsageDurationPlaybackNotBegun = 1,
|
||||
kUsageDurationsValid = 2,
|
||||
} UsageDurationStatus;
|
||||
|
||||
CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
@@ -77,17 +73,34 @@ class CryptoSession {
|
||||
// Media data path
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
|
||||
// Usage related methods
|
||||
virtual bool UsageInformationSupport(bool* has_support);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
virtual CdmResponseType DeactivateUsageInformation(
|
||||
const std::string& provider_session_token);
|
||||
virtual CdmResponseType GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report);
|
||||
const std::string& provider_session_token, std::string* usage_report,
|
||||
UsageDurationStatus* usage_duration_status,
|
||||
int64_t* seconds_since_started, int64_t* seconds_since_last_played);
|
||||
virtual CdmResponseType ReleaseUsageInformation(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& provider_session_token);
|
||||
// Delete a usage information for a single token. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteUsageInformation(
|
||||
const std::string& provider_session_token);
|
||||
// Delete usage information for a list of tokens. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType DeleteAllUsageReports();
|
||||
virtual bool IsAntiRollbackHwPresent();
|
||||
|
||||
virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current,
|
||||
OemCryptoHdcpVersion* max);
|
||||
virtual bool GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max);
|
||||
virtual bool GetRandom(size_t data_length, uint8_t* random_data);
|
||||
virtual bool GetNumberOfOpenSessions(size_t* count);
|
||||
virtual bool GetMaxNumberOfSessions(size_t* max);
|
||||
|
||||
private:
|
||||
void Init();
|
||||
@@ -109,6 +122,7 @@ class CryptoSession {
|
||||
static int session_count_;
|
||||
|
||||
bool open_;
|
||||
bool update_usage_table_after_close_session_;
|
||||
CryptoSessionId oec_session_id_;
|
||||
|
||||
OEMCryptoBufferType destination_buffer_type_;
|
||||
@@ -123,6 +137,6 @@ class CryptoSession {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#ifndef WVCDM_CORE_DEVICE_FILES_H_
|
||||
#define WVCDM_CORE_DEVICE_FILES_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
@@ -30,10 +35,14 @@ class DeviceFiles {
|
||||
return Init(security_level);
|
||||
}
|
||||
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
virtual bool StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
const std::string& wrapped_private_key);
|
||||
virtual bool RetrieveCertificate(std::string* certificate,
|
||||
virtual bool RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
std::string* wrapped_private_key);
|
||||
virtual bool HasCertificate(const std::string& origin);
|
||||
virtual bool RemoveCertificate(const std::string& origin);
|
||||
|
||||
virtual bool StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
@@ -42,14 +51,17 @@ class DeviceFiles {
|
||||
const CdmKeyResponse& key_response,
|
||||
const CdmKeyMessage& key_renewal_request,
|
||||
const CdmKeyResponse& key_renewal_response,
|
||||
const std::string& release_server_url);
|
||||
virtual bool RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response,
|
||||
CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response,
|
||||
std::string* release_server_url);
|
||||
const std::string& release_server_url,
|
||||
int64_t playback_start_time,
|
||||
int64_t last_playback_time,
|
||||
const CdmAppParameterMap& app_parameters);
|
||||
virtual bool RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state,
|
||||
CdmInitData* pssh_data, CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response, CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response, std::string* release_server_url,
|
||||
int64_t* playback_start_time, int64_t* last_playback_time,
|
||||
CdmAppParameterMap* app_parameters);
|
||||
virtual bool DeleteLicense(const std::string& key_set_id);
|
||||
virtual bool DeleteAllFiles();
|
||||
virtual bool DeleteAllLicenses();
|
||||
@@ -58,47 +70,78 @@ class DeviceFiles {
|
||||
|
||||
virtual bool StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response);
|
||||
virtual bool DeleteUsageInfo(const std::string& provider_session_token);
|
||||
virtual bool DeleteUsageInfo();
|
||||
const CdmKeyResponse& key_response,
|
||||
const std::string& app_id);
|
||||
virtual bool DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token);
|
||||
// Delete usage information from the file system. Puts a list of all the
|
||||
// psts that were deleted from the file into |provider_session_tokens|.
|
||||
virtual bool DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens);
|
||||
// Retrieve one usage info from the file. Subsequent calls will retrieve
|
||||
// subsequent entries in the table for this app_id.
|
||||
virtual bool RetrieveUsageInfo(
|
||||
const std::string& app_id,
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
|
||||
// Retrieve the usage info entry specified by |provider_session_token|.
|
||||
// Returns false if the entry could not be found.
|
||||
virtual bool RetrieveUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_response);
|
||||
|
||||
private:
|
||||
bool StoreFileWithHash(const char* name, const std::string& serialized_file);
|
||||
bool StoreFileRaw(const char* name, const std::string& serialized_file);
|
||||
bool RetrieveHashedFile(const char* name, std::string* serialized_file);
|
||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||
// well as adding the device files base path to to the file name.
|
||||
bool StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file);
|
||||
bool StoreFileRaw(const std::string& name,
|
||||
const std::string& serialized_file);
|
||||
bool RetrieveHashedFile(const std::string& name,
|
||||
std::string* serialized_file);
|
||||
bool FileExists(const std::string& name);
|
||||
bool RemoveFile(const std::string& name);
|
||||
ssize_t GetFileSize(const std::string& name);
|
||||
|
||||
// Certificate and offline licenses are now stored in security
|
||||
// level specific directories. In an earlier version they were
|
||||
// stored in a common directory and need to be copied over.
|
||||
virtual void SecurityLevelPathBackwardCompatibility();
|
||||
|
||||
// For testing only:
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetCertificateFileName(const std::string& origin);
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName();
|
||||
static std::string GetBlankFileData();
|
||||
static std::string GetUsageInfoFileName(const std::string& app_id);
|
||||
static std::string GetFileNameSafeHash(const std::string& input);
|
||||
|
||||
// For testing only:
|
||||
void SetTestFile(File* file);
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIds);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, AppParametersBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, DeleteAll);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Read);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Store);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
|
||||
FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest);
|
||||
FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo);
|
||||
FRIEND_TEST(WvCdmExtendedDurationTest, UsageOverflowTest);
|
||||
#endif
|
||||
|
||||
static std::set<std::string> reserved_license_ids_;
|
||||
|
||||
scoped_ptr<File> file_;
|
||||
CdmSecurityLevel security_level_;
|
||||
bool initialized_;
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#ifndef WVCDM_CORE_FILE_STORE_H_
|
||||
#define WVCDM_CORE_FILE_STORE_H_
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ class InitializationData {
|
||||
CdmInitData data_;
|
||||
bool is_cenc_;
|
||||
bool is_webm_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(InitializationData);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace video_widevine_server {
|
||||
namespace sdk {
|
||||
class SignedMessage;
|
||||
class LicenseRequest;
|
||||
}
|
||||
} // namespace video_widevine_server
|
||||
|
||||
@@ -23,21 +24,19 @@ class PolicyEngine;
|
||||
|
||||
class CdmLicense {
|
||||
public:
|
||||
CdmLicense();
|
||||
CdmLicense(const CdmSessionId& session_id);
|
||||
virtual ~CdmLicense();
|
||||
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
|
||||
virtual bool PrepareKeyRequest(const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual bool PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyUpdateRequest(
|
||||
bool is_renewal, const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request, std::string* server_url);
|
||||
virtual CdmResponseType HandleKeyResponse(
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
@@ -46,38 +45,50 @@ class CdmLicense {
|
||||
virtual bool RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response);
|
||||
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual bool HasInitData() { return !stored_init_data_.empty(); }
|
||||
const CdmKeyResponse& license_renewal_response,
|
||||
int64_t playback_start_time, int64_t last_playback_time);
|
||||
virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual bool HasInitData() { return stored_init_data_.get(); }
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
virtual std::string provider_session_token() {
|
||||
return provider_session_token_;
|
||||
}
|
||||
|
||||
static CdmResponseType VerifySignedServiceCertificate(
|
||||
const std::string& signed_service_certificate);
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
CdmResponseType HandleServiceCertificateResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
CdmResponseType PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
video_widevine_server::sdk::LicenseRequest* license_request);
|
||||
template <typename T>
|
||||
bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
|
||||
static CdmResponseType VerifyAndExtractSignedServiceCertificate(
|
||||
const std::string& signed_service_certificate,
|
||||
std::string* service_certificate);
|
||||
bool GetServiceCertificate(std::string* service_certificate);
|
||||
|
||||
CryptoSession* session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
std::string server_url_;
|
||||
std::string token_;
|
||||
std::string service_certificate_;
|
||||
std::string stored_init_data_;
|
||||
const CdmSessionId session_id_;
|
||||
scoped_ptr<InitializationData> stored_init_data_;
|
||||
bool initialized_;
|
||||
std::set<KeyId> loaded_keys_;
|
||||
std::string provider_session_token_;
|
||||
bool renew_with_client_id_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
@@ -85,7 +96,8 @@ class CdmLicense {
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
// For testing
|
||||
CdmLicense(Clock* clock); // CdmLicense takes ownership of the clock.
|
||||
// CdmLicense takes ownership of the clock.
|
||||
CdmLicense(const CdmSessionId& session_id, Clock* clock);
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmLicenseTest;
|
||||
#endif
|
||||
|
||||
@@ -21,10 +21,6 @@ class Lock {
|
||||
void Acquire();
|
||||
void Release();
|
||||
|
||||
// Acquires a lock if not held and returns true.
|
||||
// Returns false if the lock is held by another thread.
|
||||
bool Try();
|
||||
|
||||
friend class AutoLock;
|
||||
|
||||
private:
|
||||
@@ -50,6 +46,6 @@ class AutoLock {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_LOCK_H_
|
||||
|
||||
@@ -23,7 +23,7 @@ extern LogPriority g_cutoff;
|
||||
// This function is supplied for cases where the system layer does not
|
||||
// initialize logging. This is also needed to initialize logging in
|
||||
// unit tests.
|
||||
void InitLogging(int argc, const char* const* argv);
|
||||
void InitLogging();
|
||||
|
||||
void Log(const char* file, int line, LogPriority level, const char* fmt, ...);
|
||||
|
||||
@@ -34,6 +34,6 @@ void Log(const char* file, int line, LogPriority level, const char* fmt, ...);
|
||||
#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__)
|
||||
#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__)
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_LOG_H_
|
||||
|
||||
105
core/include/max_res_engine.h
Normal file
105
core/include/max_res_engine.h
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
#define WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "lock.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Clock;
|
||||
class MaxResEngineTest;
|
||||
|
||||
// Similar to the Policy Engine, this acts as an oracle that basically says
|
||||
// "Yes(true) you may still decrypt or no(false) you may not decrypt this data
|
||||
// anymore."
|
||||
class MaxResEngine {
|
||||
public:
|
||||
explicit MaxResEngine(CryptoSession* crypto_session);
|
||||
virtual ~MaxResEngine();
|
||||
|
||||
// The value returned is computed during the last call to SetLicense/
|
||||
// SetResolution/OnTimerEvent and may be out of sync depending on the amount
|
||||
// of time elapsed. The current decryption status is not calculated when this
|
||||
// function is called to avoid overhead in the decryption path.
|
||||
virtual bool CanDecrypt(const KeyId& key_id);
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the key constraints from the license.
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// SetResolution is called when the current output resolution is updated by
|
||||
// the decoder. The max-res engine will recalculate the current resolution
|
||||
// constraints, (if any) which may affect the results for CanDecrypt().
|
||||
virtual void SetResolution(uint32_t width, uint32_t height);
|
||||
|
||||
// OnTimerEvent is called when a timer fires. The max-res engine may check the
|
||||
// current HDCP level using the crypto session, which may affect the results
|
||||
// for CanDecrypt().
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
private:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
|
||||
OutputProtection;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::
|
||||
VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
class KeyStatus {
|
||||
public:
|
||||
explicit KeyStatus(const ConstraintList& constraints);
|
||||
KeyStatus(const ConstraintList& constraints,
|
||||
const OutputProtection::HDCP& default_hdcp_level);
|
||||
|
||||
bool can_decrypt() const { return can_decrypt_; }
|
||||
|
||||
void Update(uint32_t res,
|
||||
CryptoSession::HdcpCapability current_hdcp_level);
|
||||
|
||||
private:
|
||||
void Init(const ConstraintList& constraints);
|
||||
|
||||
VideoResolutionConstraint* GetConstraintForRes(uint32_t res);
|
||||
|
||||
static CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
|
||||
const OutputProtection::HDCP& input);
|
||||
|
||||
bool can_decrypt_;
|
||||
|
||||
CryptoSession::HdcpCapability default_hdcp_level_;
|
||||
ConstraintList constraints_;
|
||||
};
|
||||
|
||||
typedef std::map<wvcdm::KeyId,
|
||||
wvcdm::MaxResEngine::KeyStatus*>::const_iterator KeyIterator;
|
||||
|
||||
void Init(CryptoSession* crypto_session, Clock* clock);
|
||||
|
||||
void DeleteAllKeys();
|
||||
|
||||
Lock status_lock_;
|
||||
std::map<KeyId, KeyStatus*> keys_;
|
||||
uint32_t current_resolution_;
|
||||
int64_t next_check_time_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
CryptoSession* crypto_session_;
|
||||
|
||||
// For testing
|
||||
friend class MaxResEngineTest;
|
||||
MaxResEngine(CryptoSession* crypto_session, Clock* clock);
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(MaxResEngine);
|
||||
};
|
||||
|
||||
} // wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
@@ -4,26 +4,36 @@
|
||||
#define WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
enum SecurityLevel { kLevelDefault, kLevel3 };
|
||||
|
||||
/* This attempts to open a session at the desired security level.
|
||||
If one level is not available, the other will be used instead. */
|
||||
// This attempts to open a session at the desired security level.
|
||||
// If one level is not available, the other will be used instead.
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags);
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level);
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level);
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(SecurityLevel level,
|
||||
OEMCrypto_HDCP_Capability* current,
|
||||
OEMCrypto_HDCP_Capability* maximum);
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level);
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
size_t* count);
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum);
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
#ifndef WVCDM_CORE_POLICY_ENGINE_H_
|
||||
#define WVCDM_CORE_POLICY_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "license_protocol.pb.h"
|
||||
#include "max_res_engine.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -13,13 +16,15 @@ namespace wvcdm {
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
|
||||
class Clock;
|
||||
class PolicyEngineTest;
|
||||
class CryptoSession;
|
||||
class WvCdmEventListener;
|
||||
|
||||
// This acts as an oracle that basically says "Yes(true) you may still decrypt
|
||||
// or no(false) you may not decrypt this data anymore."
|
||||
class PolicyEngine {
|
||||
public:
|
||||
PolicyEngine();
|
||||
PolicyEngine(CdmSessionId session_id, WvCdmEventListener* event_listener,
|
||||
CryptoSession* crypto_session);
|
||||
virtual ~PolicyEngine();
|
||||
|
||||
// The value returned should be taken as a hint rather than an absolute
|
||||
@@ -27,14 +32,12 @@ class PolicyEngine {
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
virtual bool can_decrypt() { return can_decrypt_; }
|
||||
virtual bool CanDecrypt(const KeyId& key_id);
|
||||
|
||||
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
|
||||
// that the timer has fired and that it should check whether any events have
|
||||
// occurred since the last timer event. If so, it sets event_occurred to true
|
||||
// and sets event to point to the event that occurred. If not, it sets
|
||||
// event_occurred to false.
|
||||
virtual void OnTimerEvent(bool* event_occurred, CdmEventType* event);
|
||||
// that the timer has fired and dispatches the relevant events through
|
||||
// |event_listener_|.
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the policy information stored in the license.
|
||||
@@ -42,8 +45,14 @@ class PolicyEngine {
|
||||
// permits playback.
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// SetLicenseForRelease is used when releasing a license. The keys in this
|
||||
// license will be ignored, and any old keys will be expired.
|
||||
virtual void SetLicenseForRelease(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
virtual void DecryptionEvent(void);
|
||||
|
||||
// UpdateLicense is used in handling a license response for a renewal request.
|
||||
// The response may only contain any policy fields that have changed. In this
|
||||
@@ -53,11 +62,32 @@ class PolicyEngine {
|
||||
virtual void UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
|
||||
virtual void NotifySessionExpiration();
|
||||
|
||||
virtual CdmResponseType Query(CdmQueryMap* key_info);
|
||||
|
||||
virtual const LicenseIdentification& license_id() { return license_id_; }
|
||||
|
||||
bool GetSecondsSinceStarted(int64_t* seconds_since_started);
|
||||
bool GetSecondsSinceLastPlayed(int64_t* seconds_since_started);
|
||||
|
||||
// for offline save and restore
|
||||
int64_t GetPlaybackStartTime() { return playback_start_time_; }
|
||||
int64_t GetLastPlaybackTime() { return last_playback_time_; }
|
||||
void RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time);
|
||||
|
||||
bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; }
|
||||
bool IsPlaybackStarted() { return playback_start_time_ > 0; }
|
||||
|
||||
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
|
||||
private:
|
||||
friend class PolicyEngineTest;
|
||||
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
kLicenseStatePending, // if license is issued for sometime in the future
|
||||
@@ -67,11 +97,10 @@ class PolicyEngine {
|
||||
kLicenseStateExpired
|
||||
} LicenseState;
|
||||
|
||||
void Init(Clock* clock);
|
||||
int64_t GetLicenseExpiryTime();
|
||||
int64_t GetPlaybackExpiryTime();
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
int64_t GetLicenseDurationRemaining(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
int64_t GetPlaybackDurationRemaining(int64_t current_time);
|
||||
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
@@ -80,8 +109,19 @@ class PolicyEngine {
|
||||
|
||||
void UpdateRenewalRequest(int64_t current_time);
|
||||
|
||||
// Notifies updates in keys information and fire OnKeysChange event if
|
||||
// key changes.
|
||||
void NotifyKeysChange(CdmKeyStatus new_status);
|
||||
|
||||
// Notifies updates in expiry time and fire OnExpirationUpdate event if
|
||||
// expiry time changes.
|
||||
void NotifyExpirationUpdate();
|
||||
|
||||
// These setters are for testing only. Takes ownership of the pointers.
|
||||
void set_clock(Clock* clock);
|
||||
void set_max_res_engine(MaxResEngine* max_res_engine);
|
||||
|
||||
LicenseState license_state_;
|
||||
bool can_decrypt_;
|
||||
|
||||
// This is the current policy information for this license. This gets updated
|
||||
// as license renewals occur.
|
||||
@@ -97,6 +137,9 @@ class PolicyEngine {
|
||||
// license request/renewal
|
||||
int64_t license_start_time_;
|
||||
int64_t playback_start_time_;
|
||||
int64_t last_playback_time_;
|
||||
int64_t last_expiry_time_;
|
||||
bool last_expiry_time_set_;
|
||||
|
||||
// This is used as a reference point for policy management. This value
|
||||
// represents an offset from license_start_time_. This is used to
|
||||
@@ -104,11 +147,15 @@ class PolicyEngine {
|
||||
int64_t next_renewal_time_;
|
||||
int64_t policy_max_duration_seconds_;
|
||||
|
||||
Clock* clock_;
|
||||
// Used to dispatch CDM events.
|
||||
CdmSessionId session_id_;
|
||||
WvCdmEventListener* event_listener_;
|
||||
|
||||
// For testing
|
||||
friend class PolicyEngineTest;
|
||||
PolicyEngine(Clock* clock);
|
||||
scoped_ptr<MaxResEngine> max_res_engine_;
|
||||
|
||||
std::map<KeyId, CdmKeyStatus> keys_status_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine);
|
||||
};
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# include "gtest/gtest_prod.h"
|
||||
# include <gtest/gtest_prod.h>
|
||||
#endif
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::map<CdmSessionId, const CdmClientPropertySet*>
|
||||
typedef std::map<CdmSessionId, CdmClientPropertySet*>
|
||||
CdmClientPropertySetMap;
|
||||
|
||||
// This class saves information about features and properties enabled
|
||||
@@ -37,9 +37,6 @@ class Properties {
|
||||
static inline bool oem_crypto_use_userspace_buffers() {
|
||||
return oem_crypto_use_userspace_buffers_;
|
||||
}
|
||||
static inline bool oem_crypto_require_usage_tables() {
|
||||
return oem_crypto_require_usage_tables_;
|
||||
}
|
||||
static inline bool use_certificates_as_identification() {
|
||||
return use_certificates_as_identification_;
|
||||
}
|
||||
@@ -52,24 +49,29 @@ class Properties {
|
||||
static bool GetDeviceName(std::string* device_name);
|
||||
static bool GetProductName(std::string* product_name);
|
||||
static bool GetBuildInfo(std::string* build_info);
|
||||
static bool GetWVCdmVersion(std::string* version);
|
||||
static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
||||
std::string* base_path);
|
||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
||||
static bool GetOEMCryptoPath(std::string* library_name);
|
||||
static bool AlwaysUseKeySetIds();
|
||||
|
||||
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
|
||||
static bool GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level);
|
||||
static bool GetApplicationId(const CdmSessionId& session_id,
|
||||
std::string* app_id);
|
||||
static bool GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate);
|
||||
static bool SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate);
|
||||
static bool UsePrivacyMode(const CdmSessionId& session_id);
|
||||
static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
|
||||
|
||||
static bool AddSessionPropertySet(const CdmSessionId& session_id,
|
||||
const CdmClientPropertySet* property_set);
|
||||
CdmClientPropertySet* property_set);
|
||||
static bool RemoveSessionPropertySet(const CdmSessionId& session_id);
|
||||
|
||||
private:
|
||||
static const CdmClientPropertySet* GetCdmClientPropertySet(
|
||||
static CdmClientPropertySet* GetCdmClientPropertySet(
|
||||
const CdmSessionId& session_id);
|
||||
static void set_oem_crypto_use_secure_buffers(bool flag) {
|
||||
oem_crypto_use_secure_buffers_ = flag;
|
||||
@@ -80,9 +82,6 @@ class Properties {
|
||||
static void set_oem_crypto_use_userspace_buffers(bool flag) {
|
||||
oem_crypto_use_userspace_buffers_ = flag;
|
||||
}
|
||||
static void set_oem_crypto_require_usage_tables(bool flag) {
|
||||
oem_crypto_require_usage_tables_ = flag;
|
||||
}
|
||||
static void set_use_certificates_as_identification(bool flag) {
|
||||
use_certificates_as_identification_ = flag;
|
||||
}
|
||||
@@ -104,7 +103,6 @@ class Properties {
|
||||
static bool oem_crypto_use_secure_buffers_;
|
||||
static bool oem_crypto_use_fifo_;
|
||||
static bool oem_crypto_use_userspace_buffers_;
|
||||
static bool oem_crypto_require_usage_tables_;
|
||||
static bool use_certificates_as_identification_;
|
||||
static bool security_level_path_backward_compatibility_support_;
|
||||
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||
|
||||
@@ -60,6 +60,6 @@ class scoped_ptr {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(scoped_ptr);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_SCOPED_PTR_H_
|
||||
|
||||
@@ -22,7 +22,8 @@ std::string HexEncode(const uint8_t* bytes, unsigned size);
|
||||
std::string IntToString(int value);
|
||||
std::string UintToString(unsigned int value);
|
||||
int64_t htonll64(int64_t x);
|
||||
inline int64_t ntohll64(int64_t x) { return htonll64(x); }
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_STRING_CONVERSIONS_H_
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Timer - Platform independent interface for a Timer class
|
||||
//
|
||||
#ifndef WVCDM_CORE_TIMER_H_
|
||||
#define WVCDM_CORE_TIMER_H_
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Timer Handler class.
|
||||
//
|
||||
// Derive from this class if you wish to receive events when the timer
|
||||
// expires. Provide the handler when setting up a new Timer.
|
||||
|
||||
class TimerHandler {
|
||||
public:
|
||||
TimerHandler() {};
|
||||
virtual ~TimerHandler() {};
|
||||
|
||||
virtual void OnTimerEvent() = 0;
|
||||
};
|
||||
|
||||
// Timer class. The implementation is platform dependent.
|
||||
//
|
||||
// This class provides a simple recurring timer API. The class receiving
|
||||
// timer expiry events should derive from TimerHandler.
|
||||
// Specify the receiver class and the periodicty of timer events when
|
||||
// the timer is initiated by calling Start.
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
class Impl;
|
||||
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
bool Start(TimerHandler *handler, uint32_t time_in_secs);
|
||||
void Stop();
|
||||
bool IsRunning();
|
||||
|
||||
private:
|
||||
Impl *impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Timer);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_TIMER_H_
|
||||
@@ -14,6 +14,10 @@ static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||
|
||||
// Use 0 to represent never expired license as specified in EME spec
|
||||
// (NaN in JS translates to 0 in unix timestamp).
|
||||
static const int64_t NEVER_EXPIRES = 0;
|
||||
|
||||
static const char SESSION_ID_PREFIX[] = "sid";
|
||||
static const char KEY_SET_ID_PREFIX[] = "ksid";
|
||||
static const char KEY_SYSTEM[] = "com.widevine";
|
||||
@@ -43,6 +47,16 @@ static const std::string QUERY_KEY_SYSTEM_ID = "SystemID";
|
||||
// system id
|
||||
static const std::string QUERY_KEY_PROVISIONING_ID = "ProvisioningID";
|
||||
// provisioning unique id
|
||||
static const std::string QUERY_KEY_CURRENT_HDCP_LEVEL = "HdcpLevel";
|
||||
// current HDCP level
|
||||
static const std::string QUERY_KEY_MAX_HDCP_LEVEL = "MaxHdcpLevel";
|
||||
// maximum supported HDCP level
|
||||
static const std::string QUERY_KEY_USAGE_SUPPORT = "UsageSupport";
|
||||
// whether usage reporting is supported
|
||||
static const std::string QUERY_KEY_NUMBER_OF_OPEN_SESSIONS =
|
||||
"NumberOfOpenSessions";
|
||||
static const std::string QUERY_KEY_MAX_NUMBER_OF_SESSIONS =
|
||||
"MaxNumberOfSessions";
|
||||
|
||||
static const std::string QUERY_VALUE_TRUE = "True";
|
||||
static const std::string QUERY_VALUE_FALSE = "False";
|
||||
@@ -52,6 +66,12 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown";
|
||||
static const std::string QUERY_VALUE_DISCONNECTED = "Disconnected";
|
||||
static const std::string QUERY_VALUE_UNPROTECTED = "Unprotected";
|
||||
static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2";
|
||||
|
||||
static const std::string ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4";
|
||||
static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
|
||||
@@ -59,6 +79,8 @@ static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
|
||||
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
|
||||
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
|
||||
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
|
||||
|
||||
static const char EMPTY_ORIGIN[] = "";
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_
|
||||
|
||||
@@ -8,16 +8,19 @@
|
||||
namespace wvcdm {
|
||||
|
||||
// Listener for events from the Content Decryption Module.
|
||||
// The caller of the CDM API must provide an implementation for OnEvent
|
||||
// and signal its intent by using the Attach/DetachEventListener methods
|
||||
// in the WvContentDecryptionModule class.
|
||||
class WvCdmEventListener {
|
||||
public:
|
||||
WvCdmEventListener() {}
|
||||
virtual ~WvCdmEventListener() {}
|
||||
|
||||
virtual void OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) = 0;
|
||||
virtual void OnSessionRenewalNeeded(const CdmSessionId& session_id) = 0;
|
||||
virtual void OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) = 0;
|
||||
// Note that a |new_expiry_time_seconds| of 0 represents never expired
|
||||
// license.
|
||||
virtual void OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
int64_t new_expiry_time_seconds) = 0;
|
||||
|
||||
private:
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#ifndef WVCDM_CORE_WV_CDM_TYPES_H_
|
||||
#define WVCDM_CORE_WV_CDM_TYPES_H_
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef std::string CdmInitData;
|
||||
typedef std::string CdmKeyMessage;
|
||||
typedef std::string CdmKeyResponse;
|
||||
typedef std::string KeyId;
|
||||
typedef std::string CdmSecureStopId;
|
||||
typedef std::string CdmSessionId;
|
||||
typedef std::string CdmKeySetId;
|
||||
typedef std::string RequestId;
|
||||
@@ -28,6 +29,13 @@ typedef std::string CdmUsageInfoReleaseMessage;
|
||||
typedef std::string CdmProvisioningRequest;
|
||||
typedef std::string CdmProvisioningResponse;
|
||||
|
||||
enum CdmKeyRequestType {
|
||||
kKeyRequestTypeUnknown,
|
||||
kKeyRequestTypeInitial,
|
||||
kKeyRequestTypeRenewal,
|
||||
kKeyRequestTypeRelease,
|
||||
};
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
@@ -39,21 +47,189 @@ enum CdmResponseType {
|
||||
NEED_PROVISIONING,
|
||||
DEVICE_REVOKED,
|
||||
INSUFFICIENT_CRYPTO_RESOURCES,
|
||||
ADD_KEY_ERROR,
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_1,
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_2,
|
||||
CERT_PROVISIONING_INVALID_CERT_TYPE,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_1,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_2,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_3,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_1,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_2,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_3,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_5,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_6,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_7,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_8,
|
||||
CRYPTO_SESSION_OPEN_ERROR_1,
|
||||
CRYPTO_SESSION_OPEN_ERROR_2,
|
||||
CRYPTO_SESSION_OPEN_ERROR_3,
|
||||
CRYPTO_SESSION_OPEN_ERROR_4,
|
||||
CRYPTO_SESSION_OPEN_ERROR_5,
|
||||
DECRYPT_NOT_READY,
|
||||
DEVICE_CERTIFICATE_ERROR_1,
|
||||
DEVICE_CERTIFICATE_ERROR_2,
|
||||
DEVICE_CERTIFICATE_ERROR_3,
|
||||
DEVICE_CERTIFICATE_ERROR_4,
|
||||
EMPTY_KEY_DATA_1,
|
||||
EMPTY_KEY_DATA_2,
|
||||
EMPTY_KEYSET_ID,
|
||||
EMPTY_KEYSET_ID_ENG_1,
|
||||
EMPTY_KEYSET_ID_ENG_2,
|
||||
EMPTY_KEYSET_ID_ENG_3,
|
||||
EMPTY_KEYSET_ID_ENG_4,
|
||||
EMPTY_LICENSE_RENEWAL,
|
||||
EMPTY_LICENSE_RESPONSE_1,
|
||||
EMPTY_LICENSE_RESPONSE_2,
|
||||
EMPTY_PROVISIONING_CERTIFICATE,
|
||||
EMPTY_PROVISIONING_RESPONSE,
|
||||
EMPTY_SESSION_ID,
|
||||
GENERATE_DERIVED_KEYS_ERROR,
|
||||
LICENSE_RENEWAL_NONCE_GENERATION_ERROR,
|
||||
GENERATE_USAGE_REPORT_ERROR,
|
||||
GET_LICENSE_ERROR,
|
||||
GET_RELEASED_LICENSE_ERROR,
|
||||
GET_USAGE_INFO_ERROR_1,
|
||||
GET_USAGE_INFO_ERROR_2,
|
||||
GET_USAGE_INFO_ERROR_3,
|
||||
GET_USAGE_INFO_ERROR_4,
|
||||
INIT_DATA_NOT_FOUND,
|
||||
INVALID_CRYPTO_SESSION_1,
|
||||
INVALID_CRYPTO_SESSION_2,
|
||||
INVALID_CRYPTO_SESSION_3,
|
||||
INVALID_CRYPTO_SESSION_4,
|
||||
INVALID_CRYPTO_SESSION_5,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_1,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_2,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_3,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_4,
|
||||
INVALID_DEVICE_CERTIFICATE_TYPE,
|
||||
INVALID_KEY_SYSTEM,
|
||||
INVALID_LICENSE_RESPONSE,
|
||||
INVALID_LICENSE_TYPE,
|
||||
INVALID_PARAMETERS_ENG_1,
|
||||
INVALID_PARAMETERS_ENG_2,
|
||||
INVALID_PARAMETERS_ENG_3,
|
||||
INVALID_PARAMETERS_ENG_4,
|
||||
INVALID_PARAMETERS_LIC_1,
|
||||
INVALID_PARAMETERS_LIC_2,
|
||||
INVALID_PROVISIONING_PARAMETERS_1,
|
||||
INVALID_PROVISIONING_PARAMETERS_2,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_1,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_2,
|
||||
INVALID_QUERY_KEY,
|
||||
INVALID_SESSION_ID,
|
||||
KEY_REQUEST_ERROR_1,
|
||||
UNUSED_1, /* previously KEY_REQUEST_ERROR_2 */
|
||||
KEY_SIZE_ERROR,
|
||||
KEYSET_ID_NOT_FOUND_1,
|
||||
KEYSET_ID_NOT_FOUND_2,
|
||||
KEYSET_ID_NOT_FOUND_3,
|
||||
LICENSE_ID_NOT_FOUND,
|
||||
LICENSE_PARSER_INIT_ERROR,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_1,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_2,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_3,
|
||||
LICENSE_RESPONSE_NOT_SIGNED,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_1,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_2,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_3,
|
||||
LOAD_KEY_ERROR,
|
||||
NO_CONTENT_KEY,
|
||||
REFRESH_KEYS_ERROR,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_1,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_2,
|
||||
RELEASE_KEY_ERROR,
|
||||
RELEASE_KEY_REQUEST_ERROR,
|
||||
RELEASE_LICENSE_ERROR_1,
|
||||
RELEASE_LICENSE_ERROR_2,
|
||||
RELEASE_USAGE_INFO_ERROR,
|
||||
RENEW_KEY_ERROR_1,
|
||||
RENEW_KEY_ERROR_2,
|
||||
LICENSE_RENEWAL_SIGNING_ERROR,
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_1,
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_2,
|
||||
SESSION_INIT_ERROR_1,
|
||||
SESSION_INIT_ERROR_2,
|
||||
SESSION_INIT_GET_KEYBOX_ERROR,
|
||||
SESSION_NOT_FOUND_1,
|
||||
SESSION_NOT_FOUND_2,
|
||||
SESSION_NOT_FOUND_3,
|
||||
SESSION_NOT_FOUND_4,
|
||||
SESSION_NOT_FOUND_5,
|
||||
SESSION_NOT_FOUND_6,
|
||||
SESSION_NOT_FOUND_7,
|
||||
SESSION_NOT_FOUND_8,
|
||||
SESSION_NOT_FOUND_9,
|
||||
SESSION_NOT_FOUND_10,
|
||||
SESSION_NOT_FOUND_FOR_DECRYPT,
|
||||
SESSION_KEYS_NOT_FOUND,
|
||||
SIGNATURE_NOT_FOUND,
|
||||
STORE_LICENSE_ERROR_1,
|
||||
STORE_LICENSE_ERROR_2,
|
||||
STORE_LICENSE_ERROR_3,
|
||||
STORE_USAGE_INFO_ERROR,
|
||||
UNPROVISION_ERROR_1,
|
||||
UNPROVISION_ERROR_2,
|
||||
UNPROVISION_ERROR_3,
|
||||
UNPROVISION_ERROR_4,
|
||||
UNSUPPORTED_INIT_DATA,
|
||||
USAGE_INFO_NOT_FOUND,
|
||||
LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
PARSE_SERVICE_CERTIFICATE_ERROR,
|
||||
SERVICE_CERTIFICATE_TYPE_ERROR,
|
||||
CLIENT_ID_GENERATE_RANDOM_ERROR,
|
||||
CLIENT_ID_AES_INIT_ERROR,
|
||||
CLIENT_ID_AES_ENCRYPT_ERROR,
|
||||
CLIENT_ID_RSA_INIT_ERROR,
|
||||
CLIENT_ID_RSA_ENCRYPT_ERROR,
|
||||
INVALID_QUERY_STATUS,
|
||||
DUPLICATE_SESSION_ID_SPECIFIED,
|
||||
EMPTY_PROVISIONING_CERTIFICATE_2,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_4,
|
||||
INVALID_PARAMETERS_LIC_3,
|
||||
INVALID_PARAMETERS_LIC_4,
|
||||
UNUSED_2, /* previously INVALID_PARAMETERS_LIC_5 */
|
||||
INVALID_PARAMETERS_LIC_6,
|
||||
INVALID_PARAMETERS_LIC_7,
|
||||
LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
CENC_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_CENC_CONTENT_ID_FAILED,
|
||||
WEBM_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_WEBM_CONTENT_ID_FAILED,
|
||||
UNSUPPORTED_INIT_DATA_FORMAT,
|
||||
LICENSE_REQUEST_NONCE_GENERATION_ERROR,
|
||||
LICENSE_REQUEST_SIGNING_ERROR,
|
||||
EMPTY_LICENSE_REQUEST,
|
||||
};
|
||||
|
||||
enum CdmKeyStatus {
|
||||
kKeyStatusUsable,
|
||||
kKeyStatusExpired,
|
||||
kKeyStatusOutputNotAllowed,
|
||||
kKeyStatusPending,
|
||||
kKeyStatusInternalError,
|
||||
};
|
||||
typedef std::map<KeyId, CdmKeyStatus> CdmKeyStatusMap;
|
||||
|
||||
#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
enum CdmEventType {
|
||||
LICENSE_EXPIRED_EVENT,
|
||||
LICENSE_RENEWAL_NEEDED_EVENT
|
||||
};
|
||||
|
||||
enum CdmLicenseType {
|
||||
kLicenseTypeOffline,
|
||||
kLicenseTypeStreaming,
|
||||
kLicenseTypeRelease
|
||||
kLicenseTypeRelease,
|
||||
// If the original request was saved to make a service certificate request,
|
||||
// use Deferred for the license type in the subsequent request.
|
||||
kLicenseTypeDeferred,
|
||||
};
|
||||
|
||||
enum SecurityLevel {
|
||||
kLevelDefault,
|
||||
kLevel3
|
||||
};
|
||||
|
||||
enum CdmSecurityLevel {
|
||||
|
||||
@@ -21,16 +21,43 @@
|
||||
namespace {
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // unnamed namespace
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class UsagePropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
UsagePropertySet() {}
|
||||
virtual ~UsagePropertySet() {}
|
||||
void set_security_level(SecurityLevel security_level) {
|
||||
if (kLevel3 == security_level)
|
||||
security_level_ = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
else
|
||||
security_level_.clear();
|
||||
}
|
||||
virtual const std::string& security_level() const { return security_level_; }
|
||||
virtual bool use_privacy_mode() const { return false; }
|
||||
virtual const std::string& service_certificate() const { return empty_; }
|
||||
virtual void set_service_certificate(const std::string&) {}
|
||||
virtual bool is_session_sharing_enabled() const { return false; }
|
||||
virtual uint32_t session_sharing_id() const { return 0; }
|
||||
virtual void set_session_sharing_id(uint32_t /* id */) {}
|
||||
virtual const std::string& app_id() const { return app_id_; }
|
||||
void set_app_id(const std::string& appId) { app_id_ = appId; }
|
||||
|
||||
private:
|
||||
std::string app_id_;
|
||||
std::string security_level_;
|
||||
const std::string empty_;
|
||||
};
|
||||
|
||||
bool CdmEngine::seeded_ = false;
|
||||
|
||||
CdmEngine::CdmEngine()
|
||||
: cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
: cert_provisioning_(NULL),
|
||||
cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
usage_session_(NULL),
|
||||
last_usage_information_update_time(0) {
|
||||
last_usage_information_update_time_(0) {
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
@@ -40,6 +67,7 @@ CdmEngine::CdmEngine()
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator i(sessions_.begin());
|
||||
for (; i != sessions_.end(); ++i) {
|
||||
delete i->second;
|
||||
@@ -48,24 +76,34 @@ CdmEngine::~CdmEngine() {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
LOGI("CdmEngine::OpenSession");
|
||||
|
||||
if (!ValidateKeySystem(key_system)) {
|
||||
LOGI("CdmEngine::OpenSession: invalid key_system = %s", key_system.c_str());
|
||||
return KEY_ERROR;
|
||||
return INVALID_KEY_SYSTEM;
|
||||
}
|
||||
|
||||
if (!session_id) {
|
||||
LOGE("CdmEngine::OpenSession: no session ID destination provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_1;
|
||||
}
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(property_set));
|
||||
if (forced_session_id) {
|
||||
if (sessions_.find(*forced_session_id) != sessions_.end()) {
|
||||
return DUPLICATE_SESSION_ID_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<CdmSession> new_session(
|
||||
new CdmSession(property_set, origin, event_listener, forced_session_id));
|
||||
if (new_session->session_id().empty()) {
|
||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return EMPTY_SESSION_ID;
|
||||
}
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
@@ -74,25 +112,30 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
cert_provisioning_requested_security_level_ =
|
||||
new_session->GetRequestedSecurityLevel();
|
||||
} else {
|
||||
LOGE("CdmEngine::OpenSession: bad session init: %u", sts);
|
||||
LOGE("CdmEngine::OpenSession: bad session init: %d", sts);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
*session_id = new_session->session_id();
|
||||
AutoLock lock(session_list_lock_);
|
||||
sessions_[*session_id] = new_session.release();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_1;
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
|
||||
CdmResponseType sts =
|
||||
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
|
||||
NULL /* forced_session_id */, &session_id);
|
||||
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
@@ -102,13 +145,12 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
|
||||
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_1;
|
||||
}
|
||||
|
||||
CdmSession* session = iter->second;
|
||||
sessions_.erase(session_id);
|
||||
delete session;
|
||||
@@ -122,7 +164,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = CloseSession(iter->second);
|
||||
@@ -130,33 +172,42 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
return iter != sessions_.end();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out) {
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
CdmResponseType sts;
|
||||
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
// NOTE: If AlwaysUseKeySetIds() is true, there is no need to consult the
|
||||
// release_key_sets_ map for release licenses.
|
||||
if (license_type == kLicenseTypeRelease &&
|
||||
!Properties::AlwaysUseKeySetIds()) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_2;
|
||||
}
|
||||
|
||||
if (!session_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_SESSION_ID;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
@@ -166,12 +217,12 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_2;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
@@ -179,27 +230,23 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(
|
||||
init_data, license_type, app_parameters, key_request, server_url,
|
||||
key_set_id_out);
|
||||
init_data, license_type, app_parameters, key_request, key_request_type,
|
||||
server_url, key_set_id_out);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -221,18 +268,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
if (license_type_release) {
|
||||
if (!key_set_id) {
|
||||
LOGE("CdmEngine::AddKey: no key set id provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_3;
|
||||
}
|
||||
|
||||
if (key_set_id->empty()) {
|
||||
LOGE("CdmEngine::AddKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_3;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
|
||||
return KEY_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
@@ -242,18 +289,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::AddKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEY_DATA_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
|
||||
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -266,14 +313,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::RestoreKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_4;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_NOT_FOUND_4;
|
||||
}
|
||||
|
||||
CdmResponseType sts =
|
||||
@@ -282,10 +329,10 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
if (sts != KEY_ADDED) {
|
||||
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
|
||||
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
|
||||
}
|
||||
return sts;
|
||||
return sts; // TODO ewew
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
@@ -295,7 +342,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_5;
|
||||
}
|
||||
|
||||
iter->second->ReleaseCrypto();
|
||||
@@ -311,12 +358,12 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_6;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_4;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
@@ -326,7 +373,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
|
||||
(int)sts);
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -340,26 +387,31 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_7;
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::RenewKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEY_DATA_2;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->RenewKey(key_data);
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts);
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryStatus");
|
||||
CryptoSession crypto_session;
|
||||
if (security_level == kLevel3) {
|
||||
CdmResponseType status = crypto_session.Open(kLevel3);
|
||||
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
|
||||
}
|
||||
switch (crypto_session.GetSecurityLevel()) {
|
||||
case kSecurityLevelL1:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
@@ -376,7 +428,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
return KEY_ERROR;
|
||||
return INVALID_QUERY_KEY;
|
||||
}
|
||||
|
||||
std::string deviceId;
|
||||
@@ -399,6 +451,39 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability current_hdcp;
|
||||
CryptoSession::HdcpCapability max_hdcp;
|
||||
success = crypto_session.GetHdcpCapabilities(¤t_hdcp, &max_hdcp);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_CURRENT_HDCP_LEVEL] = MapHdcpVersion(current_hdcp);
|
||||
(*key_info)[QUERY_KEY_MAX_HDCP_LEVEL] = MapHdcpVersion(max_hdcp);
|
||||
}
|
||||
|
||||
bool supports_usage_reporting;
|
||||
success = crypto_session.UsageInformationSupport(&supports_usage_reporting);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_USAGE_SUPPORT] =
|
||||
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
}
|
||||
|
||||
size_t number_of_open_sessions;
|
||||
success = crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions);
|
||||
if (success) {
|
||||
std::ostringstream open_sessions_stream;
|
||||
open_sessions_stream << number_of_open_sessions;
|
||||
(*key_info)[QUERY_KEY_NUMBER_OF_OPEN_SESSIONS] =
|
||||
open_sessions_stream.str();
|
||||
}
|
||||
|
||||
size_t maximum_number_of_sessions;
|
||||
success = crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions);
|
||||
if (success) {
|
||||
std::ostringstream max_sessions_stream;
|
||||
max_sessions_stream << maximum_number_of_sessions;
|
||||
(*key_info)[QUERY_KEY_MAX_NUMBER_OF_SESSIONS] =
|
||||
max_sessions_stream.str();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -409,11 +494,33 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_8;
|
||||
}
|
||||
return iter->second->QueryStatus(key_info);
|
||||
}
|
||||
|
||||
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsReleaseSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_release();
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsOfflineSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_offline();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
@@ -421,7 +528,7 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_9;
|
||||
}
|
||||
return iter->second->QueryKeyStatus(key_info);
|
||||
}
|
||||
@@ -433,7 +540,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_10;
|
||||
}
|
||||
return iter->second->QueryKeyControlInfo(key_info);
|
||||
}
|
||||
@@ -443,18 +550,30 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CdmResponseType error code if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!request || !default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||
return UNKNOWN_ERROR;
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!request) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_1;
|
||||
}
|
||||
return cert_provisioning_.GetProvisioningRequest(
|
||||
if (!default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_2;
|
||||
}
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
cert_provisioning_.reset(new CertificateProvisioning());
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
request, default_url);
|
||||
origin, request, default_url);
|
||||
if (ret != NO_ERROR) {
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -462,47 +581,183 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
* in the device.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CdmResponseType error code if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
if (response.empty()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||
return UNKNOWN_ERROR;
|
||||
cert_provisioning_.reset(NULL);
|
||||
return EMPTY_PROVISIONING_RESPONSE;
|
||||
}
|
||||
if (NULL == cert) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_1;
|
||||
}
|
||||
if (NULL == wrapped_key) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_2;
|
||||
}
|
||||
return cert_provisioning_.HandleProvisioningResponse(response, cert,
|
||||
wrapped_key);
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: provisioning object missing.");
|
||||
return EMPTY_PROVISIONING_CERTIFICATE;
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
|
||||
origin, response, cert, wrapped_key);
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
return ret;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
return handle.HasCertificate(origin);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
return UNPROVISION_ERROR_1;
|
||||
}
|
||||
|
||||
if (!handle.DeleteAllFiles()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNKNOWN_ERROR;
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
if (!handle.RemoveCertificate(origin)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
|
||||
origin.c_str());
|
||||
return UNPROVISION_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
} else {
|
||||
if (!handle.DeleteAllFiles()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNPROVISION_ERROR_3;
|
||||
}
|
||||
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
|
||||
return UNPROVISION_ERROR_4;
|
||||
}
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
usage_session_.reset(new CdmSession(NULL));
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info) {
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
usage_property_set_->set_security_level(kLevelDefault);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: device file init error");
|
||||
return GET_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
|
||||
CdmKeyMessage license_request;
|
||||
CdmKeyResponse license_response;
|
||||
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
|
||||
&license_response)) {
|
||||
usage_property_set_->set_security_level(kLevel3);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
status = usage_session_->Init();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: device file init error");
|
||||
return GET_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
|
||||
&license_response)) {
|
||||
// No entry found for that ssid.
|
||||
return USAGE_INFO_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
usage_info->resize(1);
|
||||
status =
|
||||
usage_session_->RestoreUsageSession(license_request, license_response);
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
CdmUsageInfo* usage_info) {
|
||||
// Return a random usage report from a random security level
|
||||
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
|
||||
CdmResponseType status = UNKNOWN_ERROR;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
|
||||
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
|
||||
} while (KEY_CANCELED == status);
|
||||
|
||||
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
if (NEED_PROVISIONING == status)
|
||||
return NO_ERROR; // Valid scenario that one of the security
|
||||
// levels has not been provisioned
|
||||
} while (KEY_CANCELED == status);
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
SecurityLevel requested_security_level,
|
||||
CdmUsageInfo* usage_info) {
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
usage_property_set_->set_security_level(requested_security_level);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
if (NO_ERROR != status) {
|
||||
@@ -513,13 +768,13 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
|
||||
return status;
|
||||
return GET_USAGE_INFO_ERROR_3;
|
||||
}
|
||||
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
|
||||
if (!handle.RetrieveUsageInfo(&license_info)) {
|
||||
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
|
||||
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
|
||||
return UNKNOWN_ERROR;
|
||||
return GET_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (0 == license_info.size()) {
|
||||
@@ -543,25 +798,59 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
|
||||
status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
break;
|
||||
case KEY_CANCELED: // usage information not present in
|
||||
usage_session_->DeleteLicense(); // OEMCrypto, delete and try again
|
||||
usage_info->clear();
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d",
|
||||
status);
|
||||
usage_info->clear();
|
||||
break;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
CdmResponseType status = NO_ERROR;
|
||||
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
|
||||
DeviceFiles handle;
|
||||
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
|
||||
"stops", j);
|
||||
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
|
||||
} else {
|
||||
CdmResponseType status2 = usage_session_->
|
||||
DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
if (status2 != NO_ERROR) {
|
||||
status = status2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device"
|
||||
"files", j);
|
||||
status = RELEASE_ALL_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message) {
|
||||
if (NULL == usage_session_.get()) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized");
|
||||
return UNKNOWN_ERROR;
|
||||
return RELEASE_USAGE_INFO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType status = usage_session_->ReleaseKey(message);
|
||||
usage_session_.reset(NULL);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status);
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %d", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -570,25 +859,26 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters) {
|
||||
if (parameters.key_id == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no key_id");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_1;
|
||||
}
|
||||
|
||||
if (parameters.encrypt_buffer == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no src encrypt buffer");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_2;
|
||||
}
|
||||
|
||||
if (parameters.iv == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no iv");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_3;
|
||||
}
|
||||
|
||||
if (parameters.decrypt_buffer == NULL) {
|
||||
if (!parameters.is_secure &&
|
||||
!Properties::Properties::oem_crypto_use_fifo()) {
|
||||
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
|
||||
return KEY_ERROR;
|
||||
} // else we must be level 1 direct and we don't need to return a buffer.
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_4;
|
||||
}
|
||||
// else we must be level 1 direct and we don't need to return a buffer.
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter;
|
||||
@@ -605,7 +895,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(), session_id.size());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_FOR_DECRYPT;
|
||||
}
|
||||
|
||||
return iter->second->Decrypt(parameters);
|
||||
@@ -650,24 +940,12 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdmEngine::AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
void CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
if (iter != sessions_.end()) {
|
||||
iter->second->NotifyResolution(width, height);
|
||||
}
|
||||
|
||||
return iter->second->AttachEventListener(listener);
|
||||
}
|
||||
|
||||
bool CdmEngine::DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return iter->second->DetachEventListener(listener);
|
||||
}
|
||||
|
||||
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
@@ -677,37 +955,73 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
void CdmEngine::OnTimerEvent() {
|
||||
Clock clock;
|
||||
uint64_t current_time = clock.GetCurrentTime();
|
||||
bool update_usage_information = false;
|
||||
bool usage_update_period_expired = false;
|
||||
|
||||
if (current_time - last_usage_information_update_time >
|
||||
if (current_time - last_usage_information_update_time_ >
|
||||
kUpdateUsageInformationPeriod) {
|
||||
update_usage_information = true;
|
||||
last_usage_information_update_time = current_time;
|
||||
usage_update_period_expired = true;
|
||||
last_usage_information_update_time_ = current_time;
|
||||
}
|
||||
|
||||
bool is_initial_usage_update = false;
|
||||
bool is_usage_update_needed = false;
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnTimerEvent();
|
||||
is_initial_usage_update =
|
||||
is_initial_usage_update || iter->second->is_initial_usage_update();
|
||||
is_usage_update_needed =
|
||||
is_usage_update_needed || iter->second->is_usage_update_needed();
|
||||
|
||||
if (update_usage_information && iter->second->is_usage_update_needed()) {
|
||||
// usage is updated for all sessions so this needs to be
|
||||
// called only once per update usage information period
|
||||
CdmResponseType status = iter->second->UpdateUsageInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Update usage information failed: %u", status);
|
||||
} else {
|
||||
update_usage_information = false;
|
||||
iter->second->OnTimerEvent(usage_update_period_expired);
|
||||
}
|
||||
|
||||
if (is_usage_update_needed &&
|
||||
(usage_update_period_expired || is_initial_usage_update)) {
|
||||
bool has_usage_been_updated = false;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->reset_usage_flags();
|
||||
if (!has_usage_been_updated) {
|
||||
// usage is updated for all sessions so this needs to be
|
||||
// called only once per update usage information period
|
||||
CdmResponseType status = iter->second->UpdateUsageInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Update usage information failed: %d", status);
|
||||
} else {
|
||||
has_usage_been_updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter->second->reset_is_usage_update_needed();
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
AutoLock lock(session_list_lock_);
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CdmEngine::MapHdcpVersion(
|
||||
CryptoSession::HdcpCapability version) {
|
||||
switch (version) {
|
||||
case HDCP_NONE:
|
||||
return QUERY_VALUE_UNPROTECTED;
|
||||
case HDCP_V1:
|
||||
return QUERY_VALUE_HDCP_V1;
|
||||
case HDCP_V2:
|
||||
return QUERY_VALUE_HDCP_V2_0;
|
||||
case HDCP_V2_1:
|
||||
return QUERY_VALUE_HDCP_V2_1;
|
||||
case HDCP_V2_2:
|
||||
return QUERY_VALUE_HDCP_V2_2;
|
||||
case HDCP_NO_DIGITAL_OUTPUT:
|
||||
return QUERY_VALUE_DISCONNECTED;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include "cdm_session.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
@@ -22,58 +25,41 @@ const size_t kKeySetIdLength = 14;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
||||
|
||||
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(new CdmLicense(), new CryptoSession(), new PolicyEngine(),
|
||||
new DeviceFiles(), cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession::CdmSession(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(license_parser, crypto_session, policy_engine, file_handle,
|
||||
cdm_client_property_set);
|
||||
}
|
||||
|
||||
void CdmSession::Create(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
// Just return on failures. Failures will be signaled in Init.
|
||||
if (NULL == license_parser) {
|
||||
LOGE("CdmSession::Create: License parser not provided");
|
||||
return;
|
||||
CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
|
||||
const std::string& origin,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id)
|
||||
: initialized_(false),
|
||||
session_id_(GenerateSessionId()),
|
||||
origin_(origin),
|
||||
crypto_session_(new CryptoSession),
|
||||
file_handle_(new DeviceFiles),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_decryption_(true),
|
||||
has_decrypted_since_last_report_(false),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false) {
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
if (forced_session_id) {
|
||||
key_set_id_ = *forced_session_id;
|
||||
} else {
|
||||
bool ok = GenerateKeySetId(&key_set_id_);
|
||||
assert(ok);
|
||||
}
|
||||
session_id_ = key_set_id_;
|
||||
}
|
||||
if (NULL == crypto_session) {
|
||||
LOGE("CdmSession::Create: Crypto session not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == policy_engine) {
|
||||
LOGE("CdmSession::Create: Policy engine not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == file_handle) {
|
||||
LOGE("CdmSession::Create: Device files not provided");
|
||||
return;
|
||||
}
|
||||
initialized_ = false;
|
||||
session_id_ = GenerateSessionId();
|
||||
license_parser_.reset(license_parser);
|
||||
crypto_session_.reset(crypto_session);
|
||||
file_handle_.reset(file_handle);
|
||||
policy_engine_.reset(policy_engine);
|
||||
license_received_ = false;
|
||||
is_offline_ = false;
|
||||
is_release_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
requested_security_level_ = kLevelDefault;
|
||||
if (NULL != cdm_client_property_set) {
|
||||
if (QUERY_VALUE_SECURITY_LEVEL_L3.compare(
|
||||
cdm_client_property_set->security_level()) == 0) {
|
||||
license_parser_.reset(new CdmLicense(session_id_));
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
if (cdm_client_property_set) {
|
||||
if (cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
requested_security_level_ = kLevel3;
|
||||
security_level_ = kSecurityLevelL3;
|
||||
}
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
}
|
||||
@@ -84,11 +70,11 @@ CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
|
||||
CdmResponseType CdmSession::Init() {
|
||||
if (session_id_.empty()) {
|
||||
LOGE("CdmSession::Init: Failed, session not properly constructed");
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_INIT_ERROR_1;
|
||||
}
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_INIT_ERROR_2;
|
||||
}
|
||||
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
|
||||
if (NO_ERROR != sts) return sts;
|
||||
@@ -98,17 +84,18 @@ CdmResponseType CdmSession::Init() {
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
std::string wrapped_key;
|
||||
if (!file_handle_->Init(security_level_) ||
|
||||
!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
if (!crypto_session_->GetToken(&token)) return UNKNOWN_ERROR;
|
||||
if (!crypto_session_->GetToken(&token))
|
||||
return SESSION_INIT_GET_KEYBOX_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_->Init(token, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
return UNKNOWN_ERROR;
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
@@ -121,27 +108,42 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
if (!file_handle_->Reset(security_level_)) return UNKNOWN_ERROR;
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_1;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
|
||||
if (!file_handle_->RetrieveLicense(
|
||||
key_set_id, &license_state, &offline_init_data_, &key_request_,
|
||||
&key_response_, &offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_, &offline_release_server_url_)) {
|
||||
&offline_key_renewal_response_, &offline_release_server_url_,
|
||||
&playback_start_time, &last_playback_time, &app_parameters_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return GET_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
|
||||
return UNKNOWN_ERROR;
|
||||
// Do not restore a released offline license, unless a release retry
|
||||
if (!(license_type == kLicenseTypeRelease ||
|
||||
license_state == DeviceFiles::kLicenseStateActive)) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %d, type = %d",
|
||||
license_state, license_type);
|
||||
return GET_RELEASED_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_,
|
||||
offline_key_renewal_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
if (!license_parser_->RestoreLicenseForRelease(key_request_,
|
||||
key_response_)) {
|
||||
return RELEASE_LICENSE_ERROR_1;
|
||||
}
|
||||
} else {
|
||||
if (!license_parser_->RestoreOfflineLicense(
|
||||
key_request_, key_response_, offline_key_renewal_response_,
|
||||
playback_start_time, last_playback_time)) {
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_2;
|
||||
}
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
@@ -155,8 +157,8 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
key_request_ = key_request;
|
||||
key_response_ = key_response;
|
||||
|
||||
if (!license_parser_->RestoreUsageLicense(key_request_, key_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
|
||||
return RELEASE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
@@ -166,17 +168,18 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id) {
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_1;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_1;
|
||||
}
|
||||
|
||||
switch (license_type) {
|
||||
@@ -189,36 +192,61 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
case kLicenseTypeRelease:
|
||||
is_release_ = true;
|
||||
break;
|
||||
case kLicenseTypeDeferred:
|
||||
// If you're going to pass Deferred, you must have empty init data in
|
||||
// this call and stored init data from the previous call.
|
||||
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
// The arguments check out.
|
||||
// The is_release_ and is_offline_ flags were already set last time based
|
||||
// on the original license type. Do not change them, and use them to
|
||||
// re-derive the original license type.
|
||||
if (is_release_) {
|
||||
license_type = kLicenseTypeRelease;
|
||||
} else if (is_offline_) {
|
||||
license_type = kLicenseTypeOffline;
|
||||
} else {
|
||||
license_type = kLicenseTypeStreaming;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
|
||||
license_type);
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRelease;
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
} else if (license_received_) { // renewal
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRenewal;
|
||||
return GenerateRenewalRequest(key_request, server_url);
|
||||
} else {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return KEY_ERROR;
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeInitial;
|
||||
if (!license_parser_->HasInitData()) {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return UNSUPPORTED_INIT_DATA;
|
||||
}
|
||||
if (init_data.IsEmpty()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return INIT_DATA_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (init_data.IsEmpty() && !license_parser_->HasInitData()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (is_offline_ && !GenerateKeySetId(&key_set_id_)) {
|
||||
if (is_offline_ && key_set_id_.empty() &&
|
||||
!GenerateKeySetId(&key_set_id_)) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return KEY_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
app_parameters_ = app_parameters;
|
||||
CdmResponseType status = license_parser_->PrepareKeyRequest(
|
||||
init_data, license_type,
|
||||
app_parameters, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
key_request_ = *key_request;
|
||||
if (is_offline_) {
|
||||
@@ -236,12 +264,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_2;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::AddKey: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_2;
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
@@ -252,7 +280,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
|
||||
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? ADD_KEY_ERROR : sts;
|
||||
|
||||
license_received_ = true;
|
||||
key_response_ = key_response;
|
||||
@@ -270,12 +298,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGE("CdmSession::QueryStatus: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_3;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGE("CdmSession::QueryStatus: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_3;
|
||||
}
|
||||
|
||||
switch (security_level_) {
|
||||
@@ -294,7 +322,7 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
return KEY_ERROR;
|
||||
return INVALID_QUERY_KEY;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -306,12 +334,12 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_4;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_4;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
@@ -322,20 +350,40 @@ CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||
return UNKNOWN_ERROR;
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::Decrypt: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_5;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::Decrypt: Crypto session not open");
|
||||
return CRYPTO_SESSION_OPEN_ERROR_5;
|
||||
}
|
||||
|
||||
// Playback may not begin until either the start time passes or the license
|
||||
// is updated, so we treat this Decrypt call as invalid.
|
||||
if (params.is_encrypted && !policy_engine_->CanDecrypt(*params.key_id)) {
|
||||
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
|
||||
}
|
||||
|
||||
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||
|
||||
if (NO_ERROR == status) {
|
||||
if (status == NO_ERROR) {
|
||||
if (is_initial_decryption_) {
|
||||
policy_engine_->BeginDecryption();
|
||||
is_initial_decryption_ = false;
|
||||
}
|
||||
has_decrypted_since_last_report_ = true;
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ =
|
||||
!license_parser_->provider_session_token().empty();
|
||||
}
|
||||
} else {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
if (policy_engine_->IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -346,11 +394,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(true, key_request,
|
||||
server_url)) {
|
||||
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
true, app_parameters_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_request_ = *key_request;
|
||||
@@ -362,11 +409,12 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(true, key_response);
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? RENEW_KEY_ERROR_1 : sts;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive))
|
||||
return RENEW_KEY_ERROR_2;
|
||||
}
|
||||
return KEY_ADDED;
|
||||
}
|
||||
@@ -374,12 +422,14 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
is_release_ = true;
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url))
|
||||
return UNKNOWN_ERROR;
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
false, app_parameters_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (is_offline_) { // Mark license as being released
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
|
||||
return UNKNOWN_ERROR;
|
||||
return RELEASE_KEY_REQUEST_ERROR;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
@@ -388,7 +438,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(false, key_response);
|
||||
if (KEY_ADDED != sts) return sts;
|
||||
if (KEY_ADDED != sts) return (KEY_ERROR == sts) ? RELEASE_KEY_ERROR : sts;
|
||||
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
DeleteLicense();
|
||||
@@ -427,6 +477,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
key_set_id->clear();
|
||||
}
|
||||
}
|
||||
// Reserve the license ID to avoid collisions.
|
||||
file_handle_->ReserveLicenseId(*key_set_id);
|
||||
return true;
|
||||
}
|
||||
@@ -435,7 +486,7 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
if (is_offline_) {
|
||||
if (key_set_id_.empty()) {
|
||||
LOGE("CdmSession::StoreLicense: No key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return EMPTY_KEYSET_ID;
|
||||
}
|
||||
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
|
||||
@@ -447,7 +498,7 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
}
|
||||
|
||||
key_set_id_.clear();
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -456,18 +507,20 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
license_parser_->provider_session_token();
|
||||
if (provider_session_token.empty()) {
|
||||
LOGE("CdmSession::StoreLicense: No provider session token and not offline");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_3;
|
||||
}
|
||||
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
|
||||
key_response_)) {
|
||||
key_response_, app_id)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store usage info");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_USAGE_INFO_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -478,57 +531,67 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
return file_handle_->StoreLicense(
|
||||
key_set_id_, state, offline_init_data_, key_request_, key_response_,
|
||||
offline_key_renewal_request_, offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
|
||||
policy_engine_->GetLastPlaybackTime(), app_parameters_);
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
if (!is_offline_ && license_parser_->provider_session_token().empty())
|
||||
return false;
|
||||
|
||||
if (!license_parser_->provider_session_token().empty()) {
|
||||
if (crypto_session_->DeleteUsageInformation(
|
||||
license_parser_->provider_session_token()) != NO_ERROR) {
|
||||
LOGE("CdmSession::DeleteLicense: error deleting usage info");
|
||||
}
|
||||
}
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_offline_)
|
||||
if (is_offline_) {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
else
|
||||
} else {
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
return file_handle_->DeleteUsageInfo(
|
||||
license_parser_->provider_session_token());
|
||||
app_id, license_parser_->provider_session_token());
|
||||
}
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
|
||||
return result.second;
|
||||
void CdmSession::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
policy_engine_->NotifyResolution(width, height);
|
||||
}
|
||||
|
||||
bool CdmSession::DetachEventListener(WvCdmEventListener* listener) {
|
||||
return (listeners_.erase(listener) == 1);
|
||||
}
|
||||
|
||||
void CdmSession::OnTimerEvent() {
|
||||
bool event_occurred = false;
|
||||
CdmEventType event;
|
||||
|
||||
policy_engine_->OnTimerEvent(&event_occurred, &event);
|
||||
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->OnEvent(session_id_, event);
|
||||
void CdmSession::OnTimerEvent(bool update_usage) {
|
||||
if (update_usage && has_decrypted_since_last_report_) {
|
||||
policy_engine_->DecryptionEvent();
|
||||
has_decrypted_since_last_report_ = false;
|
||||
if (is_offline_ && !is_release_) {
|
||||
StoreLicense(DeviceFiles::kLicenseStateActive);
|
||||
}
|
||||
}
|
||||
policy_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
if (key_set_id_ == key_set_id) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->OnEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
policy_engine_->NotifySessionExpiration();
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
if (app_id && !Properties::GetApplicationId(session_id_, app_id)) {
|
||||
*app_id = "";
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
return crypto_session_->DeleteMultipleUsageInformation(
|
||||
provider_session_tokens);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageInformation() {
|
||||
return crypto_session_->UpdateUsageInformation();
|
||||
}
|
||||
@@ -538,4 +601,20 @@ CdmResponseType CdmSession::ReleaseCrypto() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void CdmSession::set_license_parser(CdmLicense* license_parser) {
|
||||
license_parser_.reset(license_parser);
|
||||
}
|
||||
|
||||
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
|
||||
crypto_session_.reset(crypto_session);
|
||||
}
|
||||
|
||||
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
|
||||
policy_engine_.reset(policy_engine);
|
||||
}
|
||||
|
||||
void CdmSession::set_file_handle(DeviceFiles* file_handle) {
|
||||
file_handle_.reset(file_handle);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -53,15 +53,15 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
default_url->assign(kProvisioningServerUrl);
|
||||
@@ -79,14 +79,14 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
std::string token;
|
||||
if (!crypto_session_.GetToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session_.GenerateNonce(&nonce)) {
|
||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_2;
|
||||
}
|
||||
|
||||
// The provisioning server does not convert the nonce to uint32_t, it just
|
||||
@@ -107,12 +107,21 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
break;
|
||||
default:
|
||||
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_INVALID_CERT_TYPE;
|
||||
}
|
||||
|
||||
cert_type_ = cert_type;
|
||||
options->set_certificate_authority(cert_authority);
|
||||
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
std::string device_unique_id;
|
||||
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
|
||||
LOGE("GetProvisioningRequest: fails to get device unique ID");
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
|
||||
}
|
||||
provisioning_request.set_stable_id(device_unique_id + origin);
|
||||
}
|
||||
|
||||
std::string serialized_message;
|
||||
provisioning_request.SerializeToString(&serialized_message);
|
||||
|
||||
@@ -121,11 +130,11 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
if (!crypto_session_.PrepareRequest(serialized_message, true,
|
||||
&request_signature)) {
|
||||
LOGE("GetProvisioningRequest: fails to prepare request");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_3;
|
||||
}
|
||||
if (request_signature.empty()) {
|
||||
LOGE("GetProvisioningRequest: request signature is empty");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_4;
|
||||
}
|
||||
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
@@ -177,11 +186,11 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
* in the device.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
// Extracts signed response from JSON string, decodes base64 signed response
|
||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||
const std::string kMessageEnd = "\"";
|
||||
@@ -189,7 +198,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||
&serialized_signed_response)) {
|
||||
LOGE("Fails to extract signed serialized response from JSON response");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||
}
|
||||
|
||||
// Authenticates provisioning response using D1s (server key derived from
|
||||
@@ -198,7 +207,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
SignedProvisioningMessage signed_response;
|
||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed response");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_2;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
@@ -212,19 +221,19 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) return UNKNOWN_ERROR;
|
||||
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("HandleProvisioningResponse: Fails to parse signed message");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_4;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("HandleProvisioningResponse: key not found");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_5;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
@@ -236,7 +245,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
enc_rsa_key, rsa_key_iv,
|
||||
&wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
|
||||
crypto_session_.Close();
|
||||
@@ -253,11 +262,11 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_7;
|
||||
}
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_8;
|
||||
}
|
||||
handle.DeleteAllLicenses();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "crypto_session.h"
|
||||
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "crypto_key.h"
|
||||
@@ -24,6 +25,7 @@ std::string EncodeUint32(unsigned int u) {
|
||||
s.append(1, (u >> 0) & 0xFF);
|
||||
return s;
|
||||
}
|
||||
const uint32_t kRsaSignatureLength = 256;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -35,6 +37,7 @@ uint64_t CryptoSession::request_id_index_ = 0;
|
||||
|
||||
CryptoSession::CryptoSession()
|
||||
: open_(false),
|
||||
update_usage_table_after_close_session_(false),
|
||||
is_destination_buffer_type_valid_(false),
|
||||
requested_security_level_(kLevelDefault),
|
||||
request_id_base_(0) {
|
||||
@@ -64,7 +67,11 @@ void CryptoSession::Init() {
|
||||
void CryptoSession::Terminate() {
|
||||
LOGV("CryptoSession::Terminate");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
session_count_ -= 1;
|
||||
if (session_count_ > 0) {
|
||||
session_count_ -= 1;
|
||||
} else {
|
||||
LOGE("CryptoSession::Terminate error, session count: %d", session_count_);
|
||||
}
|
||||
if (session_count_ > 0 || !initialized_) return;
|
||||
OEMCryptoResult sts = OEMCrypto_Terminate();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
@@ -168,8 +175,6 @@ bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("CryptoSession::GetApiVersion: Lock");
|
||||
*version = OEMCrypto_APIVersion(requested_security_level_);
|
||||
return true;
|
||||
}
|
||||
@@ -242,9 +247,15 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
||||
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
|
||||
open_ = true;
|
||||
} else if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) {
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
|
||||
session_count_, (int)initialized_);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
}
|
||||
if (!open_) return UNKNOWN_ERROR;
|
||||
if (!open_) {
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
|
||||
session_count_, (int)initialized_);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
OEMCrypto_GetRandom(reinterpret_cast<uint8_t*>(&request_id_base_),
|
||||
sizeof(request_id_base_));
|
||||
@@ -259,6 +270,11 @@ void CryptoSession::Close() {
|
||||
if (!open_) return;
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) {
|
||||
open_ = false;
|
||||
if (update_usage_table_after_close_session_) {
|
||||
OEMCryptoResult sts = OEMCrypto_UpdateUsageTable();
|
||||
if (sts != OEMCrypto_SUCCESS)
|
||||
LOGW("CryptoSession::Close: OEMCrypto_UpdateUsageTable error=%ld", sts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,13 +426,21 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
provider_session_token.length());
|
||||
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
if (!provider_session_token.empty()) {
|
||||
update_usage_table_after_close_session_ = true;
|
||||
sts = OEMCrypto_UpdateUsageTable();
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
LOGW("CryptoSession::LoadKeys: OEMCrypto_UpdateUsageTable error=%ld",
|
||||
sts);
|
||||
}
|
||||
}
|
||||
return KEY_ADDED;
|
||||
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
} else {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return KEY_ERROR;
|
||||
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return LOAD_KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +454,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
wrapped_key.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
|
||||
LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -500,7 +524,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) {
|
||||
enc_deriv_message.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
|
||||
LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -524,7 +548,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
enc_deriv_message.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
|
||||
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -545,7 +569,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -558,7 +582,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -574,6 +598,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
|
||||
signature->resize(kRsaSignatureLength);
|
||||
size_t length = signature->size();
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
@@ -583,7 +608,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -596,7 +621,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
&length, kSign_RSASSA_PSS);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -612,18 +637,6 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// Check if key needs to be selected
|
||||
if (params.is_encrypted) {
|
||||
if (key_id_ != *params.key_id) {
|
||||
if (SelectKey(*params.key_id)) {
|
||||
key_id_ = *params.key_id;
|
||||
} else {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor;
|
||||
buffer_descriptor.type =
|
||||
params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
|
||||
@@ -647,10 +660,29 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
break;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCTR(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (!params.is_encrypted) {
|
||||
sts = OEMCrypto_CopyBuffer(requested_security_level_,
|
||||
params.encrypt_buffer, params.encrypt_length,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
}
|
||||
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// Check if key needs to be selected
|
||||
if (params.is_encrypted) {
|
||||
if (key_id_ != *params.key_id) {
|
||||
if (SelectKey(*params.key_id)) {
|
||||
key_id_ = *params.key_id;
|
||||
} else {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
sts = OEMCrypto_DecryptCTR(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
@@ -668,8 +700,7 @@ bool CryptoSession::UsageInformationSupport(bool* has_support) {
|
||||
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return false;
|
||||
|
||||
*has_support = OEMCrypto_SupportsUsageTable(
|
||||
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
|
||||
*has_support = OEMCrypto_SupportsUsageTable(requested_security_level_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -686,14 +717,9 @@ CdmResponseType CryptoSession::UpdateUsageInformation() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report) {
|
||||
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
if (NULL == usage_report) {
|
||||
LOGE("usage_report parameter is null");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
CdmResponseType CryptoSession::DeactivateUsageInformation(
|
||||
const std::string& provider_session_token) {
|
||||
LOGV("DeactivateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
uint8_t* pst = reinterpret_cast<uint8_t*>(
|
||||
@@ -702,20 +728,44 @@ CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
OEMCryptoResult status =
|
||||
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Deactivate Usage Entry error=%ld",
|
||||
status);
|
||||
switch (status) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
return NO_ERROR;
|
||||
case OEMCrypto_ERROR_INVALID_CONTEXT:
|
||||
LOGE("CryptoSession::DeactivateUsageInformation: invalid context error");
|
||||
return KEY_CANCELED;
|
||||
default:
|
||||
LOGE("CryptoSession::DeactivateUsageInformation: error=%ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report,
|
||||
UsageDurationStatus* usage_duration_status, int64_t* seconds_since_started,
|
||||
int64_t* seconds_since_last_played) {
|
||||
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
if (NULL == usage_report) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: usage_report parameter is null");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
size_t usage_length = 0;
|
||||
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
|
||||
provider_session_token.length(), NULL,
|
||||
&usage_length);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
uint8_t* pst = reinterpret_cast<uint8_t*>(
|
||||
const_cast<char*>(provider_session_token.data()));
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
size_t usage_length = 0;
|
||||
OEMCryptoResult status = OEMCrypto_ReportUsage(
|
||||
oec_session_id_, pst, provider_session_token.length(), NULL,
|
||||
&usage_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld",
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
usage_report->resize(usage_length);
|
||||
@@ -730,10 +780,49 @@ CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (usage_length < usage_report->length()) {
|
||||
if (usage_length != usage_report->length()) {
|
||||
usage_report->resize(usage_length);
|
||||
}
|
||||
|
||||
OEMCrypto_PST_Report pst_report;
|
||||
*usage_duration_status = kUsageDurationsInvalid;
|
||||
if (usage_length < sizeof(pst_report)) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: usage report too small=%ld",
|
||||
usage_length);
|
||||
return NO_ERROR; // usage report available but no duration information
|
||||
}
|
||||
|
||||
memcpy(&pst_report, usage_report->data(), sizeof(pst_report));
|
||||
if (kUnused == pst_report.status) {
|
||||
*usage_duration_status = kUsageDurationPlaybackNotBegun;
|
||||
return NO_ERROR;
|
||||
}
|
||||
LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status);
|
||||
LOGV("OEMCrypto_PST_Report.clock_security_level: %d\n",
|
||||
pst_report.clock_security_level);
|
||||
LOGV("OEMCrypto_PST_Report.pst_length: %d\n", pst_report.pst_length);
|
||||
LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding);
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_license_received));
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_first_decrypt));
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_last_decrypt));
|
||||
LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str());
|
||||
|
||||
// When usage report state is inactive, we have to deduce whether the
|
||||
// license was ever used.
|
||||
if (kInactive == pst_report.status &&
|
||||
(0 > ntohll64(pst_report.seconds_since_first_decrypt) ||
|
||||
ntohll64(pst_report.seconds_since_license_received) <
|
||||
ntohll64(pst_report.seconds_since_first_decrypt))) {
|
||||
*usage_duration_status = kUsageDurationPlaybackNotBegun;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
*usage_duration_status = kUsageDurationsValid;
|
||||
*seconds_since_started = ntohll64(pst_report.seconds_since_first_decrypt);
|
||||
*seconds_since_last_played = ntohll64(pst_report.seconds_since_last_decrypt);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -755,10 +844,81 @@ CdmResponseType CryptoSession::ReleaseUsageInformation(
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGW("CryptoSession::ReleaseUsageInformation: update table error=%ld",
|
||||
status);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteUsageInformation(
|
||||
const std::string& provider_session_token) {
|
||||
CdmResponseType response = NO_ERROR;
|
||||
LOGV("CryptoSession::DeleteUsageInformation");
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_token.c_str()),
|
||||
provider_session_token.length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: Delete Usage Table error =%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
LOGV("CryptoSession::DeleteMultipleUsageInformation");
|
||||
CdmResponseType response = NO_ERROR;
|
||||
for (size_t i=0; i < provider_session_tokens.size(); ++i) {
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_tokens[i].c_str()),
|
||||
provider_session_tokens[i].length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("CryptoSession::DeleteMultipleUsageInformation: "
|
||||
"Delete Usage Table error =%ld", status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteMultipleUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteAllUsageReports() {
|
||||
LOGV("DeleteAllUsageReports");
|
||||
OEMCryptoResult status = OEMCrypto_DeleteUsageTable();
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error =%ld",
|
||||
status);
|
||||
}
|
||||
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeletaAllUsageReports: update table error=%ld",
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CryptoSession::IsAntiRollbackHwPresent() {
|
||||
return OEMCrypto_IsAntiRollbackHwPresent(requested_security_level_);
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
if (!nonce) {
|
||||
LOGE("input parameter is null");
|
||||
@@ -796,7 +956,7 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
const std::string& enc_rsa_key,
|
||||
const std::string& rsa_key_iv,
|
||||
std::string* wrapped_rsa_key) {
|
||||
LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
|
||||
LOGV("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
|
||||
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
@@ -841,20 +1001,20 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetHdcpCapabilities(OemCryptoHdcpVersion* current_version,
|
||||
OemCryptoHdcpVersion* max_version) {
|
||||
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max) {
|
||||
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return UNKNOWN_ERROR;
|
||||
OEMCrypto_HDCP_Capability current, max;
|
||||
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(¤t, &max);
|
||||
|
||||
if (!initialized_) return false;
|
||||
if (current == NULL || max == NULL) {
|
||||
LOGE("CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(
|
||||
requested_security_level_, current, max);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*current_version = static_cast<OemCryptoHdcpVersion>(current);
|
||||
*max_version = static_cast<OemCryptoHdcpVersion>(max);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -873,4 +1033,42 @@ bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
bool CryptoSession::GetNumberOfOpenSessions(size_t* count) {
|
||||
LOGV("GetNumberOfOpenSessions");
|
||||
if (!initialized_) return false;
|
||||
if (count == NULL) {
|
||||
LOGE("CryptoSession::GetNumberOfOpenSessions: |count| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sessions_count;
|
||||
OEMCryptoResult status = OEMCrypto_GetNumberOfOpenSessions(
|
||||
requested_security_level_, &sessions_count);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetNumberOfOpenSessions fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*count = sessions_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetMaxNumberOfSessions(size_t* max) {
|
||||
LOGV("GetMaxNumberOfSessions");
|
||||
if (!initialized_) return false;
|
||||
if (max == NULL) {
|
||||
LOGE("CryptoSession::GetMaxNumberOfSessions: |max| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t max_sessions;
|
||||
OEMCryptoResult status = OEMCrypto_GetMaxNumberOfSessions(
|
||||
requested_security_level_, &max_sessions);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetMaxNumberOfSessions fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*max = max_sessions;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,15 +2,7 @@
|
||||
|
||||
#include "device_files.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#define SHA256 CC_SHA256
|
||||
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "device_files.pb.h"
|
||||
@@ -18,6 +10,16 @@
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#define SHA256 CC_SHA256
|
||||
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#endif
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
@@ -25,22 +27,25 @@ using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
using video_widevine_client::sdk::NameValue;
|
||||
using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kUsageInfoFileName[] = "usage.bin";
|
||||
const char kCertificateFileNamePrefix[] = "cert";
|
||||
const char kCertificateFileNameExt[] = ".bin";
|
||||
const char kUsageInfoFileNamePrefix[] = "usage";
|
||||
const char kUsageInfoFileNameExt[] = ".bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
const char kEmptyFileName[] = "";
|
||||
const char kWildcard[] = "*";
|
||||
const char kDirectoryDelimiter = '/';
|
||||
const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
|
||||
const char* kSecurityLevelPathCompatibilityExclusionList[] = {
|
||||
"ay64.dat", "ay64.dat2", "ay64.dat3"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
// Some platforms cannot store a truly blank file, so we use a W for Widevine.
|
||||
const char kBlankFileData[] = "W";
|
||||
|
||||
bool Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
@@ -53,10 +58,13 @@ bool Hash(const std::string& data, std::string* hash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// static
|
||||
std::set<std::string> DeviceFiles::reserved_license_ids_;
|
||||
|
||||
DeviceFiles::DeviceFiles()
|
||||
: file_(NULL),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
@@ -68,14 +76,10 @@ DeviceFiles::~DeviceFiles() {
|
||||
}
|
||||
|
||||
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
switch (security_level) {
|
||||
case kSecurityLevelL1:
|
||||
case kSecurityLevelL2:
|
||||
case kSecurityLevelL3:
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
||||
return false;
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
|
||||
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
||||
return false;
|
||||
}
|
||||
if (!test_file_) file_.reset(new File());
|
||||
security_level_ = security_level;
|
||||
@@ -83,7 +87,8 @@ bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
bool DeviceFiles::StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreCertificate: not initialized");
|
||||
@@ -103,10 +108,11 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFileWithHash(kCertificateFileName, serialized_file);
|
||||
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
std::string* wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
|
||||
@@ -118,7 +124,9 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(GetCertificateFileName(origin), &serialized_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -148,14 +156,32 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url) {
|
||||
bool DeviceFiles::HasCertificate(const std::string& origin) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::HasCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return FileExists(GetCertificateFileName(origin));
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RemoveCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return RemoveFile(GetCertificateFileName(origin));
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(
|
||||
const std::string& key_set_id, const LicenseState state,
|
||||
const CdmInitData& pssh_data, const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url, int64_t playback_start_time,
|
||||
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreLicense: not initialized");
|
||||
return false;
|
||||
@@ -186,29 +212,38 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
license->set_renewal_request(license_renewal_request);
|
||||
license->set_renewal(license_renewal);
|
||||
license->set_release_server_url(release_server_url);
|
||||
license->set_playback_start_time(playback_start_time);
|
||||
license->set_last_playback_time(last_playback_time);
|
||||
NameValue* app_params;
|
||||
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
|
||||
iter != app_parameters.end(); ++iter) {
|
||||
app_params = license->add_app_parameters();
|
||||
app_params->set_name(iter->first);
|
||||
app_params->set_value(iter->second);
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFileWithHash(file_name.c_str(), serialized_file);
|
||||
reserved_license_ids_.erase(key_set_id);
|
||||
return StoreFileWithHash(key_set_id + kLicenseFileNameExt, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request,
|
||||
CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url) {
|
||||
bool DeviceFiles::RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url, int64_t* playback_start_time,
|
||||
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(key_set_id + kLicenseFileNameExt, &serialized_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -252,6 +287,12 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
*license_renewal_request = license.renewal_request();
|
||||
*license_renewal = license.renewal();
|
||||
*release_server_url = license.release_server_url();
|
||||
*playback_start_time = license.playback_start_time();
|
||||
*last_playback_time = license.last_playback_time();
|
||||
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
||||
(*app_parameters)[license.app_parameters(i).name()] =
|
||||
license.app_parameters(i).value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -260,16 +301,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::DeleteLicense: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteLicense: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Remove(path);
|
||||
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllLicenses() {
|
||||
@@ -277,16 +309,7 @@ bool DeviceFiles::DeleteAllLicenses() {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(kWildcard);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Remove(path);
|
||||
return RemoveFile(std::string(kWildcard) + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllFiles() {
|
||||
@@ -295,13 +318,9 @@ bool DeviceFiles::DeleteAllFiles() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteAllFiles: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_->Remove(path);
|
||||
// We pass an empty string to RemoveFile to delete the device files base
|
||||
// directory itself.
|
||||
return RemoveFile(kEmptyFileName);
|
||||
}
|
||||
|
||||
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
@@ -309,16 +328,8 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::LicenseExists: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::LicenseExists: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Exists(path);
|
||||
return reserved_license_ids_.count(key_set_id) ||
|
||||
FileExists(key_set_id + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
||||
@@ -326,14 +337,14 @@ bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFileRaw(file_name.c_str(), kBlankFileData);
|
||||
reserved_license_ids_.insert(key_set_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response) {
|
||||
const CdmKeyResponse& key_response,
|
||||
const std::string& app_id) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
|
||||
return false;
|
||||
@@ -341,7 +352,8 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
|
||||
std::string serialized_file;
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
} else {
|
||||
@@ -360,17 +372,18 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
provider_session->set_license(key_response.data(), key_response.size());
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(file_name, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) return false;
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -382,8 +395,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (; index < usage_info->sessions_size(); ++index) {
|
||||
if (usage_info->sessions(index).token().compare(provider_session_token) ==
|
||||
0) {
|
||||
if (usage_info->sessions(index).token() == provider_session_token) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -405,26 +417,43 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
sessions->RemoveLast();
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(file_name, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo() {
|
||||
bool DeviceFiles::DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
|
||||
if (NULL == provider_session_tokens) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: pst destination not provided");
|
||||
return false;
|
||||
}
|
||||
path.append(kUsageInfoFileName);
|
||||
provider_session_tokens->clear();
|
||||
|
||||
return file_->Remove(path);
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!FileExists(file_name)) return true;
|
||||
std::string serialized_file;
|
||||
if (RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
video_widevine_client::sdk::File file_proto;
|
||||
if (!file_proto.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to parse file");
|
||||
} else {
|
||||
for (int i = 0; i < file_proto.usage_info().sessions_size(); ++i) {
|
||||
provider_session_tokens->push_back(
|
||||
file_proto.usage_info().sessions(i).token());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
|
||||
}
|
||||
return RemoveFile(file_name);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveUsageInfo(
|
||||
const std::string& app_id,
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
@@ -439,15 +468,9 @@ bool DeviceFiles::RetrieveUsageInfo(
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path += kUsageInfoFileName;
|
||||
|
||||
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
||||
usage_info->resize(0);
|
||||
return true;
|
||||
}
|
||||
@@ -471,15 +494,45 @@ bool DeviceFiles::RetrieveUsageInfo(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_response) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
std::string serialized_file;
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (; index < file.usage_info().sessions_size(); ++index) {
|
||||
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Unspecified file name parameter");
|
||||
*license_request = file.usage_info().sessions(index).license_request();
|
||||
*license_response = file.usage_info().sessions(index).license();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -501,18 +554,13 @@ bool DeviceFiles::StoreFileWithHash(const char* name,
|
||||
return StoreFileRaw(name, serialized_hash_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileRaw(const char* name,
|
||||
bool DeviceFiles::StoreFileRaw(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
|
||||
@@ -546,18 +594,13 @@ bool DeviceFiles::StoreFileRaw(const char* name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
std::string* serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!serialized_file) {
|
||||
LOGW(
|
||||
"DeviceFiles::RetrieveHashedFile: Unspecified serialized_file "
|
||||
@@ -582,6 +625,9 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
if (bytes <= 0) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
|
||||
path.c_str());
|
||||
// Remove the corrupted file so the caller will not get the same error
|
||||
// when trying to access the file repeatedly, causing the system to stall.
|
||||
file_->Remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -614,8 +660,11 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hash_file.hash())) {
|
||||
if (hash != hash_file.hash()) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
|
||||
// Remove the corrupted file so the caller will not get the same error
|
||||
// when trying to access the file repeatedly, causing the system to stall.
|
||||
file_->Remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -623,6 +672,54 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::FileExists(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::FileExists: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::FileExists: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveFile(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RemoveFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->Remove(path);
|
||||
}
|
||||
|
||||
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::GetFileSize: Invalid file handle");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::GetFileSize: Unable to get base path");
|
||||
return -1;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->FileSize(path);
|
||||
}
|
||||
|
||||
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
@@ -687,17 +784,33 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
}
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetCertificateFileName() {
|
||||
return kCertificateFileName;
|
||||
std::string DeviceFiles::GetCertificateFileName(const std::string& origin) {
|
||||
std::string hash;
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
hash = GetFileNameSafeHash(origin);
|
||||
}
|
||||
return kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetLicenseFileNameExtension() {
|
||||
return kLicenseFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetUsageInfoFileName() { return kUsageInfoFileName; }
|
||||
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
|
||||
std::string hash;
|
||||
if (app_id != "") {
|
||||
hash = GetFileNameSafeHash(app_id);
|
||||
}
|
||||
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
|
||||
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
||||
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
|
||||
const unsigned char* input_ptr =
|
||||
reinterpret_cast<const unsigned char*>(input.data());
|
||||
MD5(input_ptr, input.size(), &hash[0]);
|
||||
return wvcdm::Base64SafeEncode(hash);
|
||||
}
|
||||
|
||||
void DeviceFiles::SetTestFile(File* file) {
|
||||
file_.reset(file);
|
||||
|
||||
@@ -13,6 +13,11 @@ package video_widevine_client.sdk;
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
message DeviceCertificate {
|
||||
optional bytes certificate = 1;
|
||||
optional bytes wrapped_private_key = 2;
|
||||
@@ -31,6 +36,9 @@ message License {
|
||||
optional bytes renewal_request = 5;
|
||||
optional bytes renewal = 6;
|
||||
optional bytes release_server_url = 7;
|
||||
optional int64 playback_start_time = 8 [default = 0];
|
||||
optional int64 last_playback_time = 9 [default = 0];
|
||||
repeated NameValue app_parameters = 10;
|
||||
}
|
||||
|
||||
message UsageInfo {
|
||||
|
||||
@@ -38,76 +38,122 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
|
||||
init_data.length());
|
||||
|
||||
// TODO(kqyang): Extracted from an actual init_data;
|
||||
// Need to find out where it comes from.
|
||||
// Widevine's registered system ID.
|
||||
static const uint8_t kWidevineSystemId[] = {
|
||||
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
|
||||
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
|
||||
};
|
||||
|
||||
// one PSSH blob consists of:
|
||||
// 4 byte size of the PSSH atom, inclusive
|
||||
// "pssh"
|
||||
// 4 byte flags, value 0
|
||||
// 16 byte system id
|
||||
// 4 byte size of PSSH data, exclusive
|
||||
// one PSSH box consists of:
|
||||
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
|
||||
// 4 byte atom type, "pssh".
|
||||
// (optional, if size == 1) 8 byte size of the atom, inclusive.
|
||||
// 1 byte version, value 0 or 1. (skip if larger.)
|
||||
// 3 byte flags, value 0. (ignored.)
|
||||
// 16 byte system id.
|
||||
// (optional, if version == 1) 4 byte key ID count. (K)
|
||||
// (optional, if version == 1) K * 16 byte key ID.
|
||||
// 4 byte size of PSSH data, exclusive. (N)
|
||||
// N byte PSSH data.
|
||||
while (1) {
|
||||
// size of PSSH atom, used for skipping
|
||||
uint32_t size;
|
||||
if (!reader.Read4(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
|
||||
size_t start_pos = reader.pos();
|
||||
|
||||
// atom size, used for skipping.
|
||||
uint64_t size;
|
||||
if (!reader.Read4Into8(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
if (!reader.Read8(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
|
||||
"size.");
|
||||
return false;
|
||||
}
|
||||
} else if (size == 0) {
|
||||
size = reader.size() - start_pos;
|
||||
}
|
||||
|
||||
// "pssh"
|
||||
std::vector<uint8_t> pssh;
|
||||
if (!reader.ReadVec(&pssh, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
|
||||
return false;
|
||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(&pssh[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
|
||||
|
||||
// version
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// flags
|
||||
uint32_t flags;
|
||||
if (!reader.Read4(&flags)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
|
||||
return false;
|
||||
if (version > 1) {
|
||||
// unrecognized version - skip.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (flags != 0) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
|
||||
|
||||
// flags
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||
// skip the remaining contents of the atom,
|
||||
// after size field, atom name, flags and system id
|
||||
if (!reader.SkipBytes(size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
|
||||
// skip non-Widevine PSSH boxes.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// size of PSSH box
|
||||
uint32_t pssh_length;
|
||||
if (!reader.Read4(&pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
|
||||
if (version == 1) {
|
||||
// v1 has additional fields for key IDs. We can skip them.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
|
||||
return false;
|
||||
}
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// size of PSSH data
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
1405
core/src/license.cpp
1405
core/src/license.cpp
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,10 @@ message License {
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool renew_with_client_id = 12 [default = false];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
@@ -167,6 +171,8 @@ message License {
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
@@ -174,7 +180,13 @@ message License {
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
@@ -286,36 +298,6 @@ message SignedMessage {
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
}
|
||||
|
||||
// This message is used to pass optional data on initial license issuance.
|
||||
message SessionInit {
|
||||
optional bytes session_id = 1;
|
||||
optional bytes purchase_id = 2;
|
||||
// master_signing_key should be 128 bits in length.
|
||||
optional bytes master_signing_key = 3;
|
||||
// signing_key should be 512 bits in length to be split into two
|
||||
// (server || client) HMAC-SHA256 keys.
|
||||
optional bytes signing_key = 4;
|
||||
optional int64 license_start_time = 5;
|
||||
// Client token for the session. This session is for use by the license
|
||||
// provider, and is akin to a client cookie. It will be copied to
|
||||
// License::provider_client_token, and sent back by the client in
|
||||
// ClientIdentification::provider_client_token in all license requests
|
||||
// thereafter.
|
||||
optional bytes provider_client_token = 6;
|
||||
// Session token for the session. This token is for use by the license
|
||||
// provider, and is akin to a session cookie. It will be copied to
|
||||
// LicenseIdentfication::provider_session_token, and sent back in all
|
||||
// license renewal and release requests for the session thereafter.
|
||||
optional bytes provider_session_token = 7;
|
||||
}
|
||||
|
||||
// This message is used by the server to preserve and restore session state.
|
||||
message SessionState {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional bytes signing_key = 2;
|
||||
optional uint32 keybox_system_id = 3;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -348,6 +330,9 @@ message ProvisioningRequest {
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// Required if doing per-origin provisioning.
|
||||
optional bytes stable_id = 4;
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
@@ -410,6 +395,7 @@ message ClientIdentification {
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
|
||||
173
core/src/max_res_engine.cpp
Normal file
173
core/src/max_res_engine.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "max_res_engine.h"
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kHdcpCheckInterval = 10;
|
||||
const uint32_t kNoResolution = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
MaxResEngine::MaxResEngine(CryptoSession* crypto_session) {
|
||||
Init(crypto_session, new Clock());
|
||||
}
|
||||
|
||||
MaxResEngine::MaxResEngine(CryptoSession* crypto_session, Clock* clock) {
|
||||
Init(crypto_session, clock);
|
||||
}
|
||||
|
||||
MaxResEngine::~MaxResEngine() {
|
||||
AutoLock lock(status_lock_);
|
||||
DeleteAllKeys();
|
||||
}
|
||||
|
||||
bool MaxResEngine::CanDecrypt(const KeyId& key_id) {
|
||||
AutoLock lock(status_lock_);
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->can_decrypt();
|
||||
} else {
|
||||
// If a Key ID is unknown to us, we don't know of any constraints for it,
|
||||
// so never block decryption.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::Init(CryptoSession* crypto_session, Clock* clock) {
|
||||
AutoLock lock(status_lock_);
|
||||
current_resolution_ = kNoResolution;
|
||||
clock_.reset(clock);
|
||||
next_check_time_ = clock_->GetCurrentTime();
|
||||
crypto_session_ = crypto_session;
|
||||
}
|
||||
|
||||
void MaxResEngine::SetLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
AutoLock lock(status_lock_);
|
||||
DeleteAllKeys();
|
||||
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const KeyContainer& key = license.key(key_index);
|
||||
if (key.type() == KeyContainer::CONTENT && key.has_id() &&
|
||||
key.video_resolution_constraints_size() > 0) {
|
||||
const ConstraintList& constraints = key.video_resolution_constraints();
|
||||
const KeyId& key_id = key.id();
|
||||
if (key.has_required_protection()) {
|
||||
keys_[key_id] =
|
||||
new KeyStatus(constraints, key.required_protection().hdcp());
|
||||
} else {
|
||||
keys_[key_id] = new KeyStatus(constraints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::SetResolution(uint32_t width, uint32_t height) {
|
||||
AutoLock lock(status_lock_);
|
||||
current_resolution_ = width * height;
|
||||
}
|
||||
|
||||
void MaxResEngine::OnTimerEvent() {
|
||||
AutoLock lock(status_lock_);
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (!keys_.empty() && current_resolution_ != kNoResolution &&
|
||||
current_time >= next_check_time_) {
|
||||
CryptoSession::HdcpCapability current_hdcp_level;
|
||||
CryptoSession::HdcpCapability ignored;
|
||||
if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) {
|
||||
current_hdcp_level = HDCP_NONE;
|
||||
}
|
||||
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) {
|
||||
i->second->Update(current_resolution_, current_hdcp_level);
|
||||
}
|
||||
next_check_time_ = current_time + kHdcpCheckInterval;
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::DeleteAllKeys() {
|
||||
// This helper method assumes that status_lock_ is already held.
|
||||
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) delete i->second;
|
||||
keys_.clear();
|
||||
}
|
||||
|
||||
MaxResEngine::KeyStatus::KeyStatus(const ConstraintList& constraints)
|
||||
: default_hdcp_level_(HDCP_NONE) {
|
||||
Init(constraints);
|
||||
}
|
||||
|
||||
MaxResEngine::KeyStatus::KeyStatus(
|
||||
const ConstraintList& constraints,
|
||||
const OutputProtection::HDCP& default_hdcp_level)
|
||||
: default_hdcp_level_(ProtobufHdcpToOemCryptoHdcp(default_hdcp_level)) {
|
||||
Init(constraints);
|
||||
}
|
||||
|
||||
void MaxResEngine::KeyStatus::Init(const ConstraintList& constraints) {
|
||||
constraints_.Clear();
|
||||
constraints_.MergeFrom(constraints);
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
|
||||
void MaxResEngine::KeyStatus::Update(
|
||||
uint32_t res, CryptoSession::HdcpCapability current_hdcp_level) {
|
||||
VideoResolutionConstraint* current_constraint = GetConstraintForRes(res);
|
||||
|
||||
if (current_constraint == NULL) {
|
||||
can_decrypt_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability desired_hdcp_level;
|
||||
if (current_constraint->has_required_protection()) {
|
||||
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
|
||||
current_constraint->required_protection().hdcp());
|
||||
} else {
|
||||
desired_hdcp_level = default_hdcp_level_;
|
||||
}
|
||||
can_decrypt_ = (current_hdcp_level >= desired_hdcp_level);
|
||||
}
|
||||
|
||||
MaxResEngine::VideoResolutionConstraint*
|
||||
MaxResEngine::KeyStatus::GetConstraintForRes(uint32_t res) {
|
||||
typedef ConstraintList::pointer_iterator Iterator;
|
||||
for (Iterator i = constraints_.pointer_begin();
|
||||
i != constraints_.pointer_end(); ++i) {
|
||||
VideoResolutionConstraint* constraint = *i;
|
||||
if (constraint->has_min_resolution_pixels() &&
|
||||
constraint->has_max_resolution_pixels() &&
|
||||
res >= constraint->min_resolution_pixels() &&
|
||||
res <= constraint->max_resolution_pixels()) {
|
||||
return constraint;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability
|
||||
MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp(
|
||||
const OutputProtection::HDCP& input) {
|
||||
switch (input) {
|
||||
case OutputProtection::HDCP_NONE:
|
||||
return HDCP_NONE;
|
||||
case OutputProtection::HDCP_V1:
|
||||
return HDCP_V1;
|
||||
case OutputProtection::HDCP_V2:
|
||||
return HDCP_V2;
|
||||
case OutputProtection::HDCP_V2_1:
|
||||
return HDCP_V2_1;
|
||||
case OutputProtection::HDCP_V2_2:
|
||||
return HDCP_V2_2;
|
||||
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
default:
|
||||
LOGE("MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp: "
|
||||
"Unknown HDCP Level");
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
}
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
@@ -5,7 +5,6 @@
|
||||
// compile time.
|
||||
//
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -15,6 +14,12 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
return ::OEMCrypto_OpenSession(session);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsKeyboxValid();
|
||||
}
|
||||
@@ -29,12 +34,6 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
@@ -43,8 +42,34 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
|
||||
OEMCrypto_HDCP_Capability* maximum) {
|
||||
return ::OEMCrypto_GetHDCPCapability(current, maximum);
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsAntiRollbackHwPresent();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
size_t* count) {
|
||||
return ::OEMCrypto_GetNumberOfOpenSessions(count);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum) {
|
||||
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
|
||||
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
|
||||
subsample_flags);
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
62
core/src/oemcrypto_adapter_static_v10.cpp
Normal file
62
core/src/oemcrypto_adapter_static_v10.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
|
||||
// This should be used when liboemcrypto.so is linked with the CDM code at
|
||||
// compile time.
|
||||
//
|
||||
// Defines APIs introduced in newer version (v10) which is not available in v9
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability_V9(uint8_t* current,
|
||||
uint8_t* maximum);
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadTestRSAKey() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_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) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
|
||||
if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
uint8_t current_byte, maximum_byte;
|
||||
OEMCryptoResult sts = OEMCrypto_GetHDCPCapability_V9(¤t_byte,
|
||||
&maximum_byte);
|
||||
*current = static_cast<OEMCrypto_HDCP_Capability>(current_byte);
|
||||
*maximum = static_cast<OEMCrypto_HDCP_Capability>(maximum_byte);
|
||||
return sts;
|
||||
}
|
||||
|
||||
extern "C" bool OEMCrypto_IsAntiRollbackHwPresent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
|
||||
* This should be used when liboemcrypto.so is linked with the CDM code at
|
||||
* compile time.
|
||||
* An implementation should compile either oemcrypto_adapter_dynamic.cpp or
|
||||
* oemcrypto_adapter_static.cpp, but not both.
|
||||
* This version contains shim code to allow an older, version 8 API, oemcrypto,
|
||||
* to be linked with CDM.
|
||||
*
|
||||
******************************************************************************/
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
|
||||
// This should be used when liboemcrypto.so is linked with the CDM code at
|
||||
// compile time.
|
||||
//
|
||||
// An implementation should compile either oemcrypto_adapter_dynamic.cpp or
|
||||
// oemcrypto_adapter_static.cpp, but not both.
|
||||
//
|
||||
// Defines APIs introduced in newer version (v9) which is not available in v8
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
@@ -25,45 +22,6 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_OpenSession(session);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsKeyboxValid();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
@@ -113,4 +71,3 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
size_t signature_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}; // namespace wvcdm
|
||||
@@ -13,48 +13,60 @@
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
using video_widevine_server::sdk::License;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine() { Init(new Clock()); }
|
||||
PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
WvCdmEventListener* event_listener,
|
||||
CryptoSession* crypto_session)
|
||||
: license_state_(kLicenseStateInitial),
|
||||
license_start_time_(0),
|
||||
playback_start_time_(0),
|
||||
last_playback_time_(0),
|
||||
last_expiry_time_(0),
|
||||
last_expiry_time_set_(false),
|
||||
next_renewal_time_(0),
|
||||
policy_max_duration_seconds_(0),
|
||||
session_id_(session_id),
|
||||
event_listener_(event_listener),
|
||||
max_res_engine_(new MaxResEngine(crypto_session)),
|
||||
clock_(new Clock) {}
|
||||
|
||||
PolicyEngine::PolicyEngine(Clock* clock) { Init(clock); }
|
||||
PolicyEngine::~PolicyEngine() {}
|
||||
|
||||
PolicyEngine::~PolicyEngine() {
|
||||
if (clock_) delete clock_;
|
||||
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
|
||||
if (keys_status_.find(key_id) == keys_status_.end()) {
|
||||
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
|
||||
b2a_hex(key_id).c_str());
|
||||
return false;
|
||||
}
|
||||
return keys_status_[key_id] == kKeyStatusUsable;
|
||||
}
|
||||
|
||||
void PolicyEngine::Init(Clock* clock) {
|
||||
license_state_ = kLicenseStateInitial;
|
||||
can_decrypt_ = false;
|
||||
license_start_time_ = 0;
|
||||
playback_start_time_ = 0;
|
||||
next_renewal_time_ = 0;
|
||||
policy_max_duration_seconds_ = 0;
|
||||
clock_ = clock;
|
||||
}
|
||||
|
||||
void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
*event_occurred = false;
|
||||
void PolicyEngine::OnTimerEvent() {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
// License expiration trumps all.
|
||||
if ((IsLicenseDurationExpired(current_time) ||
|
||||
IsPlaybackDurationExpired(current_time)) &&
|
||||
if (IsLicenseOrPlaybackDurationExpired(current_time) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
*event = LICENSE_EXPIRED_EVENT;
|
||||
*event_occurred = true;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
bool renewal_needed = false;
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
// HDCP may change, so force a check.
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -71,7 +83,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
case kLicenseStatePending: {
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -83,28 +95,46 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
|
||||
default: {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
NotifyKeysChange(kKeyStatusInternalError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (renewal_needed) {
|
||||
UpdateRenewalRequest(current_time);
|
||||
*event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
*event_occurred = true;
|
||||
if (event_listener_) event_listener_->OnSessionRenewalNeeded(session_id_);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
void PolicyEngine::SetLicense(const License& license) {
|
||||
license_id_.Clear();
|
||||
license_id_.CopyFrom(license.id());
|
||||
policy_.Clear();
|
||||
|
||||
// Extract content key ids.
|
||||
keys_status_.clear();
|
||||
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const License::KeyContainer& key = license.key(key_index);
|
||||
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
|
||||
keys_status_[key.id()] = kKeyStatusInternalError;
|
||||
}
|
||||
|
||||
UpdateLicense(license);
|
||||
max_res_engine_->SetLicense(license);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
license_id_.Clear();
|
||||
license_id_.CopyFrom(license.id());
|
||||
policy_.Clear();
|
||||
|
||||
// Expire any old keys.
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
|
||||
UpdateLicense(license);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
void PolicyEngine::UpdateLicense(const License& license) {
|
||||
if (!license.has_policy()) return;
|
||||
|
||||
if (kLicenseStateExpired == license_state_) {
|
||||
@@ -144,23 +174,22 @@ void PolicyEngine::UpdateLicense(
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (!policy_.can_play()) {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (!policy_.can_play() || IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (IsLicenseDurationExpired(current_time)) return;
|
||||
if (IsPlaybackDurationExpired(current_time)) return;
|
||||
|
||||
// Update state
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
} else {
|
||||
license_state_ = kLicenseStatePending;
|
||||
can_decrypt_ = false;
|
||||
NotifyKeysChange(kKeyStatusPending);
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
}
|
||||
|
||||
void PolicyEngine::BeginDecryption() {
|
||||
@@ -170,10 +199,12 @@ void PolicyEngine::BeginDecryption() {
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
playback_start_time_ = clock_->GetCurrentTime();
|
||||
last_playback_time_ = playback_start_time_;
|
||||
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
break;
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStatePending:
|
||||
@@ -184,11 +215,27 @@ void PolicyEngine::BeginDecryption() {
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::DecryptionEvent() {
|
||||
last_playback_time_ = clock_->GetCurrentTime();
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
max_res_engine_->SetResolution(width, height);
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifySessionExpiration() {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
std::stringstream ss;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
if (license_state_ == kLicenseStateInitial) return UNKNOWN_ERROR;
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
key_info->clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING
|
||||
@@ -210,47 +257,73 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
|
||||
if (playback_start_time_ == 0) return false;
|
||||
|
||||
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
|
||||
return (*seconds_since_started >= 0) ? true : false;
|
||||
}
|
||||
|
||||
bool PolicyEngine::GetSecondsSinceLastPlayed(
|
||||
int64_t* seconds_since_last_played) {
|
||||
if (last_playback_time_ == 0) return false;
|
||||
|
||||
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
|
||||
return (*seconds_since_last_played >= 0) ? true : false;
|
||||
}
|
||||
|
||||
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
|
||||
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
|
||||
NotifyExpirationUpdate();
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
license_state_ = kLicenseStateWaitingLicenseUpdate;
|
||||
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsLicenseOrPlaybackDurationExpired(int64_t current_time) {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
return (expiry_time == NEVER_EXPIRES) ? false : (expiry_time <= current_time);
|
||||
}
|
||||
|
||||
// For the policy time fields checked in the following methods, a value of 0
|
||||
// indicates that there is no limit to the duration. These methods
|
||||
// will always return false if the value is 0.
|
||||
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
|
||||
return policy_max_duration_seconds_ &&
|
||||
license_start_time_ + policy_max_duration_seconds_ <= current_time;
|
||||
int64_t PolicyEngine::GetLicenseExpiryTime() {
|
||||
return policy_max_duration_seconds_ > 0
|
||||
? license_start_time_ + policy_max_duration_seconds_
|
||||
: NEVER_EXPIRES;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackExpiryTime() {
|
||||
return (playback_start_time_ > 0 && policy_.playback_duration_seconds() > 0)
|
||||
? (playback_start_time_ + policy_.playback_duration_seconds())
|
||||
: NEVER_EXPIRES;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
|
||||
|
||||
int64_t remaining_time =
|
||||
policy_max_duration_seconds_ + license_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
else if (remaining_time > policy_max_duration_seconds_)
|
||||
remaining_time = policy_max_duration_seconds_;
|
||||
return remaining_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
return (policy_.playback_duration_seconds() > 0) && playback_start_time_ &&
|
||||
playback_start_time_ + policy_.playback_duration_seconds() <=
|
||||
current_time;
|
||||
int64_t license_expiry_time = GetLicenseExpiryTime();
|
||||
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
|
||||
if (license_expiry_time < current_time) return 0;
|
||||
return std::min(license_expiry_time - current_time,
|
||||
policy_max_duration_seconds_);
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
|
||||
if (0 == playback_start_time_) return policy_.playback_duration_seconds();
|
||||
int64_t playback_expiry_time = GetPlaybackExpiryTime();
|
||||
if (playback_expiry_time == NEVER_EXPIRES) {
|
||||
return (policy_.playback_duration_seconds() != 0)
|
||||
? policy_.playback_duration_seconds()
|
||||
: LLONG_MAX;
|
||||
}
|
||||
|
||||
int64_t remaining_time =
|
||||
policy_.playback_duration_seconds() + playback_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0) remaining_time = 0;
|
||||
return remaining_time;
|
||||
if (playback_expiry_time < current_time) return 0;
|
||||
return std::min(playback_expiry_time - current_time,
|
||||
policy_.playback_duration_seconds());
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
@@ -271,4 +344,45 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
bool keys_changed = false;
|
||||
bool has_new_usable_key = false;
|
||||
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
|
||||
it != keys_status_.end(); ++it) {
|
||||
const KeyId key_id = it->first;
|
||||
CdmKeyStatus& key_status = it->second;
|
||||
CdmKeyStatus updated_status = new_status;
|
||||
if (updated_status == kKeyStatusUsable) {
|
||||
if (!max_res_engine_->CanDecrypt(key_id))
|
||||
updated_status = kKeyStatusOutputNotAllowed;
|
||||
}
|
||||
if (key_status != updated_status) {
|
||||
key_status = updated_status;
|
||||
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
|
||||
keys_changed = true;
|
||||
}
|
||||
}
|
||||
if (keys_changed && event_listener_) {
|
||||
event_listener_->OnSessionKeysChange(session_id_, keys_status_,
|
||||
has_new_usable_key);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyExpirationUpdate() {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
|
||||
last_expiry_time_ = expiry_time;
|
||||
if (event_listener_)
|
||||
event_listener_->OnExpirationUpdate(session_id_, expiry_time);
|
||||
}
|
||||
last_expiry_time_set_ = true;
|
||||
}
|
||||
|
||||
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
|
||||
|
||||
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
|
||||
max_res_engine_.reset(max_res_engine);
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
|
||||
@@ -108,8 +108,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
}
|
||||
|
||||
int padding = 0;
|
||||
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
@@ -180,6 +180,64 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
return true;
|
||||
}
|
||||
|
||||
// LogOpenSSLError is a callback from OpenSSL which is called with each error
|
||||
// in the thread's error queue.
|
||||
static int LogOpenSSLError(const char *msg, size_t /* len */, void */* ctx */) {
|
||||
LOGE(" %s", msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
|
||||
const std::string &signature) {
|
||||
EVP_MD_CTX ctx;
|
||||
EVP_MD_CTX_init(&ctx);
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
|
||||
if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
|
||||
pkey) != 1) {
|
||||
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, kPssSaltLength) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) {
|
||||
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyFinal(
|
||||
&ctx, const_cast<uint8_t *>(
|
||||
reinterpret_cast<const uint8_t *>(signature.data())),
|
||||
signature.size()) != 1) {
|
||||
LOGE(
|
||||
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
|
||||
"signature.)");
|
||||
goto err;
|
||||
}
|
||||
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
return true;
|
||||
|
||||
err:
|
||||
ERR_print_errors_cb(LogOpenSSLError, NULL);
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
const std::string& signature) {
|
||||
if (serialized_key_.empty()) {
|
||||
@@ -190,50 +248,25 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
|
||||
return false;
|
||||
}
|
||||
RSA* key = GetKey(serialized_key_);
|
||||
if (key == NULL) {
|
||||
RSA* rsa_key = GetKey(serialized_key_);
|
||||
if (rsa_key == NULL) {
|
||||
// Error already logged by GetKey.
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key);
|
||||
if (static_cast<int>(signature.size()) != rsa_size) {
|
||||
LOGE(
|
||||
"RsaPublicKey::VerifySignature: message signature is of the wrong "
|
||||
"size (expected %d, actual %d)",
|
||||
rsa_size, signature.size());
|
||||
FreeKey(key);
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL ||
|
||||
EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
|
||||
FreeKey(rsa_key);
|
||||
LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
|
||||
return false;
|
||||
}
|
||||
FreeKey(rsa_key);
|
||||
|
||||
// Decrypt the signature.
|
||||
std::string padded_digest(signature.size(), 0);
|
||||
if (RSA_public_decrypt(
|
||||
signature.size(),
|
||||
const_cast<unsigned char*>(
|
||||
reinterpret_cast<const unsigned char*>(signature.data())),
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]), key,
|
||||
RSA_NO_PADDING) != rsa_size) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
const bool ok = VerifyPSSSignature(pkey, message, signature);
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
// Hash the message using SHA1.
|
||||
std::string message_digest(SHA_DIGEST_LENGTH, 0);
|
||||
SHA1(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
||||
reinterpret_cast<unsigned char*>(&message_digest[0]));
|
||||
|
||||
// Verify PSS padding.
|
||||
if (RSA_verify_PKCS1_PSS(
|
||||
key, reinterpret_cast<const unsigned char*>(message_digest.data()),
|
||||
EVP_sha1(),
|
||||
reinterpret_cast<const unsigned char*>(padded_digest.data()),
|
||||
kPssSaltLength) == 0) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
if (!ok) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "log.h"
|
||||
#include "properties_configuration.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
@@ -12,31 +12,18 @@ namespace wvcdm {
|
||||
bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::oem_crypto_require_usage_tables_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||
|
||||
void Properties::Init() {
|
||||
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
|
||||
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
|
||||
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
|
||||
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
|
||||
use_certificates_as_identification_ =
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
security_level_path_backward_compatibility_support_ =
|
||||
kSecurityLevelPathBackwardCompatibilitySupport;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
}
|
||||
|
||||
bool Properties::AddSessionPropertySet(
|
||||
const CdmSessionId& session_id, const CdmClientPropertySet* property_set) {
|
||||
const CdmSessionId& session_id, CdmClientPropertySet* property_set) {
|
||||
if (NULL == session_property_set_.get()) {
|
||||
return false;
|
||||
}
|
||||
std::pair<CdmClientPropertySetMap::iterator, bool> result =
|
||||
session_property_set_->insert(
|
||||
std::pair<const CdmSessionId, const CdmClientPropertySet*>(
|
||||
std::pair<const CdmSessionId, CdmClientPropertySet*>(
|
||||
session_id, property_set));
|
||||
return result.second;
|
||||
}
|
||||
@@ -48,10 +35,10 @@ bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) {
|
||||
return (1 == session_property_set_->erase(session_id));
|
||||
}
|
||||
|
||||
const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
const CdmSessionId& session_id) {
|
||||
if (NULL != session_property_set_.get()) {
|
||||
CdmClientPropertySetMap::const_iterator it =
|
||||
CdmClientPropertySetMap::iterator it =
|
||||
session_property_set_->find(session_id);
|
||||
if (it != session_property_set_->end()) {
|
||||
return it->second;
|
||||
@@ -60,14 +47,14 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Properties::GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level) {
|
||||
bool Properties::GetApplicationId(const CdmSessionId& session_id,
|
||||
std::string* app_id) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
*security_level = property_set->security_level();
|
||||
*app_id = property_set->app_id();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +69,17 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate) {
|
||||
CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
property_set->set_service_certificate(service_certificate);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <modp_b64w.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "modp_b64w.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -43,8 +44,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
unsigned char lsb = 0; // least significant 4 bits
|
||||
if (!CharToDigit(byte[i * 2], &msb) ||
|
||||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
|
||||
LOGE("Invalid hex value %c%c at index %d",
|
||||
byte[i * 2], byte[i * 2 + 1], i);
|
||||
LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1],
|
||||
i);
|
||||
return array;
|
||||
}
|
||||
array.push_back((msb << 4) | lsb);
|
||||
@@ -56,8 +57,9 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
// dump the string with the label.
|
||||
std::vector<uint8_t> a2b_hex(const std::string& label,
|
||||
const std::string& byte) {
|
||||
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
|
||||
<< std::endl << std::endl;
|
||||
std::cout << std::endl
|
||||
<< "[[DUMP: " << label << " ]= \"" << byte << "\"]" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return a2b_hex(byte);
|
||||
}
|
||||
@@ -173,15 +175,15 @@ int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
|
||||
int64_t number;
|
||||
} mixed;
|
||||
mixed.number = 1;
|
||||
if (mixed.array[0] == 1) {
|
||||
mixed.number = x; // Little Endian.
|
||||
if (mixed.array[0] == 1) { // Little Endian.
|
||||
mixed.number = x;
|
||||
uint32_t temp = mixed.array[0];
|
||||
mixed.array[0] = htonl(mixed.array[1]);
|
||||
mixed.array[1] = htonl(temp);
|
||||
return mixed.number;
|
||||
} else {
|
||||
return x; // Big Endian.
|
||||
} else { // Big Endian.
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
// Test vectors as suggested by http://tools.ietf.org/html/rfc4648#section-10
|
||||
@@ -50,9 +53,7 @@ const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
|
||||
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
|
||||
make_pair(&kTestData, &kB64TestData)};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
} // namespace
|
||||
|
||||
class Base64EncodeDecodeTest
|
||||
: public ::testing::TestWithParam<
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
// This is because we need a valid RSA certificate, and will attempt to connect
|
||||
// to the provisioning server to request one if we don't.
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "config_test_env.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
@@ -23,6 +21,8 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
// Http OK response code.
|
||||
const int kHttpOk = 200;
|
||||
@@ -30,29 +30,45 @@ const int kHttpOk = 200;
|
||||
// Default license server, can be configured using --server command line option
|
||||
// Default key id (pssh), can be configured using --keyid command line option
|
||||
std::string g_client_auth;
|
||||
wvcdm::KeyId g_key_id_pssh;
|
||||
wvcdm::KeyId g_key_id_unwrapped;
|
||||
wvcdm::CdmKeySystem g_key_system;
|
||||
KeyId g_key_id_pssh;
|
||||
KeyId g_key_id_unwrapped;
|
||||
CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
KeyId g_wrong_key_id;
|
||||
|
||||
const std::string kCencMimeType = "video/mp4";
|
||||
const std::string kWebmMimeType = "video/webm";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WvCdmEngineTest : public testing::Test {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
ConfigTestEnv config(kContentProtectionServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
g_license_server.assign(config.license_server());
|
||||
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
|
||||
|
||||
// Extract the key ID from the PSSH box.
|
||||
InitializationData extractor(CENC_INIT_DATA_FORMAT,
|
||||
g_key_id_pssh);
|
||||
g_key_id_unwrapped = extractor.data();
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
CdmResponseType status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
CdmResponseType status =
|
||||
cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */, &session_id_);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
Provision();
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */,
|
||||
&session_id_);
|
||||
}
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id_));
|
||||
}
|
||||
|
||||
virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }
|
||||
@@ -64,19 +80,17 @@ class WvCdmEngineTest : public testing::Test {
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
std::string cert, wrapped_key;
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
cdm_engine_.GetProvisioningRequest(cert_type,
|
||||
cert_authority,
|
||||
&prov_request,
|
||||
&provisioning_server_url));
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
|
||||
cert_type, cert_authority, EMPTY_ORIGIN,
|
||||
&prov_request, &provisioning_server_url));
|
||||
UrlRequest url_request(provisioning_server_url);
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
std::string message;
|
||||
bool ok = url_request.GetResponse(&message);
|
||||
EXPECT_TRUE(ok);
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
cdm_engine_.HandleProvisioningResponse(message,
|
||||
&cert, &wrapped_key));
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN,
|
||||
message, &cert,
|
||||
&wrapped_key));
|
||||
}
|
||||
|
||||
void GenerateKeyRequest(const std::string& key_id,
|
||||
@@ -87,10 +101,12 @@ class WvCdmEngineTest : public testing::Test {
|
||||
|
||||
InitializationData init_data(init_data_type_string, key_id);
|
||||
|
||||
EXPECT_EQ(KEY_MESSAGE,
|
||||
cdm_engine_.GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data, kLicenseTypeStreaming,
|
||||
app_parameters, &key_msg_, &server_url, NULL));
|
||||
CdmKeyRequestType key_request_type;
|
||||
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data,
|
||||
kLicenseTypeStreaming, app_parameters, &key_msg_,
|
||||
&key_request_type, &server_url, NULL));
|
||||
EXPECT_EQ(kKeyRequestTypeInitial, key_request_type);
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest() {
|
||||
@@ -144,14 +160,14 @@ class WvCdmEngineTest : public testing::Test {
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
CdmKeySetId key_set_id;
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED,
|
||||
EXPECT_EQ(KEY_ADDED,
|
||||
cdm_engine_.AddKey(session_id_, resp, &key_set_id));
|
||||
}
|
||||
|
||||
void VerifyRenewalKeyResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
}
|
||||
|
||||
CdmEngine cdm_engine_;
|
||||
@@ -206,94 +222,3 @@ TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config(wvcdm::kContentProtectionServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
|
||||
// The following variables are configurable through command line options.
|
||||
g_license_server.assign(config.license_server());
|
||||
g_key_id_pssh.assign(config.key_id());
|
||||
std::string license_server(g_license_server);
|
||||
|
||||
int show_usage = 0;
|
||||
static const struct option long_options[] = {
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"server", required_argument, NULL, 's'},
|
||||
{NULL, 0, NULL, '\0'}};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'k': {
|
||||
g_key_id_pssh.clear();
|
||||
g_key_id_pssh.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
// This option _may_ have already been consumed by wvcdm::InitLogging()
|
||||
// above, depending on the platform-specific logging implementation.
|
||||
// We only tell getopt about it so that it is not an error. We ignore
|
||||
// the option here when seen.
|
||||
// TODO: Stop passing argv to InitLogging, and instead set the log
|
||||
// level here through the logging API. We should keep all command-line
|
||||
// parsing at the application level, rather than split between various
|
||||
// apps and various platform-specific logging implementations.
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
show_usage = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage) {
|
||||
std::cout << std::endl;
|
||||
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
|
||||
std::cout << " enclose multiple arguments in '' when using adb shell"
|
||||
<< std::endl;
|
||||
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
|
||||
<< std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --server=<server_url>";
|
||||
std::cout
|
||||
<< "configure the license server url, please include http[s] in the url"
|
||||
<< std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
std::cout << "default: " << license_server << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
|
||||
std::cout << "configure the key id or pssh, in hex format" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " default keyid:";
|
||||
std::cout << g_key_id_pssh << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl;
|
||||
|
||||
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
|
||||
config.set_license_server(g_license_server);
|
||||
config.set_key_id(g_key_id_pssh);
|
||||
|
||||
// Extract the key ID from the PSSH box.
|
||||
wvcdm::InitializationData extractor(wvcdm::CENC_INIT_DATA_FORMAT,
|
||||
g_key_id_pssh);
|
||||
g_key_id_unwrapped = extractor.data();
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "cdm_session.h"
|
||||
#include "crypto_key.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_printers.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
const std::string kToken = wvcdm::a2bs_hex(
|
||||
|
||||
const std::string kToken = a2bs_hex(
|
||||
"0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
|
||||
"82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
|
||||
"93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE"
|
||||
@@ -45,7 +50,7 @@ const std::string kToken = wvcdm::a2bs_hex(
|
||||
"8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152"
|
||||
"C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68"
|
||||
"3C");
|
||||
const std::string kWrappedKey = wvcdm::a2bs_hex(
|
||||
const std::string kWrappedKey = a2bs_hex(
|
||||
"3B84252DD84F1A710365014A114507FFFA3DD404625D61D1EEC7C3A39D72CB8D9318ADE9DA"
|
||||
"05D69F9776DAFDA49A97BC30E84CA275925DFD98CA04F7DB23465103A224852192DE232902"
|
||||
"99FF82024F5CCA7716ACA9BE0B56348BA16B9E3136D73789C842CB2ECA4820DDAAF59CCB9B"
|
||||
@@ -82,9 +87,42 @@ const std::string kWrappedKey = wvcdm::a2bs_hex(
|
||||
"33EF70621A98184DDAB5E14BC971CF98CF6C91A37FFA83B00AD3BCABBAAB2DEF1C52F43003"
|
||||
"E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B"
|
||||
"AA3348419159D207F65FCE9C1A500C6818");
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
const std::string kTestOrigin = "com.google";
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
public:
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*,
|
||||
std::string*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD1(GetToken, bool(std::string*));
|
||||
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
|
||||
MOCK_METHOD0(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&));
|
||||
MOCK_METHOD0(DeleteAllUsageReports, CdmResponseType());
|
||||
};
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
MockPolicyEngine() : PolicyEngine("mock_session_id", NULL, NULL) {}
|
||||
|
||||
// Leaving a place-holder for when PolicyEngine methods need to be mocked
|
||||
};
|
||||
|
||||
class MockCdmLicense : public CdmLicense {
|
||||
public:
|
||||
MockCdmLicense(const CdmSessionId& session_id)
|
||||
: CdmLicense(session_id) {}
|
||||
|
||||
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
@@ -95,53 +133,22 @@ using ::testing::SetArgPointee;
|
||||
using ::testing::Sequence;
|
||||
using ::testing::StrEq;
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
public:
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD1(GetToken, bool(std::string*));
|
||||
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
|
||||
MOCK_METHOD0(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&));
|
||||
};
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
// Leaving a place holder for when PolicyEngine methods need to be mocked
|
||||
};
|
||||
|
||||
class MockCdmLicense : public CdmLicense {
|
||||
public:
|
||||
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*));
|
||||
};
|
||||
|
||||
class CdmSessionTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
license_parser_ = new MockCdmLicense();
|
||||
cdm_session_.reset(new CdmSession(NULL, kTestOrigin, NULL, NULL));
|
||||
// Inject testing mocks.
|
||||
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
|
||||
cdm_session_->set_license_parser(license_parser_);
|
||||
crypto_session_ = new MockCryptoSession();
|
||||
cdm_session_->set_crypto_session(crypto_session_);
|
||||
policy_engine_ = new MockPolicyEngine();
|
||||
cdm_session_->set_policy_engine(policy_engine_);
|
||||
file_handle_ = new MockDeviceFiles();
|
||||
cdm_session_->set_file_handle(file_handle_);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (cdm_session_) delete cdm_session_;
|
||||
}
|
||||
|
||||
void CreateSession() { CreateSession(NULL); }
|
||||
|
||||
void CreateSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
cdm_session_ =
|
||||
new CdmSession(license_parser_, crypto_session_, policy_engine_,
|
||||
file_handle_, cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession* cdm_session_;
|
||||
scoped_ptr<CdmSession> cdm_session_;
|
||||
MockCdmLicense* license_parser_;
|
||||
MockCryptoSession* crypto_session_;
|
||||
MockPolicyEngine* policy_engine_;
|
||||
@@ -158,8 +165,9 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -170,7 +178,6 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -192,7 +199,6 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(false);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -206,8 +212,9 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -218,9 +225,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
|
||||
ASSERT_NE(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
@@ -230,7 +236,6 @@ TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -244,13 +249,13 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init());
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
#include "config_test_env.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kWidevineKeySystem = "com.widevine.alpha";
|
||||
|
||||
// Content Protection license server data
|
||||
// Content Protection license server (UAT) data
|
||||
// For staging server replace url with http://wv-staging-proxy.appspot.com/proxy
|
||||
const std::string kCpLicenseServer = "http://widevine-proxy.appspot.com/proxy";
|
||||
const std::string kCpClientAuth = "";
|
||||
const std::string kCpKeyId =
|
||||
@@ -26,7 +30,7 @@ const std::string kCpOfflineKeyId =
|
||||
"00000020" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220d6f66666c696e655f636c697031";
|
||||
"74220d6f66666c696e655f636c697032";
|
||||
|
||||
// Google Play license server data
|
||||
const std::string kGpLicenseServer =
|
||||
@@ -72,17 +76,15 @@ const std::string kProductionProvisioningServerUrl =
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
kGpOfflineKeyId},
|
||||
{wvcdm::kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
|
||||
{kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
|
||||
kCpOfflineKeyId},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user