Initial import of Widevine Common Encryption DRM engine
Builds libwvmdrmengine.so, which is loaded by the new MediaDrm APIs to support playback of Widevine/CENC protected content. Change-Id: I6f57dd37083dfd96c402cb9dd137c7d74edc8f1c
This commit is contained in:
38
libwvdrmengine/oemcrypto/mock/Android.mk
Normal file
38
libwvdrmengine/oemcrypto/mock/Android.mk
Normal file
@@ -0,0 +1,38 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
src/oemcrypto_engine_mock.cpp \
|
||||
src/oemcrypto_key_mock.cpp \
|
||||
src/oemcrypto_keybox_mock.cpp \
|
||||
src/oemcrypto_mock.cpp \
|
||||
src/lock.cpp \
|
||||
src/log.cpp \
|
||||
src/string_conversions.cpp \
|
||||
src/wvcrc.cpp \
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH)/../include \
|
||||
$(LOCAL_PATH)/src \
|
||||
bionic \
|
||||
external/gtest/include \
|
||||
external/openssl/include \
|
||||
external/openssl/include/openssl \
|
||||
external/stlport/stlport \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcrypto \
|
||||
libcutils \
|
||||
libdl \
|
||||
liblog \
|
||||
libstlport \
|
||||
libutils \
|
||||
libz \
|
||||
|
||||
LOCAL_MODULE := liboemcrypto
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
109
libwvdrmengine/oemcrypto/mock/Makefile
Normal file
109
libwvdrmengine/oemcrypto/mock/Makefile
Normal file
@@ -0,0 +1,109 @@
|
||||
#
|
||||
# Builds liboemcrypto_mock.so
|
||||
#
|
||||
|
||||
#PROJECTS_ROOT = ~projects
|
||||
#
|
||||
ifndef PROJECTS_ROOT
|
||||
PROJECTS_ROOT = ../../../..
|
||||
endif
|
||||
|
||||
CDM_ROOT = $(PROJECTS_ROOT)/cdm
|
||||
CDM_SRC_PATH = $(CDM_ROOT)/cdm
|
||||
CDM_INCLUDE_PATH = $(CDM_SRC_PATH)/include
|
||||
|
||||
EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka/src
|
||||
CHROME_ROOT = $(EUREKA_ROOT)/chromium/src
|
||||
#
|
||||
# build outputs should go into Chrome repository, such as ../chromium/src/out
|
||||
# or some local equivalent.
|
||||
# WARNING: splitting outputs from CHROME_ROOT can lead to build errors
|
||||
ifndef CHROME_ROOT
|
||||
CHROME_ROOT = $(CDM_ROOT)/out
|
||||
endif
|
||||
|
||||
# TARGET_PLATFORM from {x86,eureka}
|
||||
ifndef TARGET_PLATFORM
|
||||
TARGET_PLATFORM = x86
|
||||
endif
|
||||
|
||||
# TARGET_BUILD from {debug,release}
|
||||
ifndef TARGET_BUILD
|
||||
TARGET_BUILD = debug
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_PLATFORM),x86)
|
||||
BUILDPLATFORM = out_x86_linux
|
||||
else ifeq ($(TARGET_PLATFORM),eureka)
|
||||
BUILDPLATFORM = out_arm_eureka
|
||||
else
|
||||
BUILDPLATFORM = UNKNOWN
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BUILD),debug)
|
||||
BUILDTYPE = Debug
|
||||
else ifeq ($(TARGET_BUILD),release)
|
||||
BUILDTYPE = Release
|
||||
else
|
||||
BUILDTYPE = UNKNOWN
|
||||
endif
|
||||
|
||||
BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE)
|
||||
OBJPATH = $(BUILDPATH)/obj
|
||||
|
||||
#primary build target
|
||||
TARGET_LIB = liboemcrypto_mock.so
|
||||
|
||||
LIB_OBJECTS = \
|
||||
oemcrypto_mock.o \
|
||||
oemcrypto_engine_mock.o \
|
||||
oemcrypto_key_mock.o \
|
||||
oemcrypto_keybox_mock.o
|
||||
|
||||
INCLUDES = \
|
||||
-I$(CDM_INCLUDE_PATH)
|
||||
|
||||
OBJECTDIR = $(OBJPATH)/mock
|
||||
|
||||
INSTALLDIR = $(BUILDPATH)
|
||||
|
||||
OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(LIB_OBJECTS))
|
||||
|
||||
CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST
|
||||
LINK = $(CXX)
|
||||
MKDIR = mkdir -p
|
||||
|
||||
$(INSTALLDIR)/$(TARGET_LIB): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS)
|
||||
$(LINK) -v -fPIC -m64 -shared -static-libgcc $(SHLIBFLAGS) \
|
||||
$(OBJECTS) \
|
||||
--retain-symbols-file=OECsymbols.txt \
|
||||
-Wl,-Bstatic \
|
||||
-Wl,-Bdynamic -lcrypto \
|
||||
-o $@
|
||||
|
||||
$(OBJECTDIR)/%.o: src/%.cpp
|
||||
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
$(OBJECTDIR)/%.o: src/%.cc
|
||||
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
test:
|
||||
make -C test
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(OBJECTDIR)
|
||||
$(RM) -rf $(INSTALLDIR)/$(TARGET_LIB)
|
||||
|
||||
$(OBJECTDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
$(INSTALLDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
.PHONY: $(OBJECTDIR)
|
||||
|
||||
.PHONY: $(INSTALLDIR)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
.PHONY: test
|
||||
54
libwvdrmengine/oemcrypto/mock/src/lock.cpp
Normal file
54
libwvdrmengine/oemcrypto/mock/src/lock.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Lock class - provides a simple android specific mutex implementation
|
||||
|
||||
#include "lock.h"
|
||||
#include "utils/Mutex.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Lock::Impl {
|
||||
public:
|
||||
android::Mutex lock_;
|
||||
};
|
||||
|
||||
Lock::Lock() : impl_(new Lock::Impl()) {
|
||||
}
|
||||
|
||||
Lock::~Lock() {
|
||||
delete impl_;
|
||||
impl_ = NULL;
|
||||
}
|
||||
|
||||
void Lock::Acquire() {
|
||||
impl_->lock_.lock();
|
||||
}
|
||||
|
||||
void Lock::Release() {
|
||||
impl_->lock_.unlock();
|
||||
}
|
||||
|
||||
bool Lock::Try() {
|
||||
return (impl_->lock_.tryLock() == 0);
|
||||
}
|
||||
|
||||
class AutoLock::Impl {
|
||||
public:
|
||||
android::Mutex::Autolock* autolock_;
|
||||
};
|
||||
|
||||
AutoLock::AutoLock(Lock& lock) : impl_(new AutoLock::Impl()) {
|
||||
impl_->autolock_ = new android::Mutex::Autolock(lock.impl_->lock_);
|
||||
}
|
||||
|
||||
AutoLock::AutoLock(Lock* lock) : impl_(new AutoLock::Impl()) {
|
||||
impl_->autolock_ = new android::Mutex::Autolock(lock->impl_->lock_);
|
||||
}
|
||||
|
||||
AutoLock::~AutoLock() {
|
||||
delete impl_->autolock_;
|
||||
delete impl_;
|
||||
impl_ = NULL;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
54
libwvdrmengine/oemcrypto/mock/src/lock.h
Normal file
54
libwvdrmengine/oemcrypto/mock/src/lock.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Lock - Platform independent interface for a Mutex class
|
||||
//
|
||||
#ifndef OEMCRYPTO_LOCK_H_
|
||||
#define OEMCRYPTO_LOCK_H_
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Simple lock class. The implementation is platform dependent.
|
||||
//
|
||||
// The lock must be unlocked by the thread that locked it.
|
||||
// The lock is also not recursive (ie. cannot be taken multiple times).
|
||||
class Lock {
|
||||
public:
|
||||
Lock();
|
||||
~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:
|
||||
class Impl;
|
||||
Impl* impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Lock);
|
||||
};
|
||||
|
||||
// Manages the lock automatically. It will be locked when AutoLock
|
||||
// is constructed and release when AutoLock goes out of scope
|
||||
class AutoLock {
|
||||
public:
|
||||
explicit AutoLock(Lock& lock);
|
||||
explicit AutoLock(Lock* lock);
|
||||
~AutoLock();
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // OEMCRYPTO_LOCK_H_
|
||||
33
libwvdrmengine/oemcrypto/mock/src/log.cpp
Normal file
33
libwvdrmengine/oemcrypto/mock/src/log.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Log - implemented using the standard Android logging mechanism
|
||||
|
||||
#define LOG_TAG "WVCdm"
|
||||
#define LOG_BUF_SIZE 1024
|
||||
|
||||
#include "log.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
void log_write(LogPriority level, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
char buf[LOG_BUF_SIZE];
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
android_LogPriority prio = ANDROID_LOG_VERBOSE;
|
||||
|
||||
switch(level) {
|
||||
case LOG_ERROR: prio = ANDROID_LOG_ERROR; break;
|
||||
case LOG_WARN: prio = ANDROID_LOG_WARN; break;
|
||||
case LOG_INFO: prio = ANDROID_LOG_INFO; break;
|
||||
case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break;
|
||||
case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break;
|
||||
}
|
||||
|
||||
__android_log_write(prio, LOG_TAG, buf);
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
31
libwvdrmengine/oemcrypto/mock/src/log.h
Normal file
31
libwvdrmengine/oemcrypto/mock/src/log.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Log - Platform independent interface for a Logging class
|
||||
//
|
||||
#ifndef OEMCRYPTO_LOG_H_
|
||||
#define OEMCRYPTO_LOG_H_
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Simple logging class. The implementation is platform dependent.
|
||||
|
||||
typedef enum {
|
||||
LOG_ERROR,
|
||||
LOG_WARN,
|
||||
LOG_INFO,
|
||||
LOG_DEBUG,
|
||||
LOG_VERBOSE
|
||||
} LogPriority;
|
||||
|
||||
void log_write(LogPriority priority, const char *fmt, ...);
|
||||
|
||||
// Log APIs
|
||||
#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__))
|
||||
#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__))
|
||||
#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__))
|
||||
#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__))
|
||||
#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__))
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // OEMCRYPTO_LOG_H_
|
||||
742
libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp
Normal file
742
libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp
Normal file
@@ -0,0 +1,742 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "oemcrypto_key_mock.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "openssl/rand.h"
|
||||
#include <openssl/rsa.h>
|
||||
#include "openssl/sha.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
// Increment counter for AES-CTR
|
||||
void ctr128_inc(uint8_t* counter) {
|
||||
uint32_t n = 16;
|
||||
do {
|
||||
if (++counter[--n] != 0) return;
|
||||
} while (n);
|
||||
}
|
||||
void dump_openssl_error() {
|
||||
while (unsigned long err = ERR_get_error()) {
|
||||
char buffer[120];
|
||||
LOGE("openssl error: %lu: %s",
|
||||
err, ERR_error_string(err, buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
SessionKeyTable::~SessionKeyTable() {
|
||||
for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) {
|
||||
if (NULL != i->second) {
|
||||
delete i->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) {
|
||||
if (keys_.find(key_id) != keys_.end()) return false;
|
||||
keys_[key_id] = new Key(key_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
Key* SessionKeyTable::Find(const KeyId key_id) {
|
||||
if (keys_.find(key_id) == keys_.end()) {
|
||||
return NULL;
|
||||
}
|
||||
return keys_[key_id];
|
||||
}
|
||||
|
||||
void SessionKeyTable::Remove(const KeyId key_id) {
|
||||
if (keys_.find(key_id) != keys_.end()) {
|
||||
delete keys_[key_id];
|
||||
keys_.erase(key_id);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionContext::Open() {
|
||||
}
|
||||
|
||||
void SessionContext::Close() {
|
||||
}
|
||||
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
bool SessionContext::DeriveKey(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& context,
|
||||
int counter,
|
||||
std::vector<uint8_t>* out) {
|
||||
if (key.empty() || counter > 2 || context.empty() || out == NULL) {
|
||||
LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
|
||||
if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) {
|
||||
LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.push_back(counter);
|
||||
message.insert(message.end(), context.begin(), context.end());
|
||||
|
||||
if (!CMAC_Update(cmac_ctx, &message[0], message.size())) {
|
||||
LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t reslen;
|
||||
uint8_t res[128];
|
||||
if (!CMAC_Final(cmac_ctx, res, &reslen)) {
|
||||
LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->assign(res, res + reslen);
|
||||
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::DeriveKeys(const std::vector<uint8_t>& mac_key_context,
|
||||
const std::vector<uint8_t>& enc_key_context) {
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> device_key = ce_->keybox().device_key().value();
|
||||
std::vector<uint8_t> mac_key;
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
if (!DeriveKey(device_key, mac_key_context, 1, &mac_key)) {
|
||||
return false;
|
||||
}
|
||||
if (!DeriveKey(device_key, mac_key_context, 2, &mac_key_part2)) {
|
||||
return false;
|
||||
}
|
||||
mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
if (!DeriveKey(device_key, enc_key_context, 1, &enc_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_mac_key(mac_key);
|
||||
set_encryption_key(enc_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
|
||||
const std::vector<uint8_t>& mac_key_context,
|
||||
const std::vector<uint8_t>& enc_key_context) {
|
||||
if (!rsa_key_) {
|
||||
LOGE("[RSADeriveKeys(): no RSA key set]");
|
||||
return false;
|
||||
}
|
||||
session_key_.resize(wvcdm::KEY_SIZE);
|
||||
if (-1 == RSA_private_decrypt(wvcdm::KEY_SIZE, &enc_session_key[0],
|
||||
&session_key_[0], rsa_key_,
|
||||
RSA_PKCS1_OAEP_PADDING)) {
|
||||
LOGE("[RSADeriveKeys(): error decrypting session key.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> mac_key;
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
if (!DeriveKey(session_key_, mac_key_context, 1, &mac_key)) {
|
||||
return false;
|
||||
}
|
||||
if (!DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2)) {
|
||||
return false;
|
||||
}
|
||||
mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
if (!DeriveKey(session_key_, enc_key_context, 1, &enc_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_mac_key(mac_key);
|
||||
set_encryption_key(enc_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Utility function to generate a message signature
|
||||
bool SessionContext::GenerateSignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mac_key_.empty() || mac_key_.size() != wvcdm::MAC_KEY_SIZE) {
|
||||
LOGE("[GenerateSignature(): No MAC Key]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*signature_length < SHA256_DIGEST_LENGTH) {
|
||||
*signature_length = SHA256_DIGEST_LENGTH;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int md_len = *signature_length;
|
||||
if (HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH,
|
||||
message, message_length, signature, &md_len)) {
|
||||
*signature_length = md_len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SessionContext::GenerateRSASignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
if (!rsa_key_) {
|
||||
LOGE("[GenerateRSASignature(): no RSA key set]");
|
||||
return false;
|
||||
}
|
||||
if (*signature_length < static_cast<size_t>(RSA_size(rsa_key_))) {
|
||||
*signature_length = RSA_size(rsa_key_);
|
||||
return false;
|
||||
}
|
||||
// TODO(fredgc): This uses the wrong algorithm for signing.
|
||||
// This code needs to be fixed!!
|
||||
LOGE("COmputing signature using RSASSA-PKCS1v1.5 instead of RSASSA-PSS");
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
if (!SHA1(message, message_length, hash)) {
|
||||
LOGE("[GeneratRSASignature(): error creating signature hash.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
int ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH,
|
||||
signature, signature_length, rsa_key_);
|
||||
if (ret != 1) {
|
||||
LOGE("[GeneratRSASignature(): error signing signature hash.]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate message signature
|
||||
bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
size_t message_length,
|
||||
const uint8_t* given_signature,
|
||||
size_t signature_length) {
|
||||
|
||||
if (signature_length != SHA256_DIGEST_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
uint8_t computed_signature[signature_length];
|
||||
if (! GenerateSignature(given_message, message_length,
|
||||
computed_signature, &signature_length)) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(given_signature, computed_signature, signature_length)) {
|
||||
LOGE("Invalid signature given: %s",
|
||||
wvcdm::HexEncode(given_signature, signature_length).c_str());
|
||||
LOGE("Invalid signature computed: %s",
|
||||
wvcdm::HexEncode(computed_signature, signature_length).c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::ParseKeyControl(
|
||||
const std::vector<uint8_t>& key_control_string,
|
||||
KeyControlBlock& key_control_block) {
|
||||
|
||||
key_control_block.Invalidate();
|
||||
|
||||
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
|
||||
return false;
|
||||
}
|
||||
if (!key_control_block.SetFromString(key_control_string)) {
|
||||
LOGE("KCB: BAD Size or Structure");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!key_control_block.Validate()) {
|
||||
LOGE("KCB: BAD Signature");
|
||||
return false;
|
||||
}
|
||||
if (!CheckNonce(key_control_block.nonce())) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("KCB:");
|
||||
LOGD(" valid: %d", key_control_block.valid());
|
||||
LOGD(" duration: %d", key_control_block.duration());
|
||||
LOGD(" nonce: %08X", key_control_block.nonce());
|
||||
LOGD(" bits: %08X", key_control_block.control_bits());
|
||||
LOGD(" bit kControlObserveDataPath %s.",
|
||||
(key_control_block.control_bits() & kControlObserveDataPath) ? "set" : "unset");
|
||||
LOGD(" bit kControlObserveHDCP %s.",
|
||||
(key_control_block.control_bits() & kControlObserveHDCP) ? "set" : "unset");
|
||||
LOGD(" bit kControlObserveCGMS %s.",
|
||||
(key_control_block.control_bits() & kControlObserveCGMS) ? "set" : "unset");
|
||||
LOGD(" bit kControlDataPathSecure %s.",
|
||||
(key_control_block.control_bits() & kControlDataPathSecure) ? "set" : "unset");
|
||||
LOGD(" bit kControlNonceEnabled %s.",
|
||||
(key_control_block.control_bits() & kControlNonceEnabled) ? "set" : "unset");
|
||||
LOGD(" bit kControlHDCPRequired %s.",
|
||||
(key_control_block.control_bits() & kControlHDCPRequired) ? "set" : "unset");
|
||||
uint32_t cgms_bits = key_control_block.control_bits() & 0x3;
|
||||
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
|
||||
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SessionContext::StartTimer() {
|
||||
timer_start_ = time(NULL);
|
||||
}
|
||||
|
||||
uint32_t SessionContext::CurrentTimer() {
|
||||
time_t now = time(NULL);
|
||||
return now - timer_start_;
|
||||
}
|
||||
|
||||
bool SessionContext::InstallKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_data,
|
||||
const std::vector<uint8_t>& key_data_iv,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
|
||||
// Decrypt encrypted key_data using derived encryption key and offered iv
|
||||
std::vector<uint8_t> content_key;
|
||||
std::vector<uint8_t> key_control_str;
|
||||
KeyControlBlock key_control_block;
|
||||
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, key_data_iv,
|
||||
key_data, &content_key)) {
|
||||
LOGE("[Installkey(): Could not decrypt key data]");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // Print content key to stdout.
|
||||
std::cout << " InstallKey: key_id = "
|
||||
<< wvcdm::b2a_hex(key_id) << std::endl;
|
||||
std::cout << " InstallKey: content_key = "
|
||||
<< wvcdm::b2a_hex(content_key) << std::endl;
|
||||
std::cout << " InstallKey: key_control = "
|
||||
<< wvcdm::b2a_hex(content_key) << std::endl;
|
||||
#endif
|
||||
|
||||
// Key control must be supplied by license server
|
||||
if (key_control.empty()) {
|
||||
LOGE("[Installkey(): WARNING: No Key Control]");
|
||||
key_control_block.Invalidate();
|
||||
return false;
|
||||
} else {
|
||||
if (key_control_iv.empty()) {
|
||||
LOGE("[Installkey(): ERROR: No Key Control IV]");
|
||||
return false;
|
||||
}
|
||||
if (!ce_->DecryptMessage(this, content_key, key_control_iv,
|
||||
key_control, &key_control_str)) {
|
||||
LOGE("[Installkey(): ERROR: Could not decrypt content key]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ParseKeyControl(key_control_str, key_control_block)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Key key(KEYTYPE_CONTENT, content_key, key_control_block);
|
||||
session_keys_.Insert(key_id, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::EncryptRSAKey(uint8_t* wrapped_rsa_key,
|
||||
size_t wrapped_rsa_key_length,
|
||||
uint8_t* wrapped_rsa_key_iv) {
|
||||
std::vector<uint8_t> buffer(wrapped_rsa_key_length);
|
||||
uint8_t* p = &buffer[0];
|
||||
int len = i2d_RSAPrivateKey(rsa_key_, &p);
|
||||
if (len < 0) {
|
||||
LOGE("[RewrapRSAKey(): Could not decode rsa key]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Encrypt rsa key with keybox.
|
||||
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, wrapped_rsa_key_iv, wvcdm::KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(&buffer[0], wrapped_rsa_key, wrapped_rsa_key_length,
|
||||
&aes_key, iv_buffer, AES_ENCRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
std::vector<uint8_t> enc_rsa_key_v(enc_rsa_key,
|
||||
enc_rsa_key + enc_rsa_key_length);
|
||||
std::vector<uint8_t> iv(enc_rsa_key_iv, enc_rsa_key_iv + wvcdm::KEY_IV_SIZE);
|
||||
std::vector<uint8_t> rsa_key; // unencrypted.
|
||||
|
||||
// Validate message signature
|
||||
if (!ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
LOGE("[LoadRSAKey(): Could not verify signature]");
|
||||
return false;
|
||||
}
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, iv,
|
||||
enc_rsa_key_v, &rsa_key)) {
|
||||
LOGE("[LoadRSAKey(): Could not decrypt key data]");
|
||||
return false;
|
||||
}
|
||||
if (rsa_key_) {
|
||||
RSA_free(rsa_key_);
|
||||
rsa_key_ = NULL;
|
||||
}
|
||||
uint8_t const* p = &rsa_key[0];
|
||||
RSA* rsa = d2i_RSAPrivateKey(0, &p, rsa_key.size());
|
||||
rsa_key_ = rsa;
|
||||
if (! rsa_key_) {
|
||||
LOGE("[LoadRSAKey(): Could decode unencrypted rsa key]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
switch (RSA_check_key(rsa_key_)) {
|
||||
case 1: // valid.
|
||||
return true;
|
||||
case 0: // not valid.
|
||||
LOGE("[LoadRSAKey(): rsa key not valid]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
default: // -1 == check failed.
|
||||
LOGE("[LoadRSAKey(): error checking rsa key]");
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
if (key_id.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Key* content_key = session_keys_.Find(key_id);
|
||||
|
||||
if (NULL == content_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!key_control.empty()) {
|
||||
const std::vector<uint8_t> content_key_value = content_key->value();
|
||||
|
||||
// Decrypt encrypted key control block
|
||||
// We don't actually make use of it in Oemcrypto mock, just to verify its
|
||||
// validity
|
||||
std::vector<uint8_t> control;
|
||||
if (key_control_iv.empty()) {
|
||||
control = key_control;
|
||||
} else if (!ce_->DecryptMessage(this, content_key_value, key_control_iv,
|
||||
key_control, &control)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyControlBlock key_control_block;
|
||||
if (!ParseKeyControl(control, key_control_block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!content_key->UpdateControl(key_control_block)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::UpdateMacKey(const std::vector<uint8_t>& enc_mac_key,
|
||||
const std::vector<uint8_t>& iv) {
|
||||
|
||||
// Decrypt mac key from enc_mac_key using device_key
|
||||
std::vector<uint8_t> mac_key;
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, iv,
|
||||
enc_mac_key, &mac_key)) {
|
||||
return false;
|
||||
}
|
||||
mac_key_ = mac_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::SelectContentKey(const KeyId& key_id) {
|
||||
const Key* content_key = session_keys_.Find(key_id);
|
||||
if (NULL == content_key) {
|
||||
LOGE("[SelectContentKey(): No key matches key id]");
|
||||
return false;
|
||||
}
|
||||
current_content_key_ = content_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SessionContext::AddNonce(uint32_t nonce) {
|
||||
nonce_table_.AddNonce(nonce);
|
||||
}
|
||||
|
||||
bool SessionContext::CheckNonce(uint32_t nonce) {
|
||||
return nonce_table_.CheckNonce(nonce);
|
||||
}
|
||||
|
||||
void SessionContext::FlushNonces() {
|
||||
nonce_table_.Flush();
|
||||
}
|
||||
|
||||
CryptoEngine::CryptoEngine() :
|
||||
ce_state_(CE_INITIALIZED), current_session_(NULL) {
|
||||
valid_ = true;
|
||||
ERR_load_crypto_strings();
|
||||
}
|
||||
|
||||
CryptoEngine::~CryptoEngine() {
|
||||
current_session_ = NULL;
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
void CryptoEngine::Terminate() {
|
||||
}
|
||||
|
||||
KeyboxError CryptoEngine::ValidateKeybox() { return keybox_.Validate(); }
|
||||
|
||||
SessionId CryptoEngine::CreateSession() {
|
||||
wvcdm::AutoLock lock(session_table_lock_);
|
||||
static int unique_id = 1;
|
||||
SessionId sid = (SessionId)++unique_id;
|
||||
SessionContext* sctx = new SessionContext(this, sid);
|
||||
sessions_[sid] = sctx;
|
||||
return sid;
|
||||
}
|
||||
|
||||
bool CryptoEngine::DestroySession(SessionId sid) {
|
||||
SessionContext* sctx = FindSession(sid);
|
||||
wvcdm::AutoLock lock(session_table_lock_);
|
||||
if (sctx) {
|
||||
sessions_.erase(sid);
|
||||
delete sctx;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SessionContext* CryptoEngine::FindSession(SessionId sid) {
|
||||
wvcdm::AutoLock lock(session_table_lock_);
|
||||
ActiveSessions::iterator it = sessions_.find(sid);
|
||||
if (it != sessions_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Internal utility function to decrypt the message
|
||||
bool CryptoEngine::DecryptMessage(SessionContext* session,
|
||||
const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
if (key.empty() || iv.empty() || message.empty() || !decrypted) {
|
||||
LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
|
||||
decrypted->resize(message.size());
|
||||
uint8_t iv_buffer[16];
|
||||
memcpy(iv_buffer, &iv[0], 16);
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key(&key[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(),
|
||||
&aes_key, iv_buffer, AES_DECRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoEngine::DecryptCTR(SessionContext* session,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t byte_offset,
|
||||
const std::vector<uint8_t>& cipher_data,
|
||||
bool is_encrypted,
|
||||
void* clear_data,
|
||||
BufferType buffer_type) {
|
||||
|
||||
// Check there is a content key
|
||||
if (session->current_content_key() == NULL) {
|
||||
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return false;
|
||||
}
|
||||
const KeyControlBlock& control = session->current_content_key()->control();
|
||||
if (control.control_bits() & kControlDataPathSecure) {
|
||||
if (buffer_type == kBufferTypeClear) {
|
||||
LOGE("[DecryptCTR(): Secure key with insecure buffer]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (control.control_bits() & kControlHDCPRequired) {
|
||||
// For reference implementation, we do not implement any HDCP.
|
||||
return false;
|
||||
}
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < session->CurrentTimer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& content_key = session->current_content_key()->value();
|
||||
|
||||
// Set the AES key.
|
||||
if (static_cast<int>(content_key.size()) != AES_BLOCK_SIZE) {
|
||||
LOGE("[DecryptCTR(): CONTENT_KEY has wrong size.");
|
||||
return false;
|
||||
}
|
||||
const uint8_t* key_u8 = &content_key[0];
|
||||
AES_KEY aes_key;
|
||||
if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
|
||||
LOGE("[DecryptCTR(): FAILURE]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer_type == kBufferTypeDirect) {
|
||||
// For reference implementation, we quietly drop direct video.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer_type == kBufferTypeSecure) {
|
||||
// For reference implementation, we also quietly drop secure data.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! is_encrypted) {
|
||||
memcpy(reinterpret_cast<uint8_t*>(clear_data),
|
||||
&cipher_data[0], cipher_data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Local copy (will be modified).
|
||||
uint8_t aes_iv[AES_BLOCK_SIZE];
|
||||
if (static_cast<int>(iv.size()) != AES_BLOCK_SIZE) {
|
||||
LOGE("[DecryptCTR(): FAILURE: iv has wrong length]");
|
||||
return false;
|
||||
}
|
||||
memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE);
|
||||
|
||||
// Encrypt the IV.
|
||||
uint8_t ecount_buf[AES_BLOCK_SIZE];
|
||||
if (byte_offset != 0) {
|
||||
// The context is needed only when not starting a new block.
|
||||
AES_encrypt(aes_iv, ecount_buf, &aes_key);
|
||||
ctr128_inc(aes_iv);
|
||||
}
|
||||
|
||||
// Decryption.
|
||||
unsigned int byte_offset_cur = byte_offset;
|
||||
AES_ctr128_encrypt(
|
||||
&cipher_data[0], reinterpret_cast<uint8_t*>(clear_data), cipher_data.size(),
|
||||
&aes_key, aes_iv, ecount_buf, &byte_offset_cur);
|
||||
if (byte_offset_cur != ((byte_offset + cipher_data.size()) % AES_BLOCK_SIZE)) {
|
||||
LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NonceTable::AddNonce(uint32_t nonce) {
|
||||
int new_slot = -1;
|
||||
int oldest_slot = -1;
|
||||
|
||||
// Flush any nonces that have been checked but not flushed.
|
||||
// After flush, nonces will be either valid or invalid.
|
||||
Flush();
|
||||
|
||||
for (int i = 0; i < kTableSize; ++i) {
|
||||
// Increase age of all valid nonces.
|
||||
if (kNTStateValid == state_[i]) {
|
||||
++age_[i];
|
||||
if (-1 == oldest_slot) {
|
||||
oldest_slot = i;
|
||||
} else {
|
||||
if (age_[i] > age_[oldest_slot]) {
|
||||
oldest_slot = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (-1 == new_slot) {
|
||||
age_[i] = 0;
|
||||
nonces_[i] = nonce;
|
||||
state_[i] = kNTStateValid;
|
||||
new_slot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-1 == new_slot) {
|
||||
// reuse oldest
|
||||
// assert (oldest_slot != -1)
|
||||
int i = oldest_slot;
|
||||
age_[i] = 0;
|
||||
nonces_[i] = nonce;
|
||||
state_[i] = kNTStateValid;
|
||||
}
|
||||
}
|
||||
|
||||
bool NonceTable::CheckNonce(uint32_t nonce) {
|
||||
for (int i = 0; i < kTableSize; ++i) {
|
||||
if (kNTStateInvalid != state_[i]) {
|
||||
if (nonce == nonces_[i]) {
|
||||
state_[i] = kNTStateFlushPending;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NonceTable::Flush() {
|
||||
for (int i = 0; i < kTableSize; ++i) {
|
||||
if (kNTStateFlushPending == state_[i]) {
|
||||
state_[i] = kNTStateInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace wvoec_mock
|
||||
232
libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h
Normal file
232
libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h
Normal file
@@ -0,0 +1,232 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef OEMCRYPTO_ENGINE_MOCK_H_
|
||||
#define OEMCRYPTO_ENGINE_MOCK_H_
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_key_mock.h"
|
||||
#include "oemcrypto_keybox_mock.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
enum BufferType {
|
||||
kBufferTypeClear,
|
||||
kBufferTypeSecure,
|
||||
kBufferTypeDirect
|
||||
};
|
||||
|
||||
class SessionContext;
|
||||
class CryptoEngine;
|
||||
|
||||
typedef uint32_t SessionId;
|
||||
typedef std::map<SessionId, SessionContext*> ActiveSessions;
|
||||
|
||||
typedef std::vector<uint8_t> KeyId;
|
||||
typedef std::map<KeyId, Key*> KeyMap;
|
||||
|
||||
// SessionKeyTable holds the keys for the current session
|
||||
class SessionKeyTable {
|
||||
public:
|
||||
SessionKeyTable() {}
|
||||
~SessionKeyTable();
|
||||
|
||||
bool Insert(const KeyId key_id, const Key& key_data);
|
||||
Key* Find(const KeyId key_id);
|
||||
void Remove(const KeyId key_id);
|
||||
|
||||
private:
|
||||
KeyMap keys_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable);
|
||||
};
|
||||
|
||||
class NonceTable {
|
||||
public:
|
||||
static const int kTableSize = 16;
|
||||
NonceTable() {
|
||||
for (int i = 0; i < kTableSize; ++i) {
|
||||
state_[i] = kNTStateInvalid;
|
||||
}
|
||||
}
|
||||
~NonceTable() {};
|
||||
void AddNonce(uint32_t nonce);
|
||||
bool CheckNonce(uint32_t nonce);
|
||||
void Flush();
|
||||
private:
|
||||
enum NonceTableState {
|
||||
kNTStateInvalid,
|
||||
kNTStateValid,
|
||||
kNTStateFlushPending
|
||||
};
|
||||
NonceTableState state_[kTableSize];
|
||||
uint32_t age_[kTableSize];
|
||||
uint32_t nonces_[kTableSize];
|
||||
};
|
||||
|
||||
class SessionContext {
|
||||
private:
|
||||
SessionContext() {}
|
||||
|
||||
public:
|
||||
explicit SessionContext(CryptoEngine* ce, SessionId sid)
|
||||
: valid_(true), ce_(ce), id_(sid), current_content_key_(NULL),
|
||||
rsa_key_(NULL) {}
|
||||
~SessionContext() {}
|
||||
|
||||
void Open();
|
||||
void Close();
|
||||
|
||||
bool isValid() { return valid_; }
|
||||
|
||||
bool DeriveKeys(const std::vector<uint8_t>& mac_context,
|
||||
const std::vector<uint8_t>& enc_context);
|
||||
bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
|
||||
const std::vector<uint8_t>& mac_context,
|
||||
const std::vector<uint8_t>& enc_context);
|
||||
bool GenerateSignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
bool GenerateRSASignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
|
||||
bool ValidateMessage(const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
void StartTimer();
|
||||
uint32_t CurrentTimer(); // (seconds).
|
||||
bool InstallKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_data,
|
||||
const std::vector<uint8_t>& key_data_iv,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv);
|
||||
bool EncryptRSAKey(uint8_t* wrapped_rsa_key,
|
||||
size_t wrapped_rsa_key_length,
|
||||
uint8_t* wrapped_rsa_key_iv);
|
||||
bool LoadRSAKey(const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
bool ParseKeyControl(const std::vector<uint8_t>& key_control_string,
|
||||
KeyControlBlock& key_control_block);
|
||||
bool RefreshKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv);
|
||||
bool UpdateMacKey(const std::vector<uint8_t>& mac_key, const std::vector<uint8_t>& iv);
|
||||
bool SelectContentKey(const KeyId& key_id);
|
||||
const Key* current_content_key(void) {return current_content_key_;}
|
||||
void set_mac_key(const std::vector<uint8_t>& mac_key) { mac_key_ = mac_key; }
|
||||
const std::vector<uint8_t>& mac_key() { return mac_key_; }
|
||||
|
||||
void set_encryption_key(const std::vector<uint8_t>& enc_key) {
|
||||
encryption_key_ = enc_key;
|
||||
}
|
||||
const std::vector<uint8_t>& encryption_key() { return encryption_key_; }
|
||||
|
||||
void AddNonce(uint32_t nonce);
|
||||
bool CheckNonce(uint32_t nonce);
|
||||
void FlushNonces();
|
||||
|
||||
private:
|
||||
|
||||
bool DeriveKey(const std::vector<uint8_t>& key, const std::vector<uint8_t>& context,
|
||||
int counter, std::vector<uint8_t>* out);
|
||||
|
||||
bool valid_;
|
||||
CryptoEngine* ce_;
|
||||
SessionId id_;
|
||||
std::vector<uint8_t> mac_key_;
|
||||
std::vector<uint8_t> encryption_key_;
|
||||
std::vector<uint8_t> session_key_;
|
||||
const Key* current_content_key_;
|
||||
SessionKeyTable session_keys_;
|
||||
NonceTable nonce_table_;
|
||||
RSA* rsa_key_;
|
||||
time_t timer_start_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
|
||||
};
|
||||
|
||||
class CryptoEngine {
|
||||
|
||||
private:
|
||||
|
||||
enum CryptoEngineState {
|
||||
CE_ILLEGAL,
|
||||
CE_INITIALIZED,
|
||||
CE_HAS_KEYBOX,
|
||||
CE_HAS_SESSIONS,
|
||||
CE_ERROR
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
CryptoEngine();
|
||||
~CryptoEngine();
|
||||
|
||||
bool Initialized() { return (ce_state_ != CE_ILLEGAL); }
|
||||
|
||||
void Terminate();
|
||||
|
||||
bool isValid() { return valid_; }
|
||||
|
||||
KeyboxError ValidateKeybox();
|
||||
WvKeybox& keybox() { return keybox_; }
|
||||
|
||||
SessionId CreateSession();
|
||||
|
||||
bool DestroySession(SessionId sid);
|
||||
|
||||
SessionContext* FindSession(SessionId sid);
|
||||
|
||||
void set_current_session_(SessionContext* current) {
|
||||
current_session_ = current;
|
||||
}
|
||||
|
||||
bool DecryptMessage(SessionContext* session,
|
||||
const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted);
|
||||
|
||||
bool DecryptCTR(SessionContext* session,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t byte_offset,
|
||||
const std::vector<uint8_t>& cipher_data,
|
||||
bool is_encrypted,
|
||||
void* clear_data,
|
||||
BufferType buffer_type);
|
||||
|
||||
private:
|
||||
|
||||
bool valid_;
|
||||
CryptoEngineState ce_state_;
|
||||
SessionContext* current_session_;
|
||||
ActiveSessions sessions_;
|
||||
WvKeybox keybox_;
|
||||
wvcdm::Lock session_table_lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
|
||||
};
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
#endif
|
||||
106
libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp
Normal file
106
libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "oemcrypto_key_mock.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "log.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
bool KeyControlBlock::Validate() {
|
||||
|
||||
valid_ = false;
|
||||
if (0x6b63746c != verification_) { // kctl.
|
||||
LOGE("KCB: BAD verification string: %08X (not %08X)", verification_,
|
||||
0x6b63746c);
|
||||
return false;
|
||||
}
|
||||
// TODO(gmorgan): validate control bits
|
||||
valid_ = true;
|
||||
return valid_;
|
||||
}
|
||||
|
||||
// This extracts 4 bytes in network byte order to a 32 bit integer in
|
||||
// host byte order.
|
||||
uint32_t KeyControlBlock::ExtractField(const std::vector<uint8_t>& str, int idx) {
|
||||
int bidx = idx * 4;
|
||||
uint32_t t = static_cast<unsigned char>(str[bidx]) << 24;
|
||||
t |= static_cast<unsigned char>(str[bidx + 1]) << 16;
|
||||
t |= static_cast<unsigned char>(str[bidx + 2]) << 8;
|
||||
t |= static_cast<unsigned char>(str[bidx + 3]);
|
||||
return t;
|
||||
}
|
||||
|
||||
bool KeyControlBlock::SetFromString(const std::vector<uint8_t>& key_control_string) {
|
||||
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
|
||||
LOGE("KCB: BAD Size: %d (not %d)",key_control_string.size(),
|
||||
wvcdm::KEY_CONTROL_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
verification_ = ExtractField(key_control_string, 0);
|
||||
duration_ = ExtractField(key_control_string, 1);
|
||||
nonce_ = ExtractField(key_control_string, 2);
|
||||
control_bits_ = ExtractField(key_control_string, 3);
|
||||
|
||||
return Validate();
|
||||
}
|
||||
|
||||
Key::Key(KeyType ktype, const std::vector<uint8_t>& key_string,
|
||||
const KeyControlBlock& control) :
|
||||
valid_(true), type_(ktype),
|
||||
value_(key_string), has_control_(true),
|
||||
control_(control) {
|
||||
}
|
||||
|
||||
bool Key::setValue(const char* key_string, size_t key_string_length) {
|
||||
valid_ = false;
|
||||
if (!key_string || key_string_length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value_.assign(key_string, key_string + key_string_length);
|
||||
|
||||
if (isValidType() && has_control_) {
|
||||
valid_ = true;
|
||||
}
|
||||
return valid_;
|
||||
}
|
||||
|
||||
bool Key::setType(KeyType ktype) {
|
||||
valid_ = false;
|
||||
type_ = ktype;
|
||||
if (value_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isValidType() && has_control_) {
|
||||
valid_ = true;
|
||||
}
|
||||
return valid_;
|
||||
}
|
||||
|
||||
bool Key::setControl(const KeyControlBlock& control) {
|
||||
valid_ = false;
|
||||
if (!control.valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
control_ = control;
|
||||
has_control_ = true;
|
||||
|
||||
if (isValidType() && !value_.empty()) {
|
||||
valid_ = true;
|
||||
}
|
||||
return valid_;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
117
libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h
Normal file
117
libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef OEMCRYPTO_KEY_MOCK_H_
|
||||
#define OEMCRYPTO_KEY_MOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
enum KeyType {
|
||||
KEYTYPE_UNKNOWN,
|
||||
KEYTYPE_PREPROV,
|
||||
KEYTYPE_ROOT,
|
||||
KEYTYPE_DEVICE,
|
||||
KEYTYPE_CONTENT,
|
||||
KEYTYPE_CONTENT_AUDIO,
|
||||
KEYTYPE_CONTENT_VIDEO,
|
||||
KEYTYPE_MAX
|
||||
};
|
||||
|
||||
const uint32_t kControlObserveDataPath = (1<<31);
|
||||
const uint32_t kControlObserveHDCP = (1<<30);
|
||||
const uint32_t kControlObserveCGMS = (1<<29);
|
||||
const uint32_t kControlDataPathSecure = (1<<4);
|
||||
const uint32_t kControlNonceEnabled = (1<<3);
|
||||
const uint32_t kControlHDCPRequired = (1<<2);
|
||||
const uint32_t kControlCGMSMask = (0x03);
|
||||
const uint32_t kControlCGMSCopyFreely = (0x00);
|
||||
const uint32_t kControlCGMSCopyOnce = (0x02);
|
||||
const uint32_t kControlCGMSCopyNever = (0x03);
|
||||
|
||||
class KeyControlBlock {
|
||||
public:
|
||||
KeyControlBlock() {}
|
||||
KeyControlBlock(const std::vector<uint8_t>& key_control_string) {
|
||||
valid_ = SetFromString(key_control_string);
|
||||
}
|
||||
~KeyControlBlock() {}
|
||||
|
||||
bool SetFromString(const std::vector<uint8_t>& key_control_string);
|
||||
bool Validate();
|
||||
void Invalidate() { valid_ = false; }
|
||||
|
||||
bool valid() const { return valid_; }
|
||||
uint32_t duration() const { return duration_; }
|
||||
uint32_t nonce() const { return nonce_; }
|
||||
uint32_t control_bits() const { return control_bits_; }
|
||||
|
||||
private:
|
||||
|
||||
uint32_t ExtractField(const std::vector<uint8_t>& str, int idx);
|
||||
|
||||
bool valid_;
|
||||
uint32_t verification_;
|
||||
uint32_t duration_;
|
||||
uint32_t nonce_;
|
||||
uint32_t control_bits_;
|
||||
};
|
||||
|
||||
// AES-128 crypto key
|
||||
class Key {
|
||||
public:
|
||||
Key() : valid_(false), type_(KEYTYPE_UNKNOWN), has_control_(false) {}
|
||||
Key(const Key& key) : valid_(key.valid_), type_(key.type_),
|
||||
value_(key.value_),
|
||||
has_control_(key.has_control_),
|
||||
control_(key.control_) {}
|
||||
Key(KeyType type, const std::vector<uint8_t>& key_string,
|
||||
const KeyControlBlock& control);
|
||||
|
||||
virtual ~Key() {};
|
||||
|
||||
// Key is valid iff setValue(), setType(), and setControl() have been called
|
||||
bool setValue(const char* key_string, size_t key_string_length);
|
||||
bool setType(KeyType ktype);
|
||||
bool setControl(const KeyControlBlock& control);
|
||||
bool UpdateControl(const KeyControlBlock& control) { return true; }
|
||||
|
||||
KeyType keyType() { return type_; }
|
||||
const std::vector<uint8_t>& value() const { return value_; }
|
||||
const KeyControlBlock& control() const { return control_; }
|
||||
|
||||
bool isDeviceKey() { return (KEYTYPE_DEVICE == type_); }
|
||||
bool isRootKey() { return (KEYTYPE_ROOT == type_); }
|
||||
bool isPreprovKey() { return (KEYTYPE_PREPROV == type_); }
|
||||
bool isContentKey() {
|
||||
bool ctypes = (KEYTYPE_CONTENT == type_) ||
|
||||
(KEYTYPE_CONTENT_AUDIO == type_) ||
|
||||
(KEYTYPE_CONTENT_VIDEO == type_);
|
||||
return ctypes;
|
||||
}
|
||||
bool isValidType() {
|
||||
return ((KEYTYPE_UNKNOWN < type_) && (KEYTYPE_MAX > type_));
|
||||
}
|
||||
bool isValid() { return valid_; }
|
||||
|
||||
void clear() { value_.clear(); valid_ = false; }
|
||||
|
||||
private:
|
||||
bool valid_;
|
||||
KeyType type_;
|
||||
std::vector<uint8_t> value_;
|
||||
bool has_control_;
|
||||
KeyControlBlock control_;
|
||||
};
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
|
||||
#endif
|
||||
109
libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp
Normal file
109
libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "oemcrypto_keybox_mock.h"
|
||||
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include "log.h"
|
||||
#include "wvcrc32.h"
|
||||
#include "wv_keybox.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
const WidevineKeybox kDefaultKeybox = {
|
||||
// Sample keybox used for test vectors
|
||||
{
|
||||
// deviceID
|
||||
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01
|
||||
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
|
||||
}, {
|
||||
// key
|
||||
0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e,
|
||||
0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04,
|
||||
}, {
|
||||
// data
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
|
||||
0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1,
|
||||
0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd,
|
||||
0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8,
|
||||
0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64,
|
||||
0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8,
|
||||
0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8,
|
||||
0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64,
|
||||
0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39,
|
||||
}, {
|
||||
// magic
|
||||
0x6b, 0x62, 0x6f, 0x78,
|
||||
}, {
|
||||
// Crc
|
||||
0x0a, 0x7a, 0x2c, 0x35,
|
||||
}
|
||||
};
|
||||
|
||||
WvKeybox::WvKeybox() : valid_(false) {
|
||||
Prepare();
|
||||
}
|
||||
|
||||
bool WvKeybox::Prepare() {
|
||||
InstallKeybox(reinterpret_cast<const uint8_t*>(&kDefaultKeybox),
|
||||
sizeof(kDefaultKeybox));
|
||||
valid_ = true;
|
||||
return valid_;
|
||||
}
|
||||
|
||||
KeyboxError WvKeybox::Validate() {
|
||||
if (!valid_) {
|
||||
LOGE("[KEYBOX NOT LOADED]");
|
||||
return OTHER_ERROR;
|
||||
}
|
||||
if (strncmp(reinterpret_cast<char*>(magic_), "kbox", 4) != 0) {
|
||||
LOGE("[KEYBOX HAS BAD MAGIC]");
|
||||
return BAD_MAGIC;
|
||||
}
|
||||
uint32_t crc_computed;
|
||||
uint32_t* crc_stored = (uint32_t*)crc_;
|
||||
WidevineKeybox keybox;
|
||||
memset(&keybox, 0, sizeof(keybox));
|
||||
memcpy(keybox.device_id_, &device_id_[0], device_id_.size());
|
||||
memcpy(keybox.device_key_, &device_key_.value()[0], sizeof(keybox.device_key_));
|
||||
memcpy(keybox.data_, key_data_, sizeof(keybox.data_));
|
||||
memcpy(keybox.magic_, magic_, sizeof(keybox.magic_));
|
||||
|
||||
crc_computed = ntohl(wvcrc32(reinterpret_cast<uint8_t*>(&keybox),
|
||||
sizeof(keybox) - 4)); // Don't include last 4 bytes.
|
||||
if (crc_computed != *crc_stored) {
|
||||
LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n",
|
||||
crc_computed, *crc_stored);
|
||||
return BAD_CRC;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) {
|
||||
if (keyBoxLength != 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const WidevineKeybox* keybox
|
||||
= reinterpret_cast<const WidevineKeybox*>(buffer);
|
||||
device_id_.assign(keybox->device_id_,
|
||||
keybox->device_id_ + sizeof(keybox->device_id_));
|
||||
device_key_.setValue(reinterpret_cast<const char*>(keybox->device_key_),
|
||||
sizeof(keybox->device_key_));
|
||||
device_key_.setType(KEYTYPE_DEVICE);
|
||||
memcpy(key_data_, keybox->data_, sizeof(keybox->data_));
|
||||
memcpy(magic_, keybox->magic_, sizeof(keybox->magic_));
|
||||
memcpy(crc_, keybox->crc_, sizeof(keybox->crc_));
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
50
libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.h
Normal file
50
libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
#define OEMCRYPTO_KEYBOX_MOCK_H_
|
||||
|
||||
#include "oemcrypto_key_mock.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
const int DEVICE_KEY_LENGTH = 16;
|
||||
typedef uint8_t WvKeyboxKey[DEVICE_KEY_LENGTH];
|
||||
|
||||
const int KEY_DATA_LENGTH = 72;
|
||||
typedef uint8_t WvKeyboxKeyData[KEY_DATA_LENGTH];
|
||||
|
||||
enum KeyboxError { NO_ERROR, BAD_CRC, BAD_MAGIC, OTHER_ERROR };
|
||||
|
||||
// Widevine keybox
|
||||
class WvKeybox {
|
||||
public:
|
||||
WvKeybox();
|
||||
~WvKeybox() {}
|
||||
|
||||
KeyboxError Validate();
|
||||
const std::vector<uint8_t>& device_id() { return device_id_; }
|
||||
Key& device_key() { return device_key_; }
|
||||
const WvKeyboxKeyData& key_data() { return key_data_; }
|
||||
size_t key_data_length() { return KEY_DATA_LENGTH; }
|
||||
bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength);
|
||||
|
||||
private:
|
||||
|
||||
bool Prepare();
|
||||
|
||||
bool valid_;
|
||||
std::vector<uint8_t> device_id_;
|
||||
Key device_key_;
|
||||
WvKeyboxKeyData key_data_;
|
||||
uint8_t magic_[4];
|
||||
uint8_t crc_[4];
|
||||
};
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
#endif
|
||||
893
libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp
Normal file
893
libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp
Normal file
@@ -0,0 +1,893 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* mock implementation of OEMCrypto APIs
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
static CryptoEngine* crypto_engine = NULL;
|
||||
|
||||
// Set this to true when you are generating test vectors.
|
||||
const bool trace_all_calls = false;
|
||||
|
||||
typedef struct {
|
||||
uint8_t signature[wvcdm::MAC_KEY_SIZE];
|
||||
uint8_t context[wvcdm::MAC_KEY_SIZE];
|
||||
uint8_t iv[wvcdm::KEY_IV_SIZE];
|
||||
uint8_t enc_rsa_key[];
|
||||
} WrappedRSAKey;
|
||||
|
||||
static void dump_hex(std::string name, const uint8_t* vector, size_t length) {
|
||||
printf("%s = ", name.c_str());
|
||||
if (vector == NULL) {
|
||||
printf("NULL;\n");
|
||||
return;
|
||||
}
|
||||
// TODO(fredgc): replace with HEXEncode.
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (i == 0) {
|
||||
printf("\n wvcdm::a2b_hex(\"");
|
||||
} else if (i % 32 == 0) {
|
||||
printf("\"\n \"");
|
||||
}
|
||||
printf("%02X", vector[i]);
|
||||
}
|
||||
printf("\");\n");
|
||||
}
|
||||
|
||||
void dump_array_part(std::string array, size_t index,
|
||||
std::string name, const uint8_t* vector, size_t length) {
|
||||
if (vector == NULL) {
|
||||
printf("%s[%zu].%s = NULL;\n", array.c_str(), index, name.c_str());
|
||||
return;
|
||||
}
|
||||
printf("std::string s%zu_", index);
|
||||
dump_hex(name, vector, length);
|
||||
printf("%s[%zu].%s = message_ptr + message.find(s%zu_%s.data());\n",
|
||||
array.c_str(), index, name.c_str(), index, name.c_str());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Initialize(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("------------------------- OEMCrypto_Initialize(void)\n");
|
||||
}
|
||||
|
||||
crypto_engine = new CryptoEngine;
|
||||
|
||||
if (!crypto_engine || !crypto_engine->Initialized()) {
|
||||
LOGE("[OEMCrypto_Initialize(): failed]");
|
||||
return OEMCrypto_ERROR_INIT_FAILED;
|
||||
}
|
||||
LOGD("[OEMCrypto_Initialize(): success]");
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Terminate(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n");
|
||||
}
|
||||
|
||||
if (!crypto_engine) {
|
||||
LOGE("[OEMCrypto_Terminate(): failed]");
|
||||
return OEMCrypto_ERROR_TERMINATE_FAILED;
|
||||
}
|
||||
|
||||
if (crypto_engine->Initialized()) {
|
||||
crypto_engine->Terminate();
|
||||
}
|
||||
|
||||
delete crypto_engine;
|
||||
crypto_engine = NULL;
|
||||
LOGD("[OEMCrypto_Terminate(): success]");
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session)\n");
|
||||
}
|
||||
SessionId sid = crypto_engine->CreateSession();
|
||||
*session = (OEMCrypto_SESSION)sid;
|
||||
LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session)\n");
|
||||
}
|
||||
if (!crypto_engine->DestroySession((SessionId)session)) {
|
||||
LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session);
|
||||
return OEMCrypto_ERROR_CLOSE_SESSION_FAILED;
|
||||
} else {
|
||||
LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
||||
uint32_t* nonce) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,\n");
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
uint32_t nonce_value;
|
||||
uint8_t* nonce_string = reinterpret_cast<uint8_t*>(&nonce_value);
|
||||
|
||||
// Generate 4 bytes of random data
|
||||
if (!RAND_bytes(nonce_string, 4)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
session_ctx->AddNonce(nonce_value);
|
||||
*nonce = nonce_value;
|
||||
if (trace_all_calls) {
|
||||
printf("nonce = %08x\n", nonce_value);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
const uint8_t* mac_key_context,
|
||||
uint32_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
uint32_t enc_key_context_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n");
|
||||
dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
|
||||
mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t> enc_ctx_str(enc_key_context,
|
||||
enc_key_context + enc_key_context_length);
|
||||
|
||||
// Generate mac and encryption keys for current session context
|
||||
if (!session_ctx->DeriveKeys(mac_ctx_str, enc_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (trace_all_calls) {
|
||||
dump_hex("mac_key", &session_ctx->mac_key()[0],
|
||||
session_ctx->mac_key().size());
|
||||
dump_hex("enc_key", &session_ctx->encryption_key()[0],
|
||||
session_ctx->encryption_key().size());
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n");
|
||||
dump_hex("message", message, message_length);
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
if (session_ctx->GenerateSignature(message,
|
||||
message_length,
|
||||
signature,
|
||||
signature_length)) {
|
||||
if (trace_all_calls) {
|
||||
dump_hex("signature", signature, *signature_length);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;;
|
||||
}
|
||||
|
||||
bool RangeCheck(const uint8_t* message,
|
||||
uint32_t message_length,
|
||||
const uint8_t* field,
|
||||
uint32_t field_length,
|
||||
bool allow_null) {
|
||||
if (field == NULL) return allow_null;
|
||||
if (field < message) return false;
|
||||
if (field + field_length > message + message_length) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv,
|
||||
const uint8_t* enc_mac_key,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n");
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE);
|
||||
dump_hex("enc_mac_key", enc_mac_key, wvcdm::MAC_KEY_SIZE);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
printf("key_array[%zu].key_id_length=%zu;\n", i, key_array[i].key_id_length);
|
||||
dump_array_part("key_array", i, "key_id",
|
||||
key_array[i].key_id, key_array[i].key_id_length);
|
||||
dump_array_part("key_array", i, "key_data_iv",
|
||||
key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE);
|
||||
dump_array_part("key_array", i, "key_data",
|
||||
key_array[i].key_data, wvcdm::KEY_IV_SIZE);
|
||||
dump_array_part("key_array", i, "key_control_iv",
|
||||
key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE);
|
||||
dump_array_part("key_array", i, "key_control",
|
||||
key_array[i].key_control, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0 ||
|
||||
key_array == NULL || num_keys == 0) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, enc_mac_key,
|
||||
wvcdm::MAC_KEY_SIZE, true) ||
|
||||
!RangeCheck(message, message_length, enc_mac_key_iv,
|
||||
wvcdm::KEY_IV_SIZE, true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
if (!RangeCheck(message, message_length, key_array[i].key_id,
|
||||
key_array[i].key_id_length, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_data,
|
||||
wvcdm::KEY_SIZE, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_data_iv,
|
||||
wvcdm::KEY_IV_SIZE, false) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_control,
|
||||
wvcdm::KEY_CONTROL_SIZE, true) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_control_iv,
|
||||
wvcdm::KEY_IV_SIZE, true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE -range check %d]", i);
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate message signature
|
||||
if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
session_ctx->StartTimer();
|
||||
|
||||
// Decrypt and install keys in key object
|
||||
// Each key will have a key control block. They will all have the same nonce.
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> enc_key_data;
|
||||
std::vector<uint8_t> key_data_iv;
|
||||
std::vector<uint8_t> key_control;
|
||||
std::vector<uint8_t> key_control_iv;
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
key_id.assign(key_array[i].key_id,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
enc_key_data.assign(key_array[i].key_data,
|
||||
key_array[i].key_data + wvcdm::KEY_SIZE);
|
||||
key_data_iv.assign(key_array[i].key_data_iv,
|
||||
key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE);
|
||||
if (key_array[i].key_control == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
key_control.assign(key_array[i].key_control,
|
||||
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
||||
key_control_iv.assign(key_array[i].key_control_iv,
|
||||
key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE);
|
||||
|
||||
if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control,
|
||||
key_control_iv)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// All keys processed. Flush nonce table
|
||||
session_ctx->FlushNonces();
|
||||
|
||||
// enc_mac_key can be NULL if license renewal is not supported
|
||||
if (enc_mac_key == NULL) return OEMCrypto_SUCCESS;
|
||||
|
||||
// V2 license protocol: update mac key after processing license response
|
||||
const std::vector<uint8_t> enc_mac_key_str = std::vector<uint8_t>(
|
||||
enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE);
|
||||
const std::vector<uint8_t> enc_mac_key_iv_str = std::vector<uint8_t>(
|
||||
enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE);
|
||||
|
||||
if (!session_ctx->UpdateMacKey(enc_mac_key_str, enc_mac_key_iv_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_KeyRefreshObject* key_array) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,\n");
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_RefreshKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// Range check
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
if (!RangeCheck(message, message_length, key_array[i].key_id,
|
||||
key_array[i].key_id_length, true) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_control,
|
||||
wvcdm::KEY_CONTROL_SIZE, true) ||
|
||||
!RangeCheck(message, message_length, key_array[i].key_control_iv,
|
||||
wvcdm::KEY_IV_SIZE, true)) {
|
||||
LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate message signature
|
||||
if (!session_ctx->ValidateMessage(message, message_length,
|
||||
signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
// Decrypt and refresh keys in key refresh object
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> key_control;
|
||||
std::vector<uint8_t> key_control_iv;
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
// TODO(gmorgan): key_id may be null if special control key type (TBS)
|
||||
if (key_array[i].key_id != NULL) {
|
||||
key_id.assign(key_array[i].key_id,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
} else {
|
||||
key_id.clear();
|
||||
}
|
||||
if (key_array[i].key_control != NULL) {
|
||||
key_control.assign(key_array[i].key_control,
|
||||
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
||||
key_control_iv.assign(key_array[i].key_control_iv,
|
||||
key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE);
|
||||
} else {
|
||||
key_control.clear();
|
||||
key_control_iv.clear();
|
||||
}
|
||||
|
||||
if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
session_ctx->StartTimer();
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,\n");
|
||||
dump_hex("key_id", key_id, key_id_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> key_id_str = std::vector<uint8_t>(key_id, key_id + key_id_length);
|
||||
if (!session_ctx->SelectContentKey(key_id_str)) {
|
||||
LOGE("[OEMCrypto_SelectKey(): FAIL]");
|
||||
return OEMCrypto_ERROR_NO_CONTENT_KEY;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
|
||||
const uint8_t* data_addr,
|
||||
size_t data_length,
|
||||
bool is_encrypted,
|
||||
const uint8_t* iv,
|
||||
size_t offset,
|
||||
const OEMCrypto_DestBufferDesc* out_buffer) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,\n");
|
||||
}
|
||||
wvoec_mock::BufferType buffer_type = kBufferTypeDirect;
|
||||
void* destination = NULL;
|
||||
size_t max_length = 0;
|
||||
switch (out_buffer->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
buffer_type = kBufferTypeClear;
|
||||
destination = out_buffer->buffer.clear.address;
|
||||
max_length = out_buffer->buffer.clear.max_length;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_type = kBufferTypeSecure;
|
||||
destination = out_buffer->buffer.secure.handle;
|
||||
max_length = out_buffer->buffer.secure.max_length;
|
||||
break;
|
||||
default:
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
buffer_type = kBufferTypeDirect;
|
||||
destination = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buffer_type != kBufferTypeDirect && max_length < data_length) {
|
||||
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]");
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_DecryptCTR(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
if (data_addr == NULL || data_length == 0 ||
|
||||
iv == NULL || out_buffer == NULL) {
|
||||
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> iv_v(iv, iv + 16);
|
||||
std::vector<uint8_t> content(data_addr, data_addr + data_length);
|
||||
|
||||
if (!crypto_engine->DecryptCTR(session_ctx, iv_v, (int)offset,
|
||||
content, is_encrypted,
|
||||
destination, buffer_type)) {
|
||||
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n");
|
||||
}
|
||||
if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_WRITE_KEYBOX;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n");
|
||||
}
|
||||
switch(crypto_engine->ValidateKeybox()) {
|
||||
case NO_ERROR: return OEMCrypto_SUCCESS;
|
||||
case BAD_CRC: return OEMCrypto_ERROR_BAD_CRC;
|
||||
case BAD_MAGIC: return OEMCrypto_ERROR_BAD_MAGIC;
|
||||
default:
|
||||
case OTHER_ERROR: return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
size_t* idLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n");
|
||||
}
|
||||
std::vector<uint8_t> dev_id_string = crypto_engine->keybox().device_id();
|
||||
if (dev_id_string.empty()) {
|
||||
LOGE("[OEMCrypto_GetDeviceId(): Keybox Invalid]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
size_t dev_id_len = dev_id_string.size();
|
||||
if (*idLength < dev_id_len) {
|
||||
*idLength = dev_id_len;
|
||||
LOGE("[OEMCrypto_GetDeviceId(): ERROR_SHORT_BUFFER]");
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memset(deviceID, 0, *idLength);
|
||||
memcpy(deviceID, &dev_id_string[0], dev_id_len);
|
||||
*idLength = dev_id_len;
|
||||
LOGD("[OEMCrypto_GetDeviceId(): success]");
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
size_t* keyDataLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n");
|
||||
}
|
||||
size_t length = crypto_engine->keybox().key_data_length();
|
||||
if (*keyDataLength < length) {
|
||||
*keyDataLength = length;
|
||||
LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]");
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memset(keyData, 0, *keyDataLength);
|
||||
memcpy(keyData, crypto_engine->keybox().key_data(), length);
|
||||
*keyDataLength = length;
|
||||
LOGD("[OEMCrypto_GetKeyData(): success]");
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n");
|
||||
}
|
||||
if (!randomData) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (RAND_bytes(randomData, dataLength)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
uint8_t* wrappedKeybox,
|
||||
size_t* wrappedKeyBoxLength,
|
||||
const uint8_t* transportKey,
|
||||
size_t transportKeyLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n");
|
||||
}
|
||||
if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength
|
||||
|| (keyBoxLength != *wrappedKeyBoxLength)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
// This implementation ignores the transport key. For test keys, we
|
||||
// don't need to encrypt the keybox.
|
||||
memcpy(wrappedKeybox, keybox, keyBoxLength);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
uint32_t* nonce,
|
||||
const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
uint8_t* wrapped_rsa_key,
|
||||
size_t* wrapped_rsa_key_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n");
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
printf("nonce = %08X;\n", *nonce);
|
||||
dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length);
|
||||
dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
if (wrapped_rsa_key_length == NULL) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
// For the reference implementation, the wrapped key and the encrypted
|
||||
// key are the same size -- just encrypted with different keys.
|
||||
// We add 32 bytes for a context, and 32 bytes for a signature.
|
||||
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
|
||||
size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
||||
|
||||
if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) {
|
||||
LOGW("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
||||
*wrapped_rsa_key_length = buffer_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
*wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used.
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (message == NULL || message_length == 0 || signature == NULL
|
||||
|| signature_length == 0 || nonce == NULL || enc_rsa_key == NULL) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, reinterpret_cast<uint8_t*>(nonce),
|
||||
sizeof(uint32_t), true) ||
|
||||
!RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length,
|
||||
true) ||
|
||||
!RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE,
|
||||
true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Validate nonce
|
||||
if (!session_ctx->CheckNonce(*nonce)) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
session_ctx->FlushNonces();
|
||||
|
||||
// Decrypt key and verify signature.
|
||||
if (!session_ctx->LoadRSAKey(enc_rsa_key, enc_rsa_key_length,
|
||||
enc_rsa_key_iv, message, message_length,
|
||||
signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
// return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
||||
}
|
||||
|
||||
// Now we generate a wrapped keybox.
|
||||
WrappedRSAKey* wrapped = reinterpret_cast<WrappedRSAKey*>(wrapped_rsa_key);
|
||||
// Pick a random context and IV for generating keys.
|
||||
if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
const std::vector<uint8_t> mac_ctx_str(wrapped->context,
|
||||
wrapped->context + sizeof(wrapped->context));
|
||||
// Generate mac and encryption keys for encrypting the signature.
|
||||
if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
size_t sig_length = sizeof(wrapped->signature);
|
||||
if (!session_ctx->GenerateSignature(wrapped->context, // start signing here.
|
||||
buffer_size - sizeof(wrapped->signature),
|
||||
wrapped->signature,
|
||||
&sig_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
// Encrypt rsa key with keybox.
|
||||
if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key,
|
||||
enc_rsa_key_length,
|
||||
wrapped->iv)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
if (trace_all_calls) {
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* wrapped_rsa_key,
|
||||
size_t wrapped_rsa_key_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n");
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length);
|
||||
}
|
||||
|
||||
if (wrapped_rsa_key == NULL) {
|
||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
// Now we generate a wrapped keybox.
|
||||
const WrappedRSAKey* wrapped
|
||||
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||
const std::vector<uint8_t> mac_ctx_str(wrapped->context,
|
||||
wrapped->context + sizeof(wrapped->context));
|
||||
// Generate mac and encryption keys for encrypting the signature.
|
||||
if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
// Decrypt key and verify signature.
|
||||
if (!session_ctx->LoadRSAKey(wrapped->enc_rsa_key,
|
||||
wrapped_rsa_key_length - sizeof(WrappedRSAKey),
|
||||
wrapped->iv,
|
||||
wrapped->context,
|
||||
wrapped_rsa_key_length - sizeof(wrapped->signature),
|
||||
wrapped->signature,
|
||||
sizeof(wrapped->signature))) {
|
||||
return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n");
|
||||
dump_hex("message", message, message_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
if (session_ctx->GenerateRSASignature(message,
|
||||
message_length,
|
||||
signature,
|
||||
signature_length)) {
|
||||
if (trace_all_calls) {
|
||||
dump_hex("signature", signature, *signature_length);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
OEMCrypto_SESSION session,
|
||||
const uint8_t* enc_session_key,
|
||||
size_t enc_session_key_length,
|
||||
const uint8_t* mac_key_context,
|
||||
size_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
size_t enc_key_context_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n");
|
||||
dump_hex("enc_session_key", enc_session_key, enc_session_key_length);
|
||||
dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length);
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> ssn_key_str(enc_session_key,
|
||||
enc_session_key + enc_session_key_length);
|
||||
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
|
||||
mac_key_context + mac_key_context_length);
|
||||
const std::vector<uint8_t> enc_ctx_str(enc_key_context,
|
||||
enc_key_context + enc_key_context_length);
|
||||
|
||||
// Generate mac and encryption keys for current session context
|
||||
if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (trace_all_calls) {
|
||||
dump_hex("mac_key", &session_ctx->mac_key()[0],
|
||||
session_ctx->mac_key().size());
|
||||
dump_hex("enc_key", &session_ctx->encryption_key()[0],
|
||||
session_ctx->encryption_key().size());
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_mock
|
||||
91
libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp
Normal file
91
libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "string_conversions.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
static bool CharToDigit(char ch, unsigned char* digit) {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
*digit = ch - '0';
|
||||
} else {
|
||||
ch = tolower(ch);
|
||||
if ((ch >= 'a') && (ch <= 'f')) {
|
||||
*digit = ch - 'a' + 10;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// converts an ascii hex string(2 bytes per digit) into a decimal byte string
|
||||
std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
std::vector<uint8_t> array(0);
|
||||
unsigned int count = byte.size();
|
||||
if (count == 0 || (count % 2) != 0) {
|
||||
LOGE("Invalid input size %u for string %s", count, byte.c_str());
|
||||
return array;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < count / 2; ++i) {
|
||||
unsigned char msb = 0; // most significant 4 bits
|
||||
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);
|
||||
return array;
|
||||
}
|
||||
array.push_back((msb << 4) | lsb);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
std::string b2a_hex(const std::vector<uint8_t>& byte) {
|
||||
return HexEncode(&byte[0], byte.size());
|
||||
}
|
||||
|
||||
std::string HexEncode(const uint8_t* in_buffer, unsigned int size) {
|
||||
static const char kHexChars[] = "0123456789ABCDEF";
|
||||
|
||||
// Each input byte creates two output hex characters.
|
||||
std::string out_buffer(size * 2, '\0');
|
||||
|
||||
for (unsigned int i = 0; i < size; ++i) {
|
||||
char byte = in_buffer[i];
|
||||
out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf];
|
||||
out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf];
|
||||
}
|
||||
return out_buffer;
|
||||
}
|
||||
|
||||
std::string IntToString(int value) {
|
||||
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
|
||||
// So round up to allocate 3 output characters per byte, plus 1 for '-'.
|
||||
const int kOutputBufSize = 3 * sizeof(int) + 1;
|
||||
char buffer[kOutputBufSize];
|
||||
memset(buffer, 0, kOutputBufSize);
|
||||
snprintf(buffer, kOutputBufSize, "%d", value);
|
||||
|
||||
std::string out_string(buffer, sizeof(buffer));
|
||||
return out_string;
|
||||
}
|
||||
|
||||
std::string UintToString(unsigned int value) {
|
||||
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
|
||||
// So round up to allocate 3 output characters per byte.
|
||||
const int kOutputBufSize = 3 * sizeof(unsigned int);
|
||||
char buffer[kOutputBufSize];
|
||||
memset(buffer, 0, kOutputBufSize);
|
||||
snprintf(buffer, kOutputBufSize, "%u", value);
|
||||
|
||||
std::string out_string(buffer, sizeof(buffer));
|
||||
return out_string;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
20
libwvdrmengine/oemcrypto/mock/src/string_conversions.h
Normal file
20
libwvdrmengine/oemcrypto/mock/src/string_conversions.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef OEMCRYPTO_STRING_CONVERSIONS_H_
|
||||
#define OEMCRYPTO_STRING_CONVERSIONS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
std::vector<uint8_t> a2b_hex(const std::string& b);
|
||||
std::string b2a_hex(const std::vector<uint8_t>& b);
|
||||
std::string HexEncode(const uint8_t* bytes, unsigned size);
|
||||
std::string IntToString(int value);
|
||||
std::string UintToString(unsigned int value);
|
||||
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // OEMCRYPTO_STRING_CONVERSIONS_H_
|
||||
14
libwvdrmengine/oemcrypto/mock/src/wv_cdm_constants.h
Normal file
14
libwvdrmengine/oemcrypto/mock/src/wv_cdm_constants.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef OEMCRYPTO_WV_CDM_CONSTANTS_H_
|
||||
#define OEMCRYPTO_WV_CDM_CONSTANTS_H_
|
||||
|
||||
namespace wvcdm {
|
||||
static const size_t KEY_CONTROL_SIZE = 16;
|
||||
static const size_t KEY_IV_SIZE = 16;
|
||||
static const size_t KEY_PAD_SIZE = 16;
|
||||
static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // OEMCRYPTO_WV_CDM_CONSTANTS_H_
|
||||
49
libwvdrmengine/oemcrypto/mock/src/wv_cdm_types.h
Normal file
49
libwvdrmengine/oemcrypto/mock/src/wv_cdm_types.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef OEMCRYPTO_WV_CDM_TYPES_H_
|
||||
#define OEMCRYPTO_WV_CDM_TYPES_H_
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::string CdmKeySystem;
|
||||
typedef std::string CdmInitData;
|
||||
typedef std::string CdmKeyMessage;
|
||||
typedef std::string CdmKeyResponse;
|
||||
typedef std::string KeyId;
|
||||
typedef std::string CdmSessionId;
|
||||
typedef std::string RequestId;
|
||||
typedef uint32_t CryptoResult;
|
||||
typedef uint32_t CryptoSessionId;
|
||||
typedef std::string CryptoKeyId;
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
KEY_ADDED,
|
||||
KEY_ERROR,
|
||||
KEY_MESSAGE,
|
||||
NEED_KEY,
|
||||
KEY_CANCELED,
|
||||
};
|
||||
|
||||
#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
enum CdmEventType {
|
||||
LICENSE_EXPIRED,
|
||||
LICENSE_RENEWAL_NEEDED
|
||||
};
|
||||
|
||||
// forward class references
|
||||
class KeyMessage;
|
||||
class Request;
|
||||
class Key;
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // OEMCRYPTO_WV_CDM_TYPES_H_
|
||||
24
libwvdrmengine/oemcrypto/mock/src/wv_keybox.h
Normal file
24
libwvdrmengine/oemcrypto/mock/src/wv_keybox.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WV_KEYBOX_H_
|
||||
#define WV_KEYBOX_H_
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
// This is the format of a Widevine keybox.
|
||||
typedef struct { // 128 bytes total.
|
||||
// C character string identifying the device. Null terminated.
|
||||
uint8_t device_id_[32];
|
||||
// 128 bit AES key assigned to device. Generated by Widevine.
|
||||
uint8_t device_key_[16];
|
||||
// Key Data. Encrypted data.
|
||||
uint8_t data_[72];
|
||||
// Constant code used to recognize a valid keybox "kbox" = 0x6b626f78.
|
||||
uint8_t magic_[4];
|
||||
// The CRC checksum of the first 124 bytes of the keybox.
|
||||
uint8_t crc_[4];
|
||||
} WidevineKeybox;
|
||||
|
||||
}
|
||||
|
||||
#endif // WV_KEYBOX_H_
|
||||
93
libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp
Normal file
93
libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*********************************************************************
|
||||
* wvcrc32.cpp
|
||||
*
|
||||
* (c) Copyright 2011-2012 Google, Inc.
|
||||
*
|
||||
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
*********************************************************************/
|
||||
|
||||
#include "wvcrc32.h"
|
||||
|
||||
#define INIT_CRC32 0xffffffff
|
||||
|
||||
uint32_t wvrunningcrc32(uint8_t* p_begin, int i_count, uint32_t i_crc) {
|
||||
static uint32_t CRC32[256] = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
||||
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
|
||||
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
|
||||
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
|
||||
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
|
||||
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
|
||||
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
|
||||
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
|
||||
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
|
||||
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
|
||||
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
|
||||
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
|
||||
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
|
||||
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
|
||||
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
|
||||
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
|
||||
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
|
||||
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
|
||||
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
|
||||
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
|
||||
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
|
||||
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
|
||||
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
|
||||
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
|
||||
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
|
||||
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
|
||||
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
|
||||
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
|
||||
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
|
||||
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
|
||||
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
|
||||
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
|
||||
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
|
||||
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
|
||||
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
/* Calculate the CRC */
|
||||
while (i_count > 0) {
|
||||
i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin) ];
|
||||
p_begin++;
|
||||
i_count--;
|
||||
}
|
||||
|
||||
return(i_crc);
|
||||
}
|
||||
|
||||
uint32_t wvcrc32(uint8_t* p_begin, int i_count) {
|
||||
return(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
|
||||
}
|
||||
16
libwvdrmengine/oemcrypto/mock/src/wvcrc32.h
Normal file
16
libwvdrmengine/oemcrypto/mock/src/wvcrc32.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*********************************************************************
|
||||
* wvcrc32.h
|
||||
*
|
||||
* (c) Copyright 2011-2012 Google, Inc.
|
||||
*
|
||||
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef WV_CRC_32_H_
|
||||
#define WV_CRC_32_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t wvcrc32(uint8_t* p_begin, int i_count);
|
||||
|
||||
#endif // WV_CRC_32_H_
|
||||
Reference in New Issue
Block a user