Version 17 plus test updates and OPK v17
This is the first public release of OPK v17. See the file CHANGELOG.md for details.
This commit is contained in:
121
CHANGELOG.md
Normal file
121
CHANGELOG.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Widevine OEMCrypto, ODK, and OPK Changelog
|
||||
|
||||
[TOC]
|
||||
|
||||
## [Version 17 plus test updates and OPK v17][v17+test-updates+opk]
|
||||
|
||||
This release contains the first partner release version of OPK, which is also
|
||||
the first version of OPK to support OEMCrypto v17. OPK v17 represents a
|
||||
considerable upgrade from the previous beta releases and makes many significant
|
||||
changes to the WTPI. This release includes sample ports to both the OP-TEE and
|
||||
Trusty TEE OSes. The Trusty port has been tested on the Pixel 6 and the OP-TEE
|
||||
port has been tested on the NXP iMX8 reference board. See their respective
|
||||
README.md files for platform-specific instructions and an explanation of any
|
||||
failing tests.
|
||||
|
||||
This release of OPK still uses Provisioning 2.0 (keyboxes). Provisioning 4.0 has
|
||||
not yet been tested, and support for it is incomplete. We expect there to be
|
||||
another release with updates to support Provisioning 4.0 in the near future. Our
|
||||
intention is to continue to support both Provisioning 2.0 and 4.0. Devices that
|
||||
plan to use Provisioning 4.0 must support ECC and have enough entropy to
|
||||
generate ephemeral keys on the device.
|
||||
|
||||
Beyond OPK, this release contains several small updates to OEMCrypto and ODK:
|
||||
|
||||
- ODK has been updated to use version 17 core messages by default.
|
||||
- `ERROR_INVALID_RSA_KEY` has been renamed to `ERROR_INVALID_KEY` in order to
|
||||
make it clearer that this error also applies when the key is an elliptic curve
|
||||
key.
|
||||
- The deprecated SRM update functions have been removed from the OEMCrypto
|
||||
header.
|
||||
|
||||
This release also contains several updates to the OEMCrypto unit tests:
|
||||
|
||||
- The fuzz tests have been updated to be compatible with OEMCrypto v17.
|
||||
- A test has been added that verifies the device can load at least as many DRM
|
||||
keys as promised by its resource rating tier.
|
||||
- A test has been added to verify that loading invalid usage entries fails.
|
||||
- An issue in `TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths` where the
|
||||
test attempted to load the license before encrypting and sigining it has been
|
||||
addressed.
|
||||
- An issue where some tests were not including a nonce in all license requests
|
||||
has been fixed.
|
||||
|
||||
## [Version 17][v17-initial-release]
|
||||
|
||||
Initial release of OEMCrypto v17 unit tests and documentation.
|
||||
See https://developers.google.com/widevine/drm/client/oemcrypto/v17/delta for
|
||||
changes since v16.
|
||||
|
||||
|
||||
## [Version 16.4 plus opk beta 2][v16.4+opk-beta2]
|
||||
|
||||
Second beta release of the OEMCrypto Porting Kit (OPK), supporting OEMCrypto v16.
|
||||
|
||||
The following changes are included with this update:
|
||||
- Add makefiles to build OEMCrypto TA and host apps for OP-TEE. See
|
||||
`oemcrypto/opk/ports/optee/README.md` for information on how to build with make
|
||||
- Update missing and outdated files such as `odk_message.h` and
|
||||
`OEMCryptoCENCCommon.h`
|
||||
- Rename WTPI interface files with common WTPI prefix
|
||||
- Add more WTPI unit tests for crypto functions
|
||||
- Replace DER parsing code in OEMCrypto TA OPTEE port with mbedtls
|
||||
implementation
|
||||
- Update oemcrypto unittests
|
||||
|
||||
Using the default make settings and an external OP-TEE repository setup, the
|
||||
OEMCrypto TA port is now buildable for QEMU. Slight changes to environment
|
||||
variables will enable STM32MP1 and NXP iMX8 targets. Keep in mind that the
|
||||
performance capabilities of QEMU and the STM32MP1 platforms do not meet the
|
||||
timing requirements for many oemcrypto unittests; so far we have only passed all
|
||||
tests on the NXP hardware.
|
||||
|
||||
This update does not include any Trusty port code.
|
||||
|
||||
## [Version 16.4 plus opk beta][v16.4+opk-beta]
|
||||
|
||||
Initial beta release of the OEMCrypto Porting Kit (OPK), supporting OEMCrypto v16.
|
||||
|
||||
## [Version 16.4 doc updates][v16.4+doc-updates]
|
||||
|
||||
Documentation updates. All headers have been updated so that documentation may
|
||||
be extracted using Doxygen. Documentation can now be found at
|
||||
https://developers.google.com/widevine/drm/client/oemcrypto
|
||||
|
||||
|
||||
## [Version 16.4 plus extra tests][v16.4+extra-test]
|
||||
|
||||
We have added several new tests to the OEMCrypto test suite in order to identify
|
||||
and fix certain types of security issues that are being discovered and disclosed
|
||||
by security researchers. Widevine strongly recommends these additional security
|
||||
tests, in order to minimize the risk and exposure from external security
|
||||
research.
|
||||
|
||||
Most of the new tests are checking for buffer overflow and off-by-one
|
||||
errors. They verify that OEMCrypto correctly handles the case where input
|
||||
buffers are larger than output buffers; total subsamples are larger than
|
||||
samples; and message buffers are much larger than required. OEMCrypto is
|
||||
expected to accept bad input and fail gracefully. Failing these tests is an
|
||||
indication that there might be a security risk.
|
||||
|
||||
Because buffer overflow bugs might crash the device or cause a seg fault, these
|
||||
tests might fail and then stop running. For this reason, you cannot assume that
|
||||
your device is passing all of the tests if you don't see FAIL in the
|
||||
output. Instead, you should look for a summary at the end of the test suite
|
||||
output saying that all the tests passed. See the README.md in oemcrypto/test
|
||||
for more details.
|
||||
|
||||
|
||||
## [Version 16.4][v16.4]
|
||||
|
||||
Public release for OEMCrypto API and ODK library version 16.4.
|
||||
|
||||
|
||||
|
||||
[v16.4]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v16.4
|
||||
[v16.4+extra-test]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v16.4+extra-tests
|
||||
[v16.4+doc-updates]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v16.4+doc-updates
|
||||
[v16.4+opk-beta]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v16.4+opk-beta
|
||||
[v16.4+opk-beta2]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v16.4+opk-beta2
|
||||
[v17-initial-release]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17-initial-release
|
||||
[v17+test-updates+opk]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17+test-updates+opk
|
||||
296
linux/src/file_store.cpp
Normal file
296
linux/src/file_store.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// File class - provides a simple file implementation
|
||||
|
||||
#include "file_store.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
const char kCurrentDirectory[] = ".";
|
||||
const char kParentDirectory[] = "..";
|
||||
const char kDirectoryDelimiter = '/';
|
||||
const char kWildcard[] = "*";
|
||||
|
||||
bool IsCurrentOrParentDirectory(char* dir) {
|
||||
return strcmp(dir, kCurrentDirectory) == 0 ||
|
||||
strcmp(dir, kParentDirectory) == 0;
|
||||
}
|
||||
|
||||
bool IsDirectory(const std::string& path) {
|
||||
struct stat buf;
|
||||
if (stat(path.c_str(), &buf) == 0)
|
||||
return buf.st_mode & S_IFDIR;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CreateDirectory(const std::string& path_in) {
|
||||
std::string path = path_in;
|
||||
size_t size = path.size();
|
||||
if ((size == 1) && (path[0] == kDirectoryDelimiter)) return true;
|
||||
|
||||
if (size <= 1) return false;
|
||||
|
||||
size_t pos = path.find(kDirectoryDelimiter, 1);
|
||||
while (pos < size) {
|
||||
path[pos] = '\0';
|
||||
if (mkdir(path.c_str(), 0700) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
LOGW("File::CreateDirectory: mkdir failed: %d, %s", errno,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
path[pos] = kDirectoryDelimiter;
|
||||
pos = path.find(kDirectoryDelimiter, pos + 1);
|
||||
}
|
||||
|
||||
if (path[size - 1] != kDirectoryDelimiter) {
|
||||
if (mkdir(path.c_str(), 0700) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
LOGW("File::CreateDirectory: mkdir failed: %d, %s", errno,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace wvutil {
|
||||
|
||||
class FileImpl : public File {
|
||||
public:
|
||||
FileImpl() {}
|
||||
|
||||
void FlushFile() {
|
||||
fflush(file_);
|
||||
fsync(fileno(file_));
|
||||
}
|
||||
|
||||
~FileImpl() override {
|
||||
if (file_) {
|
||||
FlushFile();
|
||||
fclose(file_);
|
||||
file_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t Read(char* buffer, size_t bytes) override {
|
||||
if (!buffer) {
|
||||
LOGW("File::Read: buffer is empty");
|
||||
return -1;
|
||||
}
|
||||
if (!file_) {
|
||||
LOGW("File::Read: file not open");
|
||||
return -1;
|
||||
}
|
||||
size_t len = fread(buffer, sizeof(char), bytes, file_);
|
||||
if (len != bytes) {
|
||||
LOGW("File::Read: fread failed: %d, %s", errno, strerror(errno));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t Write(const char* buffer, size_t bytes) override {
|
||||
if (!buffer) {
|
||||
LOGW("File::Write: buffer is empty");
|
||||
return -1;
|
||||
}
|
||||
if (!file_) {
|
||||
LOGW("File::Write: file not open");
|
||||
return -1;
|
||||
}
|
||||
size_t len = fwrite(buffer, sizeof(char), bytes, file_);
|
||||
if (len != bytes) {
|
||||
LOGW("File::Write: fwrite failed: %d, %s", errno, strerror(errno));
|
||||
}
|
||||
FlushFile();
|
||||
return len;
|
||||
}
|
||||
|
||||
FILE* file_;
|
||||
std::string file_path_;
|
||||
};
|
||||
|
||||
class FileSystem::Impl {};
|
||||
|
||||
FileSystem::FileSystem() {}
|
||||
FileSystem::FileSystem(const std::string& origin, void*) : origin_(origin) {}
|
||||
FileSystem::~FileSystem() {}
|
||||
|
||||
std::unique_ptr<File> FileSystem::Open(const std::string& name, int flags) {
|
||||
std::string open_flags;
|
||||
|
||||
// create the enclosing directory if it does not exist
|
||||
size_t delimiter_pos = name.rfind(kDirectoryDelimiter);
|
||||
if (delimiter_pos != std::string::npos) {
|
||||
std::string dir_path = name.substr(0, delimiter_pos);
|
||||
if ((flags & FileSystem::kCreate) && !Exists(dir_path))
|
||||
CreateDirectory(dir_path);
|
||||
}
|
||||
|
||||
// ensure only owners has access
|
||||
mode_t old_mask = umask(077);
|
||||
if (((flags & FileSystem::kTruncate) && Exists(name)) ||
|
||||
((flags & FileSystem::kCreate) && !Exists(name))) {
|
||||
FILE* fp = fopen(name.c_str(), "w+");
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
open_flags = (flags & FileSystem::kReadOnly) ? "rb" : "rb+";
|
||||
|
||||
std::unique_ptr<FileImpl> file_impl(new FileImpl());
|
||||
file_impl->file_ = fopen(name.c_str(), open_flags.c_str());
|
||||
umask(old_mask);
|
||||
if (!file_impl->file_) {
|
||||
LOGW("File::Open: fopen failed: %d, %s", errno, strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
file_impl->file_path_ = name;
|
||||
return file_impl;
|
||||
}
|
||||
|
||||
bool FileSystem::Exists(const std::string& path) {
|
||||
struct stat buf;
|
||||
int res = stat(path.c_str(), &buf) == 0;
|
||||
if (!res) {
|
||||
LOGV("File::Exists: stat failed: %d, %s", errno, strerror(errno));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool FileSystem::Remove(const std::string& path) {
|
||||
if (IsDirectory(path)) {
|
||||
// Handle directory deletion
|
||||
DIR* dir;
|
||||
if ((dir = opendir(path.c_str())) != nullptr) {
|
||||
// first remove files and dir within it
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (!IsCurrentOrParentDirectory(entry->d_name)) {
|
||||
std::string path_to_remove = path + kDirectoryDelimiter;
|
||||
path_to_remove += entry->d_name;
|
||||
if (!Remove(path_to_remove)) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
if (rmdir(path.c_str())) {
|
||||
LOGW("File::Remove: rmdir failed: %d, %s", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
size_t wildcard_pos = path.find(kWildcard);
|
||||
if (wildcard_pos == std::string::npos) {
|
||||
// Handle file deletion
|
||||
if (unlink(path.c_str()) && (errno != ENOENT)) {
|
||||
LOGW("File::Remove: unlink failed: %d, %s", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Handle wildcard specified file deletion
|
||||
size_t delimiter_pos = path.rfind(kDirectoryDelimiter, wildcard_pos);
|
||||
if (delimiter_pos == std::string::npos) {
|
||||
LOGW("File::Remove: unable to find path delimiter before wildcard");
|
||||
return false;
|
||||
}
|
||||
|
||||
DIR* dir;
|
||||
std::string dir_path = path.substr(0, delimiter_pos);
|
||||
if ((dir = opendir(dir_path.c_str())) == nullptr) {
|
||||
LOGW("File::Remove: directory open failed for wildcard");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
std::string ext = path.substr(wildcard_pos + 1);
|
||||
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
size_t filename_len = strlen(entry->d_name);
|
||||
if (filename_len > ext.size()) {
|
||||
if (strcmp(entry->d_name + filename_len - ext.size(), ext.c_str()) ==
|
||||
0) {
|
||||
std::string file_path_to_remove =
|
||||
dir_path + kDirectoryDelimiter + entry->d_name;
|
||||
if (!Remove(file_path_to_remove)) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t FileSystem::FileSize(const std::string& path) {
|
||||
struct stat buf;
|
||||
if (stat(path.c_str(), &buf) == 0)
|
||||
return buf.st_size;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Accept a directory, return all the files in that directory.
|
||||
// Returns false if the directory does not exist.
|
||||
bool FileSystem::List(const std::string& dirpath,
|
||||
std::vector<std::string>* filenames) {
|
||||
if (filenames == nullptr) {
|
||||
LOGE("FileSystem::List: destination not provided");
|
||||
return false;
|
||||
}
|
||||
if (!Exists(dirpath)) {
|
||||
LOGW("FileSystem::List: path %s does not exist: %d, %s",
|
||||
dirpath.c_str(), errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
DIR* dir = opendir(dirpath.c_str());
|
||||
if (dir == nullptr) {
|
||||
LOGW("FileSystem::List: directory open failed %s: %d, %s", dirpath.c_str(),
|
||||
errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
filenames->clear();
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (!IsCurrentOrParentDirectory(entry->d_name)) {
|
||||
filenames->push_back(entry->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSystem::set_origin(const std::string& origin) { origin_ = origin; }
|
||||
|
||||
void FileSystem::set_identifier(const std::string& identifier) {
|
||||
identifier_ = identifier;
|
||||
}
|
||||
|
||||
} // namespace wvutil
|
||||
62
linux/src/log.cpp
Normal file
62
linux/src/log.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Log - implemented using stdout.
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace {
|
||||
|
||||
FILE* const kOutputFile = stdout;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvutil {
|
||||
|
||||
LogPriority g_cutoff = CDM_LOG_WARN;
|
||||
|
||||
void InitLogging() {
|
||||
// Note: The default log level is CDM_LOG_WARN, above. If you set the
|
||||
// environment variable VERBOSE_LOG, you will get verbose logging. This is
|
||||
// set by jenkins (http://go/wvbuild), so that we have more details when the
|
||||
// build breaks.
|
||||
const char* verbose_env = getenv("VERBOSE_LOG");
|
||||
if (verbose_env && !strncmp(verbose_env, "yes", 3) ) {
|
||||
g_cutoff = CDM_LOG_VERBOSE;
|
||||
}
|
||||
}
|
||||
|
||||
void Log(const char* file, const char* function, int line, LogPriority level,
|
||||
const char* fmt, ...) {
|
||||
const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" };
|
||||
if (level >=
|
||||
static_cast<LogPriority>(sizeof(severities) / sizeof(*severities))) {
|
||||
fprintf(kOutputFile, "[FATAL:%s(%d):%s] Invalid log priority level: %d\n",
|
||||
file, line, function, level);
|
||||
return;
|
||||
}
|
||||
if (level > g_cutoff) return;
|
||||
|
||||
// Strip off the the leading "../" that clutters the logs.
|
||||
const char * up_dir = "../";
|
||||
const size_t up_dir_size = strlen(up_dir);
|
||||
while (strncmp(up_dir, file, up_dir_size) == 0) file += up_dir_size;
|
||||
|
||||
|
||||
fprintf(kOutputFile, "[%s:%s(%d):%s] ", severities[level], file, line,
|
||||
function);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(kOutputFile, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
putc('\n', kOutputFile);
|
||||
fflush(kOutputFile);
|
||||
}
|
||||
|
||||
} // namespace wvutil
|
||||
@@ -1038,7 +1038,7 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
|
||||
*
|
||||
* @verification
|
||||
* If the RSA key's allowed_schemes is not kSign_RSASSA_PSS, then no keys are
|
||||
* derived and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. An RSA
|
||||
* derived and the error OEMCrypto_ERROR_INVALID_KEY is returned. An RSA
|
||||
* key cannot be used for both deriving session keys and also for PKCS1
|
||||
* signatures.
|
||||
*
|
||||
@@ -2116,7 +2116,7 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session,
|
||||
* 256 bits it will be used for OEMCrypto_Generic_Sign() or
|
||||
* OEMCrypto_Generic_Verify() as specified in the key control block. If the key
|
||||
* will be used for OEMCrypto_Generic_Encrypt() or OEMCrypto_Generic_Decrypt()
|
||||
* then the cipher mode will always be OEMCrypto_CipherMode_CBC. Continue to
|
||||
* then the cipher mode will always be OEMCrypto_CipherMode_CBCS. Continue to
|
||||
* use this key for this session until OEMCrypto_SelectKey() is called again,
|
||||
* or until OEMCrypto_CloseSession() is called.
|
||||
*
|
||||
@@ -2292,15 +2292,15 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
|
||||
* 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The
|
||||
* schemes 'cens' and 'cbc1' are not supported.
|
||||
*
|
||||
* The decryption mode, either OEMCrypto_CipherMode_CTR or
|
||||
* OEMCrypto_CipherMode_CBC, was already specified in the call to
|
||||
* The decryption mode, either OEMCrypto_CipherMode_CENC or
|
||||
* OEMCrypto_CipherMode_CBCS, was already specified in the call to
|
||||
* OEMCrypto_SelectKey(). The encryption pattern is specified by the fields in
|
||||
* the parameter pattern. A description of partial encryption patterns for
|
||||
* 'cbcs' can be found in the ISO-CENC standard, section 10.4.
|
||||
*
|
||||
* 'cenc' SCHEME:
|
||||
*
|
||||
* The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption
|
||||
* The 'cenc' scheme is OEMCrypto_CipherMode_CENC without an encryption
|
||||
* pattern. All the bytes in the encrypted portion of each subsample are
|
||||
* encrypted. In the pattern parameter, both the encrypt and skip fields will
|
||||
* be zero.
|
||||
@@ -2323,7 +2323,7 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
|
||||
*
|
||||
* 'cbcs' SCHEME:
|
||||
*
|
||||
* The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern.
|
||||
* The 'cbcs' scheme is OEMCrypto_CipherMode_CBCS with an encryption pattern.
|
||||
* Only some of the bytes in the encrypted portion of each subsample are
|
||||
* encrypted. In the pattern parameter, the encrypt and skip fields will
|
||||
* usually be non-zero. This mode allows devices to decrypt FMP4 HLS content,
|
||||
@@ -3077,7 +3077,7 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void);
|
||||
* @retval OEMCrypto_ERROR_BAD_MAGIC
|
||||
* @retval OEMCrypto_ERROR_BAD_CRC
|
||||
* @retval OEMCrypto_ERROR_KEYBOX_INVALID
|
||||
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY
|
||||
* @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED
|
||||
* @retval OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING
|
||||
*
|
||||
@@ -3964,7 +3964,7 @@ OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport(void);
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY
|
||||
* @retval OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
* @retval OEMCrypto_ERROR_INVALID_NONCE
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER
|
||||
@@ -4029,7 +4029,7 @@ OEMCryptoResult OEMCrypto_LoadProvisioning(
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY
|
||||
* @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_ERROR_SESSION_LOST_STATE
|
||||
@@ -4108,7 +4108,7 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void);
|
||||
* @verification
|
||||
* Both the padding_scheme and the RSA key's allowed_schemes must be 0x2. If
|
||||
* not, then the signature is not computed and the error
|
||||
* OEMCrypto_ERROR_INVALID_RSA_KEY is returned.
|
||||
* OEMCrypto_ERROR_INVALID_KEY is returned.
|
||||
*
|
||||
* @param[in] session: crypto session identifier.
|
||||
* @param[in] message: pointer to memory containing message to be signed.
|
||||
@@ -4125,7 +4125,7 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void);
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small.
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY
|
||||
* @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if algorithm > 0, and the device
|
||||
@@ -4533,6 +4533,7 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry(
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT an entry was not created or loaded,
|
||||
* or the pst does not match.
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE
|
||||
@@ -4684,6 +4685,7 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE
|
||||
* @retval OEMCrypto_ERROR_ENTRY_IN_USE
|
||||
@@ -4763,13 +4765,13 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count,
|
||||
*
|
||||
* @param[out] bcc: pointer to the buffer that receives the serialized boot
|
||||
* certificate chain in CBOR format.
|
||||
* @param[in,out] bcc_size - on input, size of the caller's bcc buffer. On
|
||||
* @param[in,out] bcc_length - on input, size of the caller's bcc buffer. On
|
||||
* output, the number of bytes written into the buffer.
|
||||
* @param[out] additional_signature: pointer to the buffer that receives
|
||||
* additional device key signature (certificate chain). This field is only
|
||||
* used by the signing model where a vendor certificate is available on the
|
||||
* device.
|
||||
* @param[in,out] additional_signature_size - on input, size of the caller's
|
||||
* @param[in,out] additional_signature_length - on input, size of the caller's
|
||||
* additional_signature buffer. On output, the number of bytes written into
|
||||
* the buffer.
|
||||
*
|
||||
@@ -4788,8 +4790,8 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count,
|
||||
* This method is new in API version 17.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GetBootCertificateChain(
|
||||
uint8_t* bcc, size_t* bcc_size, uint8_t* additional_signature,
|
||||
size_t* additional_signature_size);
|
||||
uint8_t* bcc, size_t* bcc_length, uint8_t* additional_signature,
|
||||
size_t* additional_signature_length);
|
||||
|
||||
/**
|
||||
* Generates a key pair used in OEM and DRM certificate provisioning. The public
|
||||
@@ -4806,18 +4808,20 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain(
|
||||
* @param[out] public_key: pointer to the buffer that receives the public key
|
||||
* that is to be certified by the server. The key must be an ASN.1
|
||||
* DER-encoded SubjectPublicKeyInfo as specified in RFC 5280.
|
||||
* @param[in,out] public_key_size: on input, size of the caller's public_key
|
||||
* @param[in,out] public_key_length: on input, size of the caller's public_key
|
||||
* buffer. On output, the number of bytes written into the buffer.
|
||||
* @param[out] public_key_signature: pointer to the buffer that receives the
|
||||
* signature of the public key. If an OEM private key is unavailable, it is
|
||||
* signed by the device private key; otherwise is signed by the OEM private
|
||||
* key.
|
||||
* @param[in,out] public_key_signature_size: on input, size of the caller's
|
||||
* signature of the public key.
|
||||
* If an OEM private key is unavailable: it is signed by the device private
|
||||
* key. The signature must be in COSE_SIGN1 format as specified in RFC 8152.
|
||||
* If an OEM private key is available: it is signed by the OEM private key.
|
||||
* The signature must be raw signature bytes.
|
||||
* @param[in,out] public_key_signature_length: on input, size of the caller's
|
||||
* public_key_signature buffer. On output, the number of bytes written into
|
||||
* the buffer.
|
||||
* @param[out] wrapped_private_key: pointer to the buffer that receives the
|
||||
* encrypted private key. It is encrypted by the device encryption key.
|
||||
* @param[in,out] wrapped_private_key_size: on input, size of the caller's
|
||||
* @param[in,out] wrapped_private_key_length: on input, size of the caller's
|
||||
* wrapped_private_key buffer. On output, the number of bytes written into
|
||||
* the buffer.
|
||||
* @param[out] key_type: the type of the generated key pair (RSA or ECC).
|
||||
@@ -4840,9 +4844,9 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain(
|
||||
* This method is new in API version 17.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair(
|
||||
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_size,
|
||||
uint8_t* public_key_signature, size_t* public_key_signature_size,
|
||||
uint8_t* wrapped_private_key, size_t* wrapped_private_key_size,
|
||||
OEMCrypto_SESSION session, uint8_t* public_key, size_t* public_key_length,
|
||||
uint8_t* public_key_signature, size_t* public_key_signature_length,
|
||||
uint8_t* wrapped_private_key, size_t* wrapped_private_key_length,
|
||||
OEMCrypto_PrivateKeyType* key_type);
|
||||
|
||||
/// @}
|
||||
@@ -5071,7 +5075,7 @@ OEMCryptoResult OEMCrypto_FreeSecureBuffer(
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* @retval OEMCrypto_ERROR_NO_DEVICE_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_SESSION
|
||||
* @retval OEMCrypto_ERROR_INVALID_RSA_KEY
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY
|
||||
* @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_ERROR_SESSION_LOST_STATE
|
||||
|
||||
@@ -27,8 +27,8 @@ struct CoreMessageFeatures {
|
||||
// behavior is for the server to restrict messages to at most this version
|
||||
// number. The default is 16.5, the last version used by Chrome. This will
|
||||
// change to 17.0 when v17 has been released.
|
||||
uint32_t maximum_major_version = 16;
|
||||
uint32_t maximum_minor_version = 5;
|
||||
uint32_t maximum_major_version = 17;
|
||||
uint32_t maximum_minor_version = 0;
|
||||
|
||||
bool operator==(const CoreMessageFeatures &other) const;
|
||||
bool operator!=(const CoreMessageFeatures &other) const {
|
||||
|
||||
@@ -595,6 +595,20 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length, ODK_ParsedProvisioning* parsed_response);
|
||||
|
||||
/**
|
||||
* The function ODK_ParseProvisioning will parse the message and verify the
|
||||
* API version is at most the version passed in.
|
||||
*
|
||||
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||
* @param[in] major_versioh: current API major version.
|
||||
* @param[in] minor_version: current API minor version.
|
||||
*
|
||||
* @version
|
||||
* This method is new in version 17 of the API.
|
||||
*/
|
||||
bool CheckApiVersionAtMost(const ODK_NonceValues* nonce_values,
|
||||
uint16_t major_version, uint16_t minor_version);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -19,7 +19,7 @@ extern "C" {
|
||||
#define ODK_MINOR_VERSION 0
|
||||
|
||||
/* ODK Version string. Date changed automatically on each release. */
|
||||
#define ODK_RELEASE_DATE "ODK v17.0 2021-11-15"
|
||||
#define ODK_RELEASE_DATE "ODK v17.0 2022-03-25"
|
||||
|
||||
/* The lowest version number for an ODK message. */
|
||||
#define ODK_FIRST_VERSION 16
|
||||
|
||||
@@ -101,8 +101,11 @@ bool CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
|
||||
}
|
||||
case video_widevine::License_KeyContainer::CONTENT:
|
||||
case video_widevine::License_KeyContainer::OPERATOR_SESSION:
|
||||
case video_widevine::License_KeyContainer::OEM_CONTENT:
|
||||
case video_widevine::License_KeyContainer::OEM_ENTITLEMENT:
|
||||
case video_widevine::License_KeyContainer::ENTITLEMENT: {
|
||||
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT) {
|
||||
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT ||
|
||||
k.type() == video_widevine::License_KeyContainer::OEM_ENTITLEMENT) {
|
||||
any_entitlement = true;
|
||||
} else {
|
||||
any_content = true;
|
||||
|
||||
@@ -92,7 +92,7 @@ static OEMCryptoResult ODK_PrepareRequest(
|
||||
}
|
||||
|
||||
/* Parse the core message and verify that it has the right type. The nonce
|
||||
* values are updated to hold the resposne's API version.
|
||||
* values are updated to hold the response's API version.
|
||||
*/
|
||||
static OEMCryptoResult ODK_ParseCoreHeader(const uint8_t* message,
|
||||
size_t message_length,
|
||||
@@ -163,9 +163,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedLicenseRequest license_request = {
|
||||
{0, 0, {}},
|
||||
};
|
||||
ODK_PreparedLicenseRequest license_request = {0};
|
||||
return ODK_PrepareRequest(
|
||||
message, message_length, core_message_length, ODK_License_Request_Type,
|
||||
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
|
||||
@@ -193,7 +191,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
ODK_PreparedRenewalRequest renewal_request = {{0, 0, {}}, 0};
|
||||
ODK_PreparedRenewalRequest renewal_request = {0};
|
||||
/* First, we compute the time this request was made relative to the playback
|
||||
* clock. */
|
||||
if (clock_values->time_of_first_decrypt == 0) {
|
||||
@@ -226,11 +224,7 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {
|
||||
{0, 0, {}},
|
||||
0,
|
||||
{0},
|
||||
};
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {0};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
@@ -263,13 +257,13 @@ OEMCryptoResult ODK_ParseLicense(
|
||||
return err;
|
||||
}
|
||||
|
||||
ODK_LicenseResponse license_response = {{{0, 0, {}}}, NULL};
|
||||
ODK_LicenseResponse license_response = {0};
|
||||
license_response.parsed_license = parsed_license;
|
||||
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
if (nonce_values->api_major_version == 16) {
|
||||
ODK_LicenseResponseV16 license_response_v16 = {{{0, 0, {}}}, {}, {0}};
|
||||
ODK_LicenseResponseV16 license_response_v16 = {0};
|
||||
Unpack_ODK_LicenseResponseV16(&msg, &license_response_v16);
|
||||
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
@@ -381,10 +375,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
ODK_RenewalResponse renewal_response = {
|
||||
{{0, 0, {}}, 0},
|
||||
0,
|
||||
};
|
||||
ODK_RenewalResponse renewal_response = {0};
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
Unpack_ODK_RenewalResponse(&msg, &renewal_response);
|
||||
@@ -433,7 +424,7 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
ODK_ProvisioningResponse provisioning_response = {{{0, 0, {}}, 0, {0}}, NULL};
|
||||
ODK_ProvisioningResponse provisioning_response = {0};
|
||||
provisioning_response.parsed_provisioning = parsed_response;
|
||||
|
||||
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||
@@ -469,3 +460,10 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool CheckApiVersionAtMost(const ODK_NonceValues* nonce_values,
|
||||
uint16_t major_version, uint16_t minor_version) {
|
||||
return nonce_values->api_major_version < major_version ||
|
||||
(nonce_values->api_major_version == major_version &&
|
||||
nonce_values->api_minor_version <= minor_version);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'toolsets' : [ 'target' ],
|
||||
'target_name': 'odk',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library' : 1,
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
'../../include',
|
||||
@@ -18,10 +20,6 @@
|
||||
# TODO(b/172518513): Remove this
|
||||
'-Wno-error=cast-qual',
|
||||
],
|
||||
'cflags_c': [
|
||||
# TODO(b/159354894): Remove this
|
||||
'-Wno-error=bad-function-cast',
|
||||
],
|
||||
'defines': [
|
||||
# Needed for <endian.h> to work.
|
||||
'_DEFAULT_SOURCE',
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
# TODO(b/172518513): Remove this
|
||||
'-Wno-error=cast-qual',
|
||||
],
|
||||
'cflags_c': [
|
||||
# TODO(b/159354894): Remove this
|
||||
'-Wno-error=bad-function-cast',
|
||||
],
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
'-g3',
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// License Agreement.
|
||||
#include "fuzzing/odk_fuzz_helper.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
'../util/libssl_dependency.gypi',
|
||||
'test/oemcrypto_unittests.gypi',
|
||||
'ref/oec_ref.gypi',
|
||||
'ref/oec_ref_unittests.gypi',
|
||||
'util/oec_ref_util.gypi',
|
||||
'util/oec_ref_util_unittests.gypi',
|
||||
],
|
||||
'libraries': [
|
||||
'-lpthread',
|
||||
|
||||
46
oemcrypto/opk/FILES.md
Normal file
46
oemcrypto/opk/FILES.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# REE-Side Serialization & IPC Files
|
||||
|
||||
All files are in the `serialization` directory. The REE-side code is made up of
|
||||
two libraries:
|
||||
|
||||
## The API Library
|
||||
|
||||
- `api_support.c`
|
||||
- `special_case_apis.c`
|
||||
- `generated_src/oemcrypto_api.c`
|
||||
|
||||
## The Serialization Library
|
||||
|
||||
- `api_support.c`
|
||||
- `bump_allocator.c`
|
||||
- `marshaller_base.c`
|
||||
- `message.c`
|
||||
- `opk_serialization_base.c`
|
||||
- `shared_memory_allocator.c`
|
||||
- `special_cases.c`
|
||||
- `generated_src/deserializer.c`
|
||||
- `generated_src/dispatcher.c`
|
||||
- `generated_src/serializer.c`
|
||||
|
||||
# TEE-Side Serialization & IPC Files
|
||||
|
||||
The TEE-side code consists of the same Serialization Library code as the
|
||||
REE-side code.
|
||||
|
||||
# Trusted App Files
|
||||
|
||||
All files are in the `oemcrypto_ta` directory. All the files in that directory
|
||||
should be part of the TA, but here is a list:
|
||||
|
||||
- `oemcrypto.c`
|
||||
- `oemcrypto_asymmetric_key_table.c`
|
||||
- `oemcrypto_endianness.c`
|
||||
- `oemcrypto_key.c`
|
||||
- `oemcrypto_key_table.c`
|
||||
- `oemcrypto_nonce_table.c`
|
||||
- `oemcrypto_overflow.c`
|
||||
- `oemcrypto_session.c`
|
||||
- `oemcrypto_session_key_table.c`
|
||||
- `oemcrypto_session_table.c`
|
||||
- `oemcrypto_usage_table.c`
|
||||
- `oemcrypto_serialized_usage_table.c`
|
||||
34
oemcrypto/opk/README.md
Normal file
34
oemcrypto/opk/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# OEMCrypto Porting Kit For Trusted Execution Environments
|
||||
|
||||
OPK is the Widevine hardened reference implementation of OEMCrypto suitable
|
||||
to run in a TEE. It is written in C with a thin porting interface to make it
|
||||
easier to port to various trusted environments.
|
||||
|
||||
The porting interface functions are prefixed with `WTPI_`, which stands for
|
||||
"Widevine TA Porting Interface".
|
||||
|
||||
## Current Status
|
||||
|
||||
This very early preview release contains an early version of the OPK source
|
||||
code. It contains only the following:
|
||||
|
||||
1) Code for an IPC layer that implements the OEMCrypto API functions, translates
|
||||
the calls into serialized objects, deserializes the objects inside the TEE,
|
||||
and invokes the appropriate TA function
|
||||
2) Code for a Trusted Application that implements the logic of OEMCrypto
|
||||
|
||||
No build system is included. No implementation of the porting layers for working
|
||||
with different TEE OSes and chip hardware is included.
|
||||
|
||||
In addition, the code herein has the following known limitations:
|
||||
|
||||
1) The usage table code does not yet encrypt the usage table information.
|
||||
2) The code is only sporadically and opportunistically hardened.
|
||||
3) Some minor functionality is still missing, though it should all be marked
|
||||
with TODO comments.
|
||||
|
||||
If you have received this code, Widevine is looking for your feedback! Please
|
||||
let us know where it can be improved. Don't hesitate to call out things you
|
||||
think we already know, particularly as regards hardening. We want to know
|
||||
whether the places we see room for improvement are the same as the ones where
|
||||
you do.
|
||||
118
oemcrypto/opk/build/Makefile.opk
Normal file
118
oemcrypto/opk/build/Makefile.opk
Normal file
@@ -0,0 +1,118 @@
|
||||
# This is the top level makefile for a port of the OPK. It
|
||||
# invokes the gyp-generated Makefile.rules, then includes the
|
||||
# generated target.mk for each target library.
|
||||
|
||||
# The directory structure under ./build mirrors the directory
|
||||
# structure rooted at the top level of the repo. This isolates all of
|
||||
# the generated makefiles from the source tree so they are not
|
||||
# intermingled with the source code, and can be managed/cleaned
|
||||
# independently. Since these are generated files there is usually no
|
||||
# need to modify these makefiles or the directory structure.
|
||||
#
|
||||
# The top level files are:
|
||||
# Makefile.opk : This file, top level makefile for the OPK
|
||||
# Makefile.rules : Generated Make rules for building the OPK
|
||||
# host.gyp : gyp file to make liboemcrypto and unit tests
|
||||
# ta.gyp : gyp file with dependencies to make the TEE libraries
|
||||
|
||||
# The generated *.mk files contain the rules to build each library:
|
||||
# ├── oemcrypto
|
||||
# │ ├── odk
|
||||
# │ │ └── src
|
||||
# │ │ └── odk.target.mk
|
||||
# │ └── opk
|
||||
# │ ├── build
|
||||
# │ │ ├── liboemcrypto.target.mk
|
||||
# │ │ └── ta.target.mk
|
||||
# │ ├── oemcrypto_ta
|
||||
# │ │ ├── oemcrypto_ta.target.mk
|
||||
# │ │ ├── wtpi_reference
|
||||
# │ │ │ ├── oemcrypto_ta_reference_clock.target.mk
|
||||
# │ │ │ ├── oemcrypto_ta_reference_crypto.target.mk
|
||||
# │ │ │ ├── oemcrypto_ta_reference_renewal.target.mk
|
||||
# │ │ │ └── oemcrypto_ta_reference_root_of_trust.target.mk
|
||||
# │ │ └── wtpi_test
|
||||
# │ │ ├── ree
|
||||
# │ │ │ ├── opk_ree_api.target.mk
|
||||
# │ │ │ └── opk_ree.target.mk
|
||||
# │ │ ├── tee
|
||||
# │ │ │ └── opk_tee_wtpi_test.target.mk
|
||||
# │ │ ├── wtpi_test_lib.target.mk
|
||||
# │ │ └── wtpi_test.target.mk
|
||||
# │ └── serialization
|
||||
# │ ├── ree
|
||||
# │ │ └── opk_ree.target.mk
|
||||
# │ └── tee
|
||||
# │ └── opk_tee.target.mk
|
||||
# └── third_party
|
||||
# ├── boringssl
|
||||
# │ └── crypto.target.mk
|
||||
# └── gtest.target.mk
|
||||
|
||||
# You can add additional compiler options by setting these defines or
|
||||
# passing them on the make command line:
|
||||
#
|
||||
# CFLAGS := <options for C only>
|
||||
# CPPFLAGS := <options for C and C++>
|
||||
# CXXFLAGS := <options for C++ only>
|
||||
|
||||
# By default, warnings are not treated as errors, and no additional
|
||||
# compiler warnings are enabled, to avoid adding warnings that may not
|
||||
# be supported by all compilers, or introducing build failures that
|
||||
# may in fact be harmless. You may choose to enable warnings by
|
||||
# uncommenting the define and adding other warnings as desired:
|
||||
#
|
||||
# CPPFLAGS := -Werror -Wall -Wextra -Wunused
|
||||
|
||||
# NO_LOAD disables the includes from Makefile.rules, they are
|
||||
# included explicitly below
|
||||
NO_LOAD := oemcrypto third_party
|
||||
|
||||
include Makefile.rules
|
||||
|
||||
# Include rules to build unit tests
|
||||
include third_party/boringssl/ssl.target.mk
|
||||
include third_party/boringssl/crypto.target.mk
|
||||
include third_party/gmock.target.mk
|
||||
include third_party/gtest.target.mk
|
||||
include $(OEMCRYPTO_UNITTEST_DIR)/oemcrypto_unittests.target.mk
|
||||
|
||||
# Include rules to build the OPK libraries
|
||||
include oemcrypto/odk/src/odk.target.mk
|
||||
include oemcrypto/opk/build/ta.target.mk
|
||||
include oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.target.mk
|
||||
include oemcrypto/opk/serialization/ree/opk_ree.target.mk
|
||||
include oemcrypto/opk/serialization/tee/opk_tee.target.mk
|
||||
include $(WTPI_IMPL_DIR)/wtpi_impl.target.mk
|
||||
|
||||
# Include rules to build the WTPI test libraries
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_test/ree/opk_ree_api.target.mk
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_test/wtpi_test_lib.target.mk
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_test/tee/opk_tee_wtpi_test.target.mk
|
||||
include $(WTPI_UNITTEST_DIR)/wtpi_unittests.target.mk
|
||||
|
||||
# Add rules for the transport layer implementations for OEMCrypto TA and WTPI unit tests
|
||||
include $(REE_TOS_DIR)/ree_tos.target.mk
|
||||
include $(REE_TOS_WTPI_DIR)/ree_tos_wtpi.target.mk
|
||||
|
||||
ifeq ($(USE_TA_REFERENCE_CRYPTO),yes)
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_reference/oemcrypto_ta_reference_crypto.target.mk
|
||||
ta_libs: oemcrypto_ta_reference_crypto
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TA_REFERENCE_CLOCK),yes)
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_reference/oemcrypto_ta_reference_clock.target.mk
|
||||
ta_libs: oemcrypto_ta_reference_clock
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TA_REFERENCE_RENEWAL),yes)
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_reference/oemcrypto_ta_reference_renewal.target.mk
|
||||
ta_libs: oemcrypto_ta_reference_renewal
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TA_REFERENCE_ROOT_OF_TRUST),yes)
|
||||
include oemcrypto/opk/oemcrypto_ta/wtpi_reference/oemcrypto_ta_reference_root_of_trust.target.mk
|
||||
ta_libs: oemcrypto_ta_reference_root_of_trust
|
||||
endif
|
||||
|
||||
include oemcrypto/opk/build/liboemcrypto.target.mk
|
||||
177
oemcrypto/opk/build/Makefile.optee
Normal file
177
oemcrypto/opk/build/Makefile.optee
Normal file
@@ -0,0 +1,177 @@
|
||||
# Makefile for OP-TEE liboemcrypto.so and the OP-TEE widevine trusted app
|
||||
|
||||
# $OPTEE_DIR must be defined as the root of the OP-TEE SDK
|
||||
ifndef OPTEE_DIR
|
||||
$(error OPTEE_DIR is undefined)
|
||||
endif
|
||||
|
||||
# $CDM_DIR must be defined as the path to the top level of the OPK release
|
||||
ifndef CDM_DIR
|
||||
$(error CDM_DIR is undefined)
|
||||
endif
|
||||
|
||||
.EXPORT_ALL_VARIABLES:
|
||||
|
||||
# Set platform-specific toolchain flags for OP-TEE
|
||||
# Run make with the OPTEE_PLATFORM variable set to one of the following values:
|
||||
# qemu (QEMU v7)
|
||||
# stm32mp1 (STM32MP157 DK1 eval kit)
|
||||
# nxpimx8m (NXP iMX8M eval kit)
|
||||
|
||||
# Default is QEMU
|
||||
OPTEE_PLATFORM ?= qemu
|
||||
CFG_TEE_TA_MALLOC_DEBUG:=y
|
||||
|
||||
# Default toolchain dir from the optee repositories
|
||||
OPTEE_TOOLCHAIN_DIR ?= $(OPTEE_DIR)/toolchains
|
||||
|
||||
ifeq ($(OPTEE_PLATFORM),qemu)
|
||||
PLATFORM := vexpress-qemu_virt
|
||||
TEEC_EXPORT ?= $(OPTEE_DIR)/out-br/build/optee_client_ext-1.0/libteec
|
||||
OPTEE_TOOLCHAIN := $(OPTEE_TOOLCHAIN_DIR)/aarch32
|
||||
TA_DEV_KIT_DIR := $(OPTEE_DIR)/optee_os/out/arm/export-ta_arm32
|
||||
CROSS_COMPILE := arm-linux-gnueabihf-
|
||||
CPPFLAGS := \
|
||||
-isystem $(OPTEE_TOOLCHAIN)/lib/gcc/arm-none-linux-gnueabihf/10.2.1/include \
|
||||
|
||||
else ifeq ($(OPTEE_PLATFORM),stm32mp1)
|
||||
TEEC_EXPORT ?= $(OPTEE_DIR)/out-br/build/optee_client_ext-1.0/libteec
|
||||
OPTEE_TOOLCHAIN := $(OPTEE_TOOLCHAIN_DIR)/aarch32
|
||||
TA_DEV_KIT_DIR := $(OPTEE_DIR)/optee_os/out/arm/export-ta_arm32
|
||||
CROSS_COMPILE := arm-linux-gnueabihf-
|
||||
CPPFLAGS := \
|
||||
-isystem $(OPTEE_TOOLCHAIN)/lib/gcc/arm-none-linux-gnueabihf/10.2.1/include \
|
||||
|
||||
else ifeq ($(OPTEE_PLATFORM),nxpimx8m)
|
||||
PLATFORM := imx-mx8mqevk
|
||||
TEEC_EXPORT ?= $(OPTEE_DIR)/out-br/build/optee_client_ext-1.0/libteec
|
||||
OPTEE_TOOLCHAIN := $(OPTEE_TOOLCHAIN_DIR)/aarch64
|
||||
TA_DEV_KIT_DIR := $(OPTEE_DIR)/optee_os/out/arm/export-ta_arm64
|
||||
CROSS_COMPILE := aarch64-linux-gnu-
|
||||
CPPFLAGS := \
|
||||
-isystem $(OPTEE_TOOLCHAIN)/lib/gcc/aarch64-none-linux-gnu/10.2.1/include \
|
||||
|
||||
else
|
||||
$(error Unknown OPTEE_PLATFORM $(OPTEE_PLATFORM))
|
||||
endif
|
||||
|
||||
# Set paths and flags for the OP-TEE toolchain
|
||||
PATH := $(PATH):$(OPTEE_TOOLCHAIN)/bin
|
||||
CC_target := $(OPTEE_TOOLCHAIN)/bin/$(CROSS_COMPILE)gcc
|
||||
CXX_target := $(OPTEE_TOOLCHAIN)/bin/$(CROSS_COMPILE)g++
|
||||
AR_target := $(OPTEE_TOOLCHAIN)/bin/$(CROSS_COMPILE)ar
|
||||
CPPFLAGS += \
|
||||
-I $(OPTEE_DIR)/optee_client/public \
|
||||
-Wno-psabi \
|
||||
|
||||
# OEMCrypto TA optional components
|
||||
USE_TA_REFERENCE_CRYPTO := no
|
||||
USE_TA_REFERENCE_CLOCK := no
|
||||
USE_TA_REFERENCE_RENEWAL := no
|
||||
USE_TA_REFERENCE_ROOT_OF_TRUST := no
|
||||
|
||||
# Where the build output goes: $CDM/out/opk_optee
|
||||
builddir_name := $(shell 'pwd')/../../../out/opk_optee
|
||||
$(info XXXXX builddir_name $(builddir_name))
|
||||
|
||||
# List libraries from the Trusted OS SDK to link into
|
||||
# liboemcrypto.so
|
||||
TRUSTED_OS_SDK_LIBS := $(OPTEE_DIR)/out-br/build/optee_client_ext-1.0/libteec/libteec.so
|
||||
|
||||
PORT_BASE_DIR:=../ports/optee
|
||||
|
||||
# The makefile for liboemcrypto_ta.a requires this environment variable in
|
||||
# order to locate headers with configuration macros, which can be
|
||||
# implementation specific
|
||||
WTPI_CONFIG_MACRO_DIR := $(PORT_BASE_DIR)/ta/common/wtpi_impl
|
||||
|
||||
# NO_LOAD disables the includes from Makefile.rules, they are
|
||||
# included explicitly from Makefile.opk instead
|
||||
NO_LOAD := oemcrypto third_party
|
||||
include Makefile.rules
|
||||
|
||||
# OP-TEE specific linker flags for liboemcrypto.so
|
||||
# Manually add entire ree_tos library for liboemcrypto.so. This ree_tos library
|
||||
# implementation uses a file with static functions that are not reachable by
|
||||
# main, but we still want to include. So we force inclusion with --whole-archive
|
||||
LIBOEMCRYPTO_LDFLAGS := \
|
||||
-Wl,--whole-archive \
|
||||
-L$(builddir)/ \
|
||||
-lree_tos \
|
||||
-Wl,-no-whole-archive \
|
||||
|
||||
# OP-TEE specific linker flags for the WTPI unit tests
|
||||
WTPI_UNITTEST_LDFLAGS := \
|
||||
-Wl,--whole-archive -lteec -L$(TEEC_EXPORT) \
|
||||
-L$(builddir)/ \
|
||||
-L$(builddir)/obj.target/third_party \
|
||||
-L$(builddir)/obj.target/oemcrypto/opk/oemcrypto_ta/wtpi_test/ree \
|
||||
-lcrypto \
|
||||
-lopk_ree_api \
|
||||
-lgtest \
|
||||
-lwtpi_test_lib \
|
||||
-lree_tos_wtpi \
|
||||
-Wl,-no-whole-archive \
|
||||
-static-libstdc++ \
|
||||
|
||||
# OP-TEE specific linker flags for the oemcrypto unit tests
|
||||
OEMCRYPTO_UNITTEST_LDFLAGS := \
|
||||
-static-libstdc++ \
|
||||
|
||||
# Makefile.opk expects this variable, which points to the directory containing
|
||||
# wtpi_impl.target.mk. This builds a static lib containing all the required
|
||||
# WTPI functions for the OEMCrypto TA or WTPI unit tests to link
|
||||
WTPI_IMPL_DIR := $(PORT_BASE_DIR)/ta/common/wtpi_impl
|
||||
|
||||
# Makefile.opk expects this variable, which points to
|
||||
# oemcrypto_unittests.target.mk. That makefile builds the oemcrypto unittest
|
||||
# host executable.
|
||||
OEMCRYPTO_UNITTEST_DIR := $(PORT_BASE_DIR)/host/oemcrypto_unittests
|
||||
|
||||
# Makefile.opk expects this variable, which points to wtpi_unittests.target.mk.
|
||||
# That makefile builds the wtpi unittest host executable.
|
||||
WTPI_UNITTEST_DIR := $(PORT_BASE_DIR)/host/wtpi_unittests
|
||||
|
||||
# Makefile.opk expects these two variables. They point to ree_tos.target.mk and
|
||||
# ree_tos_wtpi.target.mk respectively, which build the transport layer
|
||||
# implementations ree_tos.a and ree_tos_wtpi.a
|
||||
REE_TOS_DIR := $(PORT_BASE_DIR)/host/common/tos
|
||||
REE_TOS_WTPI_DIR := $(PORT_BASE_DIR)/host/common/tos
|
||||
|
||||
# Add rules for a simple "hello world" host executable, which can be used to check
|
||||
# that REE-TEE interactions are working correctly. This is not expected by Makefile.opk.
|
||||
include $(PORT_BASE_DIR)/host/oemcrypto_helloworld/oemcrypto_helloworld.target.mk
|
||||
|
||||
# Include common OPK make rules
|
||||
include Makefile.opk
|
||||
|
||||
# Force ree_tos recipe to execute before liboemcrypto.so artifact is built.
|
||||
# Using the absolute path in the output directory instead of the top level,
|
||||
# since the absolute path is what gets built, while the top level is where
|
||||
# liboemcrypto.so gets copied to
|
||||
# TODO: clean up dependency organization between liboemcrypto.so and port-specific ree_tos
|
||||
$(obj).target/oemcrypto/opk/build/liboemcrypto.so: ree_tos
|
||||
|
||||
# Add in dependency-tracking rules. $(all_deps) is the list of every single
|
||||
# target in our tree. Only consider the ones with .d (dependency) info:
|
||||
d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
|
||||
ifneq ($(d_files),)
|
||||
include $(d_files)
|
||||
endif
|
||||
|
||||
# Build the OEMCrypto trusted app using the OP-TEE target build system
|
||||
# The prerequisites are linked by the oemcrypto_ta makefile, so necessarily must be built first
|
||||
.PHONY: trusted_app
|
||||
trusted_app: odk opk_tee oemcrypto_ta wtpi_impl
|
||||
CFLAGS="$(CFLAGS.target)" $(MAKE) -C ../ports/optee/ta/oemcrypto_ta --no-builtin-variables
|
||||
|
||||
# Build the WTPI unit test trusted app
|
||||
.PHONY: wtpi_test_ta
|
||||
wtpi_test_ta: odk opk_tee_wtpi_test oemcrypto_ta wtpi_impl
|
||||
CFLAGS="$(CFLAGS.target)" $(MAKE) -C ../ports/optee/ta/wtpi_test_ta --no-builtin-variables
|
||||
|
||||
# Add OEMCrypto TA and WTPI unit test TA recipes to all. All the other targets
|
||||
# included from Makefile.opk are already part of the "all" recipe; these
|
||||
# two must be added manually
|
||||
.PHONY: all
|
||||
all: trusted_app wtpi_test_ta
|
||||
90
oemcrypto/opk/build/host.gyp
Normal file
90
oemcrypto/opk/build/host.gyp
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
'includes' : [
|
||||
'../serialization/settings.gypi',
|
||||
],
|
||||
'variables': {
|
||||
'platform_specific_dir': '<(DEPTH)/linux/src',
|
||||
'util_dir': '<(DEPTH)/util',
|
||||
'privacy_crypto_impl': 'boringssl',
|
||||
'boringssl_libcrypto_path': '<(third_party_dir)/boringssl/boringssl.gyp:crypto',
|
||||
'boringssl_libssl_path': '<(third_party_dir)/boringssl/boringssl.gyp:ssl',
|
||||
'gtest_dependency': '<(third_party_dir)/googletest.gyp:gtest',
|
||||
'gmock_dependency': '<(third_party_dir)/googletest.gyp:gmock',
|
||||
'support_ota_keybox_functions': 'false',
|
||||
'wtpi_test_serialization': '<(oemcrypto_ta_dir)/wtpi_test',
|
||||
},
|
||||
'targets' : [
|
||||
{
|
||||
# liboemcrypto.so shared library
|
||||
'toolsets' : [ 'target' ],
|
||||
'target_name': 'liboemcrypto',
|
||||
'type': 'shared_library',
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'$(TRUSTED_OS_SDK_LIBS)',
|
||||
'<(PRODUCT_DIR)/libree_tos.a',
|
||||
],
|
||||
},
|
||||
'dependencies': [
|
||||
'<(ree_dir)/ree.gyp:opk_ree',
|
||||
],
|
||||
'ldflags': [
|
||||
'$(LIBOEMCRYPTO_LDFLAGS)',
|
||||
],
|
||||
},
|
||||
{
|
||||
# OEMCrypto unit tests
|
||||
'toolsets' : [ 'target' ],
|
||||
'target_name': 'oemcrypto_unittests',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'<(oemcrypto_dir)/test/oemcrypto_test_main.cpp',
|
||||
'<(odk_dir)/src/core_message_deserialize.cpp',
|
||||
'<(odk_dir)/src/core_message_serialize.cpp',
|
||||
'<(platform_specific_dir)/file_store.cpp',
|
||||
'<(platform_specific_dir)/log.cpp',
|
||||
'<(util_dir)/src/cdm_random.cpp',
|
||||
'<(util_dir)/src/platform.cpp',
|
||||
'<(util_dir)/src/rw_lock.cpp',
|
||||
'<(util_dir)/src/string_conversions.cpp',
|
||||
'<(util_dir)/test/test_sleep.cpp',
|
||||
'<(util_dir)/test/test_clock.cpp',
|
||||
'<(odk_dir)/src/core_message_features.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'<(util_dir)/include',
|
||||
'<(util_dir)/test',
|
||||
],
|
||||
'dependencies': [
|
||||
'liboemcrypto',
|
||||
'<(gtest_dependency)',
|
||||
'<(gmock_dependency)',
|
||||
],
|
||||
'includes': [
|
||||
'../../test/oemcrypto_unittests.gypi',
|
||||
'../../util/oec_ref_util.gypi',
|
||||
],
|
||||
'ldflags': [
|
||||
'$(OEMCRYPTO_UNITTEST_LDFLAGS)',
|
||||
],
|
||||
},
|
||||
{
|
||||
# WTPI unit tests
|
||||
'toolsets': [ 'target' ],
|
||||
'target_name': 'wtpi_unittests',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'<(wtpi_test_serialization)/wtpi_test_main.cpp',
|
||||
],
|
||||
'dependencies': [
|
||||
'<(wtpi_test_serialization)/ree/ree_api.gyp:opk_ree_api',
|
||||
'<(wtpi_test_serialization)/wtpi_test.gyp:wtpi_test_lib',
|
||||
'<(gtest_dependency)',
|
||||
'<(gmock_dependency)',
|
||||
],
|
||||
'ldflags': [
|
||||
'$(WTPI_UNITTEST_LDFLAGS)',
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Port-specific makefiles for OP-TEE will be placed here after running
|
||||
jenkins/opk_makefiles with optee-specific gen_makefiles scripts executed. The
|
||||
generated port-specific makefiles include:
|
||||
* oemcrypto_helloworld.target.mk
|
||||
* ree_tos.target.mk
|
||||
* ree_tos_wtpi_target.mk
|
||||
* wtpi_impl.target.mk
|
||||
|
||||
as well as the unit test makefiles under `oemcrypto/opk/build/oemcrypto/opk/build/`:
|
||||
* oemcrypto_unittests.target.mk
|
||||
* wtpi_unittests.target.mk
|
||||
|
||||
Examples of how these are referenced can be found in the include rules in the
|
||||
top level file `Makefile.opk`. Examples of how these are defined for the OP-TEE
|
||||
port can be found in file `Makefile.optee`.
|
||||
26
oemcrypto/opk/build/ta.gyp
Normal file
26
oemcrypto/opk/build/ta.gyp
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Builds a static library which contains the TEE
|
||||
# serialization code, dispatcher and the OEMCrypto TA
|
||||
#
|
||||
{
|
||||
'includes' : [
|
||||
'../serialization/settings.gypi',
|
||||
],
|
||||
'targets' : [
|
||||
{
|
||||
'target_name' : 'ta',
|
||||
'toolsets' : [ 'target' ],
|
||||
'type' : 'static_library',
|
||||
'standalone_static_library' : 1,
|
||||
'dependencies' : [
|
||||
'<(odk_dir)/src/odk.gyp:odk',
|
||||
'<(oemcrypto_ta_dir)/oemcrypto_ta.gyp:oemcrypto_ta',
|
||||
'<(oemcrypto_ta_dir)/wtpi_reference/wtpi_reference.gyp:oemcrypto_ta_reference_renewal',
|
||||
'<(oemcrypto_ta_dir)/wtpi_reference/wtpi_reference.gyp:oemcrypto_ta_reference_root_of_trust',
|
||||
'<(oemcrypto_ta_dir)/wtpi_reference/wtpi_reference.gyp:oemcrypto_ta_reference_clock',
|
||||
'<(oemcrypto_ta_dir)/wtpi_reference/wtpi_reference.gyp:oemcrypto_ta_reference_crypto',
|
||||
'<(tee_dir)/tee.gyp:opk_tee',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
3594
oemcrypto/opk/oemcrypto_ta/oemcrypto.c
Normal file
3594
oemcrypto/opk/oemcrypto_ta/oemcrypto.c
Normal file
File diff suppressed because it is too large
Load Diff
43
oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h
Normal file
43
oemcrypto/opk/oemcrypto_ta/oemcrypto_api_macros.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_
|
||||
|
||||
// Partners can force OPK to run in debug/not debug mode by defining
|
||||
// OPK_IS_DEBUG to true or false, respectively, in the compiler flags. If it is
|
||||
// not defined by the compiler flags, we make a best-effort to infer it from
|
||||
// values that are conventionally defined to indicate debug/non-debug.
|
||||
#if !defined(OPK_IS_DEBUG)
|
||||
# if !defined(NDEBUG) && defined(_DEBUG)
|
||||
# define OPK_IS_DEBUG true
|
||||
# else
|
||||
# define OPK_IS_DEBUG false
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if OPK_IS_DEBUG
|
||||
# define OPK_IS_DEBUG_STR "d"
|
||||
#else
|
||||
# define OPK_IS_DEBUG_STR ""
|
||||
#endif
|
||||
|
||||
// For the OPK version, mirror the major.minor values of the supported OEMCrypto
|
||||
// API. Increment the patch value to distinguish between different releases of
|
||||
// OPK for a single major.minor pair. For example, the first release of OPK
|
||||
// supporting v17.0 would be v17.0.0, and the second release supporting the same
|
||||
// OEMCrypto API version would be v17.0.1; once the supported OEMCrypto API
|
||||
// version bumps to v17.1, the first released OPK implementation would be
|
||||
// v17.1.0
|
||||
#define API_MAJOR_VERSION 17
|
||||
#define API_MINOR_VERSION 0
|
||||
#define OPK_PATCH_VERSION 1
|
||||
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
#define BUILD_INFO() \
|
||||
"Widevine OPK v" XSTR(API_MAJOR_VERSION) "." XSTR( \
|
||||
API_MINOR_VERSION) "." XSTR(OPK_PATCH_VERSION) OPK_IS_DEBUG_STR
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */
|
||||
111
oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.c
Normal file
111
oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_asymmetric_key_table.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
#if MAX_NUMBER_OF_ASYMMETRIC_KEYS <= 0
|
||||
# error "MAX_NUMBER_OF_ASYMMETRIC_KEYS must be > 0"
|
||||
#elif MAX_NUMBER_OF_ASYMMETRIC_KEYS >= UINT32_MAX - 1
|
||||
# error "MAX_NUMBER_OF_ASYMMETRIC_KEYS is too large"
|
||||
#endif
|
||||
|
||||
static OEMCryptoResult DtorTrampoline(void* key) {
|
||||
return OPKI_FreeAsymmetricKey((AsymmetricKey*)key);
|
||||
}
|
||||
|
||||
DEFINE_OBJECT_TABLE(key_table, AsymmetricKey, MAX_NUMBER_OF_ASYMMETRIC_KEYS,
|
||||
&DtorTrampoline);
|
||||
|
||||
void OPKI_InitializeAsymmetricKeyTable(void) {
|
||||
OPKI_UnsafeClearObjectTable(&key_table);
|
||||
}
|
||||
|
||||
uint32_t OPKI_MaxNumberOfAsymmetricKeys(void) { return key_table.capacity; }
|
||||
|
||||
OEMCryptoResult OPKI_NumberOfUsedAsymmetricKeys(uint32_t* num_used_keys) {
|
||||
if (num_used_keys == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
*num_used_keys = OPKI_GetObjectTableUseCount(&key_table);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_CreateAsymmetricKey(
|
||||
AsymmetricKey** key, AsymmetricKeyType key_type, const uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length, size_t key_size, uint32_t allowed_schemes) {
|
||||
if (key == NULL || wrapped_key == NULL || wrapped_key_length == 0)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
OEMCryptoResult result;
|
||||
if (*key != NULL) {
|
||||
result = OPKI_FreeAsymmetricKeyFromTable(key);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
}
|
||||
bool key_found = false;
|
||||
AsymmetricKey* cur_key = NULL;
|
||||
/* If the same wrapped key already exists in the key table, just updates the
|
||||
* reference counter and returns a pointer to the existing key. */
|
||||
for (uint32_t i = 0; i < key_table.capacity; i++) {
|
||||
cur_key = (AsymmetricKey*)OPKI_GetFromObjectTable(&key_table, i);
|
||||
if (cur_key != NULL && key_type == cur_key->key_type &&
|
||||
wrapped_key_length == cur_key->wrapped_key_length &&
|
||||
memcmp(wrapped_key, cur_key->wrapped_key, wrapped_key_length) == 0) {
|
||||
key_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!key_found) {
|
||||
cur_key = OPKI_AllocFromObjectTable(&key_table, NULL);
|
||||
if (cur_key == NULL) return OEMCrypto_ERROR_TOO_MANY_KEYS;
|
||||
result = OPKI_InitializeAsymmetricKey(cur_key, key_type, wrapped_key,
|
||||
wrapped_key_length, key_size,
|
||||
allowed_schemes);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
OPKI_FreeAsymmetricKeyFromTable(key);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
cur_key->ref_count++;
|
||||
*key = cur_key;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeAsymmetricKeyFromTable(AsymmetricKey** key) {
|
||||
if (!key) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if ((*key) == NULL) return OEMCrypto_SUCCESS;
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
if ((*key)->ref_count > 0) {
|
||||
(*key)->ref_count--;
|
||||
}
|
||||
if ((*key)->ref_count == 0) {
|
||||
result = OPKI_FreeFromObjectTable(&key_table, *key);
|
||||
}
|
||||
if (result == OEMCrypto_SUCCESS) *key = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_TerminateAsymmetricKeyTable(void) {
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < key_table.capacity; i++) {
|
||||
AsymmetricKey* key = OPKI_GetFromObjectTable(&key_table, i);
|
||||
if (key) {
|
||||
/* Attempt to free the key. */
|
||||
key->ref_count = 0;
|
||||
OEMCryptoResult free_result = OPKI_FreeAsymmetricKeyFromTable(&key);
|
||||
if (free_result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could not free asymmetric key at index %u with error: %u", i,
|
||||
free_result);
|
||||
if (result == OEMCrypto_SUCCESS) {
|
||||
result = OEMCrypto_ERROR_TERMINATE_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
48
oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.h
Normal file
48
oemcrypto/opk/oemcrypto_ta/oemcrypto_asymmetric_key_table.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_ASYMMETRIC_KEY_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_ASYMMETRIC_KEY_TABLE_H_
|
||||
|
||||
#include "oemcrypto_key.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
|
||||
/* Initializes the key table so the session can grab keys at a late point. */
|
||||
void OPKI_InitializeAsymmetricKeyTable(void);
|
||||
|
||||
/* Gets the max number of asymmetric keys. */
|
||||
uint32_t OPKI_MaxNumberOfAsymmetricKeys(void);
|
||||
|
||||
/* Gets the number of currently used keys. Returns
|
||||
OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not been initialized
|
||||
and OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |num_used_keys| and it must not be NULL. */
|
||||
OEMCryptoResult OPKI_NumberOfUsedAsymmetricKeys(uint32_t* num_used_keys);
|
||||
|
||||
/* Grabs, gets, and initializes a AsymmetricKey. If |key| points to an existing
|
||||
key, this method tries to free it before continuing. If there is an error in
|
||||
generating the new key, this method will free it before returning and set
|
||||
*|key| to NULL. If successful, caller gains ownership of *|key| and it must
|
||||
not be NULL. */
|
||||
OEMCryptoResult OPKI_CreateAsymmetricKey(
|
||||
AsymmetricKey** key, AsymmetricKeyType key_type, const uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length, size_t key_size, uint32_t allowed_schemes);
|
||||
|
||||
/* Given a pointer to a AsymmetricKey*, attempts to free the AsymmetricKey it
|
||||
points to if it exists, and then sets the pointer to the AsymmetricKey to
|
||||
NULL. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not
|
||||
been initialized, OEMCrypto_ERROR_INVALID_CONTEXT if the non-null
|
||||
AsymmetricKey has not been grabbed or if its index is invalid. Returns the
|
||||
result of freeing the AsymmetricKey otherwise. If there is an existing error
|
||||
in the caller, in which case this is likely used for cleanup, that error will
|
||||
be returned and the result of this shall be ignored. Caller retains ownership
|
||||
of *|key| but **|key| will be destroyed if *|key| is not NULL. */
|
||||
OEMCryptoResult OPKI_FreeAsymmetricKeyFromTable(AsymmetricKey** key);
|
||||
|
||||
/* Clears and cleans up the key table. The key table must be reinitialized to be
|
||||
used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if there are any active keys
|
||||
still. Returns OEMCrypto_SUCCESS otherwise. */
|
||||
OEMCryptoResult OPKI_TerminateAsymmetricKeyTable(void);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_ASYMMETRIC_KEY_TABLE_H_ */
|
||||
37
oemcrypto/opk/oemcrypto_ta/oemcrypto_check_macros.h
Normal file
37
oemcrypto/opk/oemcrypto_ta/oemcrypto_check_macros.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef OEMCRYPTO_CHECK_MACROS_H_
|
||||
#define OEMCRYPTO_CHECK_MACROS_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
// Error-checking conditionals should almost always be done in-line in the
|
||||
// function where they occur, for ease of readability and verifiability of the
|
||||
// code. We made an exception for these two cases and wrote macros for them
|
||||
// because they are so common that they appear multiple times in nearly every
|
||||
// function.
|
||||
|
||||
#define RETURN_INVALID_CONTEXT_IF_NULL(parameter) \
|
||||
if (UNLIKELY((parameter) == NULL)) { \
|
||||
LOGE(#parameter " is NULL"); \
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT; \
|
||||
}
|
||||
|
||||
#define ABORT_IF_NULL(parameter) \
|
||||
ABORT_IF(parameter == NULL, #parameter " is NULL");
|
||||
|
||||
#define RETURN_INVALID_CONTEXT_IF_ZERO(parameter) \
|
||||
if (UNLIKELY((parameter) == 0)) { \
|
||||
LOGE(#parameter " is zero"); \
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT; \
|
||||
}
|
||||
|
||||
#define ABORT_IF_ZERO(parameter) \
|
||||
ABORT_IF(parameter == 0, #parameter " is zero");
|
||||
|
||||
#endif // OEMCRYPTO_CHECK_MACROS_H_
|
||||
50
oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_attributes.h
Normal file
50
oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_attributes.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_COMPILER_ATTRIBUTES_H_
|
||||
#define OEMCRYPTO_COMPILER_ATTRIBUTES_H_
|
||||
|
||||
#include "oemcrypto_compiler_detection.h"
|
||||
|
||||
/* Nonstandard attribute annotations */
|
||||
#if __has_attribute(__unused__) || COMPATIBLE_WITH_GCC(2) || \
|
||||
COMPATIBLE_WITH_CLANG(4)
|
||||
# define UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
# define UNUSED
|
||||
#endif
|
||||
|
||||
#if __has_attribute(__noreturn__) || COMPATIBLE_WITH_GCC(2) || \
|
||||
COMPATIBLE_WITH_CLANG(4)
|
||||
# define NORETURN __attribute__((__noreturn__))
|
||||
#else
|
||||
# define NORETURN
|
||||
#endif
|
||||
|
||||
#if __has_attribute(__warn_unused_result__) || COMPATIBLE_WITH_GCC(4) || \
|
||||
COMPATIBLE_WITH_CLANG(4)
|
||||
# define NO_IGNORE_RESULT __attribute__((__warn_unused_result__))
|
||||
#else
|
||||
# define NO_IGNORE_RESULT
|
||||
#endif
|
||||
|
||||
/* Nonstandard likeliness annotations */
|
||||
#if __has_builtin(__builtin_expect) || COMPATIBLE_WITH_GCC(3) || \
|
||||
COMPATIBLE_WITH_CLANG(3)
|
||||
# define LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
# define LIKELY(x) (x)
|
||||
# define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
/* Sanitizer annotations */
|
||||
#if COMPATIBLE_WITH_CLANG(4)
|
||||
# define UBSAN_IGNORE_UNSIGNED_OVERFLOW \
|
||||
__attribute__((__no_sanitize__("unsigned-integer-overflow")))
|
||||
#else
|
||||
# define UBSAN_IGNORE_UNSIGNED_OVERFLOW
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_COMPILER_ATTRIBUTES_H_ */
|
||||
38
oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_detection.h
Normal file
38
oemcrypto/opk/oemcrypto_ta/oemcrypto_compiler_detection.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_COMPILER_DETECTION_H_
|
||||
#define OEMCRYPTO_COMPILER_DETECTION_H_
|
||||
|
||||
/* These polyfills allow us to use __has_attribute() and __has_builtin() even
|
||||
on compilers that did not have them, simplifying preprocessor logic
|
||||
elsewhere.
|
||||
|
||||
Note that __has_attribute() and __has_builtin() were often not available on
|
||||
older compilers. Universal support in GCC & Clang didn't come until 2020.
|
||||
Checks for attributes or builtins that are older than that should also
|
||||
perform a manual compiler version check to detect older compilers. */
|
||||
|
||||
#if !defined(__has_attribute)
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if !defined(__has_builtin)
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define COMPATIBLE_WITH_GCC(major_version) (__GNUC__ >= (major_version))
|
||||
#else
|
||||
# define COMPATIBLE_WITH_GCC(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && defined(__clang_major__)
|
||||
# define COMPATIBLE_WITH_CLANG(major_version) \
|
||||
(__clang_major__ >= (major_version))
|
||||
#else
|
||||
# define COMPATIBLE_WITH_CLANG(x) 0
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_COMPILER_DETECTION_H_ */
|
||||
60
oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c
Normal file
60
oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_entitled_key_session.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_util.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_key_table.h"
|
||||
|
||||
OEMCryptoResult OPKI_InitializeEntitledKeySession(
|
||||
OEMCryptoEntitledKeySession* session, OEMCrypto_SESSION key_session_id,
|
||||
OEMCrypto_SESSION entitlement_session_id) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(session);
|
||||
session->key_session_id = key_session_id;
|
||||
session->current_entitled_content_key_index = CONTENT_KEYS_PER_SESSION;
|
||||
for (int i = 0; i < CONTENT_KEYS_PER_SESSION; i++) {
|
||||
session->entitled_content_keys[i] = NULL;
|
||||
session->entitlement_keys[i] = (EntitlementKeyInfo){0};
|
||||
}
|
||||
session->num_entitled_content_keys = 0;
|
||||
session->entitlement_session_id = entitlement_session_id;
|
||||
session->decrypt_hash = (DecryptHash){
|
||||
.hash_error = OEMCrypto_SUCCESS,
|
||||
};
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_TerminateEntitledKeySession(
|
||||
OEMCryptoEntitledKeySession* session) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(session);
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < session->num_entitled_content_keys; i++) {
|
||||
OEMCryptoResult free_key_result =
|
||||
OPKI_FreeKeyFromTable(&session->entitled_content_keys[i]);
|
||||
if (result == OEMCrypto_SUCCESS) result = free_key_result;
|
||||
}
|
||||
*session = (OEMCryptoEntitledKeySession){0};
|
||||
return result;
|
||||
}
|
||||
|
||||
SymmetricKey* OPKI_FindEntitledContentKeyFromTable(
|
||||
OEMCryptoEntitledKeySession* session, const uint8_t* key_id,
|
||||
size_t key_id_length) {
|
||||
if (session == NULL || key_id == NULL || key_id_length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
SymmetricKey** key_table = session->entitled_content_keys;
|
||||
for (size_t i = 0; i < session->num_entitled_content_keys; i++) {
|
||||
SymmetricKey* key = key_table[i];
|
||||
ABORT_IF(key == NULL, "Key at index %zu is NULL", i);
|
||||
if (key_id_length == key->key_id_size &&
|
||||
memcmp(key->key_id, key_id, key_id_length) == 0) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
61
oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h
Normal file
61
oemcrypto/opk/oemcrypto_ta/oemcrypto_entitled_key_session.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_key.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
|
||||
typedef struct EntitlementKeyInfo {
|
||||
uint8_t entitlement_key_id[KEY_ID_MAX_SIZE];
|
||||
size_t entitlement_key_id_size;
|
||||
} EntitlementKeyInfo;
|
||||
|
||||
typedef struct DecryptHash {
|
||||
/* These are used when doing full decrypt path testing. */
|
||||
bool compute_hash; /* True if the current frame needs a hash. */
|
||||
uint32_t current_hash; /* Running CRC hash of frame. */
|
||||
uint32_t given_hash; /* True CRC hash of frame. */
|
||||
uint32_t current_frame_number; /* Current frame for CRC hash. */
|
||||
uint32_t bad_frame_number; /* Frame number with bad hash. */
|
||||
OEMCryptoResult hash_error; /* Error code for first bad frame. */
|
||||
} DecryptHash;
|
||||
|
||||
typedef struct OEMCryptoEntitledKeySession {
|
||||
OEMCrypto_SESSION key_session_id;
|
||||
uint32_t current_entitled_content_key_index;
|
||||
SymmetricKey* entitled_content_keys[CONTENT_KEYS_PER_SESSION];
|
||||
uint32_t num_entitled_content_keys;
|
||||
/* entitlement key info of each entitled content key. */
|
||||
EntitlementKeyInfo entitlement_keys[CONTENT_KEYS_PER_SESSION];
|
||||
/* the OEMCrypto session that this entitled key session is associated with. */
|
||||
OEMCrypto_SESSION entitlement_session_id;
|
||||
DecryptHash decrypt_hash;
|
||||
} OEMCryptoEntitledKeySession;
|
||||
|
||||
/* Initializes entitled key session |session| with id |key_session_id| and the
|
||||
entitlement session id |entitlement_session_id|. Returns OEMCrypto_SUCCESS.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_InitializeEntitledKeySession(
|
||||
OEMCryptoEntitledKeySession* session, OEMCrypto_SESSION key_session_id,
|
||||
OEMCrypto_SESSION entitlement_session_id);
|
||||
|
||||
/* Cleans up the entitled key session by freeing any used keys and clearing any
|
||||
state. Returns the result of freeing the keys in the session.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_TerminateEntitledKeySession(OEMCryptoEntitledKeySession* session);
|
||||
|
||||
/* Finds the entitled content key from the key table in |session| with the given
|
||||
|key_id| and |key_id_length|. Returns either the key if there is a match or
|
||||
NULL otherwise. |key_id_length| must be > 0.
|
||||
Caller retains ownership of all parameters and they must not be NULL. */
|
||||
SymmetricKey* OPKI_FindEntitledContentKeyFromTable(
|
||||
OEMCryptoEntitledKeySession* session, const uint8_t* key_id,
|
||||
size_t key_id_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_H_ */
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_entitled_key_session_table.h"
|
||||
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_entitled_key_session.h"
|
||||
#include "oemcrypto_key.h"
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "oemcrypto_session_table.h"
|
||||
#include "oemcrypto_session_type.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
static OEMCryptoResult DtorTrampoline(void* session) {
|
||||
return OPKI_TerminateEntitledKeySession(
|
||||
(OEMCryptoEntitledKeySession*)session);
|
||||
}
|
||||
|
||||
DEFINE_OBJECT_TABLE(entitled_key_session_table, OEMCryptoEntitledKeySession,
|
||||
MAX_NUMBER_OF_ENTITLED_KEY_SESSIONS, &DtorTrampoline);
|
||||
|
||||
void OPKI_InitializeEntitledKeySessionTable(void) {
|
||||
OPKI_UnsafeClearObjectTable(&entitled_key_session_table);
|
||||
}
|
||||
|
||||
uint32_t OPKI_MaxNumberOfEntitledKeySessions(void) {
|
||||
return entitled_key_session_table.capacity;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_GrabEntitledKeySession(
|
||||
OEMCrypto_SESSION* key_session_id,
|
||||
OEMCrypto_SESSION entitlement_session_id) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(key_session_id);
|
||||
/* index is the entry position in entitled key session table. */
|
||||
uint32_t index;
|
||||
OEMCryptoEntitledKeySession* session =
|
||||
OPKI_AllocFromObjectTable(&entitled_key_session_table, &index);
|
||||
if (!session) {
|
||||
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
|
||||
}
|
||||
/* Check if too many entitled key sessions have been opened.
|
||||
* This should never happen as the index shall never go beyond
|
||||
* (1u << SESSION_ID_TYPE_SHIFT) and use the higher bits, which is reserved
|
||||
* for session type. */
|
||||
if ((index >> SESSION_ID_TYPE_SHIFT) != 0) {
|
||||
LOGE("Could not allocate entitled key session with index: %u", index);
|
||||
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
|
||||
}
|
||||
*key_session_id = index;
|
||||
OEMCryptoResult result =
|
||||
OPKI_ApplySessionType(key_session_id, SESSION_TYPE_ENTITLED_KEY);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
return OPKI_InitializeEntitledKeySession(session, *key_session_id,
|
||||
entitlement_session_id);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_GetEntitledKeySession(
|
||||
OEMCrypto_SESSION key_session_id, OEMCryptoEntitledKeySession** session) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(session);
|
||||
if (OPKI_GetSessionType(key_session_id) != SESSION_TYPE_ENTITLED_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
|
||||
}
|
||||
uint32_t index = (key_session_id & SESSION_ID_MASK);
|
||||
*session = OPKI_GetFromObjectTable(&entitled_key_session_table, index);
|
||||
if (!*session) {
|
||||
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeEntitledKeySession(OEMCrypto_SESSION key_session_id) {
|
||||
if (OPKI_GetSessionType(key_session_id) != SESSION_TYPE_ENTITLED_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
|
||||
}
|
||||
uint32_t index = (key_session_id & SESSION_ID_MASK);
|
||||
return OPKI_FreeFromObjectTableByIndex(&entitled_key_session_table, index);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeEntitledKeySessions(
|
||||
OEMCrypto_SESSION entitlement_session_id) {
|
||||
if (OPKI_GetSessionType(entitlement_session_id) != SESSION_TYPE_OEMCRYPTO) {
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < entitled_key_session_table.capacity; i++) {
|
||||
OEMCryptoEntitledKeySession* session =
|
||||
OPKI_GetFromObjectTable(&entitled_key_session_table, i);
|
||||
if (session != NULL &&
|
||||
session->entitlement_session_id == entitlement_session_id) {
|
||||
OEMCryptoResult free_result =
|
||||
OPKI_FreeEntitledKeySession(session->key_session_id);
|
||||
if (free_result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could not free entitled key session %u with error: %u", i,
|
||||
free_result);
|
||||
if (result == OEMCrypto_SUCCESS) result = free_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_TerminateEntitledKeySessionTable(void) {
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < entitled_key_session_table.capacity; i++) {
|
||||
OEMCryptoEntitledKeySession* session =
|
||||
OPKI_GetFromObjectTable(&entitled_key_session_table, i);
|
||||
if (session) {
|
||||
result = OEMCrypto_ERROR_TERMINATE_FAILED;
|
||||
/* Attempt to free the entitled key session. */
|
||||
OEMCryptoResult free_result =
|
||||
OPKI_FreeFromObjectTableByIndex(&entitled_key_session_table, i);
|
||||
if (free_result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could not free entitled key session %u with error: %u", i,
|
||||
free_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_TABLE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_entitled_key_session.h"
|
||||
#include "oemcrypto_session.h"
|
||||
|
||||
/* Initializes the entitled key session table. */
|
||||
void OPKI_InitializeEntitledKeySessionTable(void);
|
||||
|
||||
/* Gets the max number of entitled key sessions. */
|
||||
NO_IGNORE_RESULT uint32_t OPKI_MaxNumberOfEntitledKeySessions(void);
|
||||
|
||||
/* Attempts to grab an open entry in the entitled key session table and
|
||||
associates it with the entitlement session id |entitlement_session_id|.
|
||||
Places the grabbed entitled key session id in |key_session_id|. Returns
|
||||
OEMCrypto_ERROR_TOO_MANY_SESSIONS if there are no sessions left to grab.
|
||||
Returns OEMCrypto_SUCCESS otherwise. Caller retains ownership of
|
||||
|key_session_id| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_GrabEntitledKeySession(OEMCrypto_SESSION* key_session_id,
|
||||
OEMCrypto_SESSION entitlement_session_id);
|
||||
|
||||
/* Gets the entitled key session specified by |key_session_id| in the entitled
|
||||
key session table, and places it into *|session|. Returns
|
||||
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION if the session has not been
|
||||
grabbed or if the |key_session_id| is invalid. Returns OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GetEntitledKeySession(
|
||||
OEMCrypto_SESSION key_session_id, OEMCryptoEntitledKeySession** session);
|
||||
|
||||
/* Given a non-free entitled key session |key_session_id|, attempts to free it
|
||||
so it can be reused. Returns OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION if
|
||||
the |key_session_id| is invalid. Returns OEMCrypto_SUCCESS otherwise.
|
||||
*/
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_FreeEntitledKeySession(OEMCrypto_SESSION key_session_id);
|
||||
|
||||
/* Given an entitlement session |entitlement_session_id|, attempts to free all
|
||||
the entitled key sessions that are associated with this session. Returns
|
||||
OEMCrypto_ERROR_INVALID_SESSION if the |entitlement_session_id| is invalid.
|
||||
Returns the result of OPKI_FreeEntitledKeySession() if there is any error
|
||||
when attempting to free any entitled key session, and OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
*/
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_FreeEntitledKeySessions(OEMCrypto_SESSION entitlement_session_id);
|
||||
|
||||
/* Clears and cleans up the entitled key session table. The table must be
|
||||
reinitialized to be used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if there
|
||||
are any active sessions still. Returns OEMCrypto_SUCCESS otherwise. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_TerminateEntitledKeySessionTable(void);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_ENTITLED_KEY_SESSION_TABLE_H_ */
|
||||
93
oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c
Normal file
93
oemcrypto/opk/oemcrypto_ta/oemcrypto_key.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_key.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
OEMCryptoResult OPKI_InitializeSymmetricKey(SymmetricKey* key,
|
||||
SymmetricKeyType key_type,
|
||||
KeySize key_size) {
|
||||
if (key == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
key->key_handle = NULL;
|
||||
memset(key->key_id, 0, KEY_ID_MAX_SIZE);
|
||||
key->key_id_size = 0;
|
||||
key->key_type = key_type;
|
||||
key->key_size = key_size;
|
||||
memset(&key->key_control_block, 0, sizeof(key->key_control_block));
|
||||
key->is_entitled_content_key = false;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_InitializeAsymmetricKey(
|
||||
AsymmetricKey* key, AsymmetricKeyType key_type, const uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length, size_t key_size, uint32_t allowed_schemes) {
|
||||
if (key == NULL || wrapped_key == NULL || wrapped_key_length == 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (wrapped_key_length > sizeof(key->wrapped_key)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
key->key_type = key_type;
|
||||
memcpy(key->wrapped_key, wrapped_key, wrapped_key_length);
|
||||
key->wrapped_key_length = wrapped_key_length;
|
||||
key->key_size = key_size;
|
||||
key->allowed_schemes = allowed_schemes;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeSymmetricKey(SymmetricKey* key) {
|
||||
if (key == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if (key->key_handle != NULL) {
|
||||
OEMCryptoResult result = WTPI_K1_FreeKeyHandle(key->key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
memset(key, 0, sizeof(SymmetricKey));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeAsymmetricKey(AsymmetricKey* key) {
|
||||
if (key == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
memset(key, 0, sizeof(AsymmetricKey));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool OPKI_CheckKey(SymmetricKey* key, SymmetricKeyType key_type) {
|
||||
return key != NULL && key->key_type == key_type;
|
||||
}
|
||||
|
||||
bool OPKI_PrivateKeyTypeToAsymmetricKey(OEMCrypto_PrivateKeyType priv_key_type,
|
||||
AsymmetricKeyType* asym_key_type) {
|
||||
if (asym_key_type == NULL) return false;
|
||||
switch (priv_key_type) {
|
||||
case OEMCrypto_RSA_Private_Key:
|
||||
*asym_key_type = DRM_RSA_PRIVATE_KEY;
|
||||
return true;
|
||||
case OEMCrypto_ECC_Private_Key:
|
||||
*asym_key_type = DRM_ECC_PRIVATE_KEY;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OPKI_AsymmetricKeyToPrivateKeyType(
|
||||
AsymmetricKeyType asym_key_type, OEMCrypto_PrivateKeyType* priv_key_type) {
|
||||
if (priv_key_type == NULL) return false;
|
||||
switch (asym_key_type) {
|
||||
case DRM_RSA_PRIVATE_KEY:
|
||||
*priv_key_type = OEMCrypto_RSA_Private_Key;
|
||||
return true;
|
||||
case DRM_ECC_PRIVATE_KEY:
|
||||
*priv_key_type = OEMCrypto_ECC_Private_Key;
|
||||
return true;
|
||||
case PROV40_ED25519_PRIVATE_KEY:
|
||||
// ED25519 key can only be used in provisioning 4 BCC.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
86
oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h
Normal file
86
oemcrypto/opk/oemcrypto_ta/oemcrypto_key.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_H_
|
||||
|
||||
#include "oemcrypto_key_control_block.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "wtpi_config_macros.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
#include "wtpi_crypto_asymmetric_interface.h"
|
||||
|
||||
typedef struct SymmetricKey {
|
||||
SymmetricKeyType key_type;
|
||||
/* key_handle is owned by the TEE. */
|
||||
WTPI_K1_SymmetricKey_Handle key_handle;
|
||||
KeySize key_size;
|
||||
/* For entitlement or content keys only. */
|
||||
uint8_t key_id[KEY_ID_MAX_SIZE];
|
||||
uint8_t key_id_size;
|
||||
KeyControlBlock key_control_block;
|
||||
/* Index into either the content or entitlement key table in the oemcrypto
|
||||
* session, or the entitled content key table in the entitled key session. */
|
||||
uint32_t session_key_index;
|
||||
bool is_entitled_content_key;
|
||||
OEMCryptoCipherMode cipher_mode;
|
||||
} SymmetricKey;
|
||||
|
||||
typedef struct AsymmetricKey {
|
||||
AsymmetricKeyType key_type;
|
||||
uint8_t wrapped_key[MAX_WRAPPED_ASYMMETRIC_KEY_SIZE];
|
||||
size_t wrapped_key_length;
|
||||
uint32_t ref_count;
|
||||
/* This is the actual size of the asymmetric key in bytes when being loaded.
|
||||
It is a shortcut to getting the key size without unwrapping the key first.
|
||||
It is useful in cases where only the key size is needed but not the key
|
||||
content. */
|
||||
size_t key_size;
|
||||
uint32_t allowed_schemes;
|
||||
} AsymmetricKey;
|
||||
|
||||
/* Initializes the data fields in the |key| and sets |key|'s key_handle to
|
||||
an empty handle.
|
||||
Returns the result of WTPI_CreateKeyHandle if it fails and OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
|serialized_bytes_length| must be > 0 and |key_type| must be valid.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
OEMCryptoResult OPKI_InitializeSymmetricKey(SymmetricKey* key,
|
||||
SymmetricKeyType key_type,
|
||||
KeySize key_size);
|
||||
|
||||
/* Initializes the data fields in the |key|.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
OEMCryptoResult OPKI_InitializeAsymmetricKey(
|
||||
AsymmetricKey* key, AsymmetricKeyType key_type, const uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length, size_t key_size, uint32_t allowed_schemes);
|
||||
|
||||
/* Frees the key handle associated with this key if it exists and then clears
|
||||
the key. Returns the result of freeing the key handle.
|
||||
Caller retains ownership of |key| and it must not be NULL. */
|
||||
OEMCryptoResult OPKI_FreeSymmetricKey(SymmetricKey* key);
|
||||
|
||||
/* Clears the asymmetric *|key|.
|
||||
Caller retains ownership of |key| and it must not be NULL. */
|
||||
OEMCryptoResult OPKI_FreeAsymmetricKey(AsymmetricKey* key);
|
||||
|
||||
/* Checks to make sure that |key| isn't NULL, its key_type matches |key_type|.
|
||||
*/
|
||||
bool OPKI_CheckKey(SymmetricKey* key, SymmetricKeyType key_type);
|
||||
|
||||
/* Converts the OEMCrypto API OEMCrypto_PrivateKeyType value to the equivalent
|
||||
OPK API AsymmetricKeyType value.
|
||||
Returns true if the value was converted successfully, false otherwise.
|
||||
Caller retains ownership of |asym_key_type| and it must not be NULL. */
|
||||
bool OPKI_PrivateKeyTypeToAsymmetricKey(OEMCrypto_PrivateKeyType priv_key_type,
|
||||
AsymmetricKeyType* asym_key_type);
|
||||
|
||||
/* Converts the OPK API AsymmetricKeyType value to the equivalent OEMCrypto API
|
||||
OEMCrypto_PrivateKeyType value.
|
||||
Returns true if the value was converted successfully, false otherwise. Caller
|
||||
retains ownership of |priv_key_type| and it must not be NULL. */
|
||||
bool OPKI_AsymmetricKeyToPrivateKeyType(
|
||||
AsymmetricKeyType asym_key_type, OEMCrypto_PrivateKeyType* priv_key_type);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_H_ */
|
||||
50
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.c
Normal file
50
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_key_control_block.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_endian.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
/* This extracts 4 bytes in network byte order to a 32 bit integer in host byte
|
||||
order. */
|
||||
static uint32_t extract_field_from_KCB(const uint8_t* kcb, uint8_t index) {
|
||||
ABORT_IF(kcb == NULL, "Key control block pointer is NULL");
|
||||
ABORT_IF(index > 3, "Invalid index: %hhu", index);
|
||||
const uint8_t byte_index = index * 4;
|
||||
uint32_t network_order_value;
|
||||
memcpy(&network_order_value, &kcb[byte_index], sizeof(network_order_value));
|
||||
return oemcrypto_be32toh(network_order_value);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_ParseKeyControlBlock(const uint8_t* kcb,
|
||||
KeyControlBlock* out) {
|
||||
if (kcb == NULL || out == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
KeyControlBlock key_control_block;
|
||||
memset(&key_control_block, 0, sizeof(key_control_block));
|
||||
memcpy(key_control_block.verification, kcb, 4);
|
||||
key_control_block.duration = extract_field_from_KCB(kcb, 1);
|
||||
key_control_block.nonce = extract_field_from_KCB(kcb, 2);
|
||||
key_control_block.control_bits = extract_field_from_KCB(kcb, 3);
|
||||
|
||||
const char* verification = key_control_block.verification;
|
||||
if (memcmp(verification, "kc17", 4) && /* add in version 17 api */
|
||||
memcmp(verification, "kc16", 4) && /* add in version 16 api */
|
||||
memcmp(verification, "kc15", 4) && /* add in version 15 api */
|
||||
memcmp(verification, "kc14", 4) && /* add in version 14 api */
|
||||
memcmp(verification, "kc13", 4) && /* add in version 13 api */
|
||||
memcmp(verification, "kc12", 4) && /* add in version 12 api */
|
||||
memcmp(verification, "kc11", 4) && /* add in version 11 api */
|
||||
memcmp(verification, "kc10", 4) && /* add in version 10 api */
|
||||
memcmp(verification, "kc09", 4) && /* add in version 9 api */
|
||||
memcmp(verification, "kctl", 4)) { /* original verification */
|
||||
key_control_block.valid = false;
|
||||
} else {
|
||||
key_control_block.valid = true;
|
||||
}
|
||||
*out = key_control_block;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
26
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.h
Normal file
26
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_control_block.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_CONTROL_BLOCK_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_CONTROL_BLOCK_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
|
||||
typedef struct KeyControlBlock {
|
||||
bool valid;
|
||||
char verification[4];
|
||||
uint32_t duration;
|
||||
uint32_t nonce;
|
||||
uint32_t control_bits;
|
||||
} KeyControlBlock;
|
||||
|
||||
/* Parses the given |kcb| into a KeyControlBlock. Caller retains ownership of
|
||||
all pointers, and they must not be NULL. Returns an error if anything
|
||||
occurred that prevented parsing. If parsing succeeds but verification fails,
|
||||
the function still returns OEMCrypto_SUCCESS, but the KeyControlBlock is
|
||||
marked as invalid. */
|
||||
OEMCryptoResult OPKI_ParseKeyControlBlock(const uint8_t* kcb,
|
||||
KeyControlBlock* out);
|
||||
|
||||
#endif // OEMCRYPTO_TA_OEMCRYPTO_KEY_CONTROL_BLOCK_H_
|
||||
76
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.c
Normal file
76
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_key_table.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
#if MAX_NUMBER_OF_KEYS <= 0
|
||||
# error "MAX_NUMBER_OF_KEYS must be > 0"
|
||||
#elif MAX_NUMBER_OF_KEYS >= UINT32_MAX - 1
|
||||
# error "MAX_NUMBER_OF_KEYS is too large"
|
||||
#endif
|
||||
|
||||
static OEMCryptoResult DtorTrampoline(void* key) {
|
||||
return OPKI_FreeSymmetricKey((SymmetricKey*)key);
|
||||
}
|
||||
|
||||
DEFINE_OBJECT_TABLE(key_table, SymmetricKey, MAX_NUMBER_OF_KEYS,
|
||||
&DtorTrampoline);
|
||||
|
||||
void OPKI_InitializeKeyTable(void) { OPKI_UnsafeClearObjectTable(&key_table); }
|
||||
|
||||
uint32_t OPKI_MaxNumberOfKeys(void) { return key_table.capacity; }
|
||||
|
||||
OEMCryptoResult OPKI_NumberOfUsedKeys(uint32_t* num_used_keys) {
|
||||
if (num_used_keys == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
*num_used_keys = OPKI_GetObjectTableUseCount(&key_table);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_CreateKey(SymmetricKey** key, SymmetricKeyType key_type,
|
||||
KeySize key_size) {
|
||||
if (key == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
OEMCryptoResult result;
|
||||
if (*key != NULL) {
|
||||
result = OPKI_FreeKeyFromTable(key);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
}
|
||||
*key = OPKI_AllocFromObjectTable(&key_table, NULL);
|
||||
if (!*key) return OEMCrypto_ERROR_TOO_MANY_KEYS;
|
||||
result = OPKI_InitializeSymmetricKey(*key, key_type, key_size);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
OPKI_FreeKeyFromTable(key);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeKeyFromTable(SymmetricKey** key) {
|
||||
if (!key) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
OEMCryptoResult result = OPKI_FreeFromObjectTable(&key_table, *key);
|
||||
if (result == OEMCrypto_SUCCESS) *key = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_TerminateKeyTable(void) {
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < key_table.capacity; i++) {
|
||||
SymmetricKey* key = OPKI_GetFromObjectTable(&key_table, i);
|
||||
if (key) {
|
||||
result = OEMCrypto_ERROR_TERMINATE_FAILED;
|
||||
/* Attempt to free the key. */
|
||||
OEMCryptoResult free_result = OPKI_FreeKeyFromTable(&key);
|
||||
if (free_result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could not free key at index %u with error: %u", i, free_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
48
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.h
Normal file
48
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_table.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_
|
||||
|
||||
#include "oemcrypto_key.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
|
||||
/* Initializes the key table so the session can grab keys at a late point. */
|
||||
void OPKI_InitializeKeyTable(void);
|
||||
|
||||
/* Gets the max number of keys. */
|
||||
uint32_t OPKI_MaxNumberOfKeys(void);
|
||||
|
||||
/* Gets the number of currently used keys. Returns
|
||||
OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not been initialized
|
||||
and OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |num_used_keys| and it must not be NULL. */
|
||||
OEMCryptoResult OPKI_NumberOfUsedKeys(uint32_t* num_used_keys);
|
||||
|
||||
/* Grabs, gets, and initializes a SymmetricKey to an empty key handle.
|
||||
If |key| points to an existing key, this method tries to free it before
|
||||
continuing. If there is an error in generating the new key, this method will
|
||||
free it before returning and set *|key| to NULL.
|
||||
If successful, caller gains ownership of *|key| and it must not be NULL. */
|
||||
OEMCryptoResult OPKI_CreateKey(SymmetricKey** key, SymmetricKeyType key_type,
|
||||
KeySize key_size);
|
||||
|
||||
/* Given a pointer to a SymmetricKey*, attempts to free the SymmetricKey it
|
||||
points to if it exists, and then sets the pointer to the SymmetricKey to
|
||||
NULL. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not
|
||||
been initialized, OEMCrypto_ERROR_INVALID_CONTEXT if the non-null
|
||||
SymmetricKey has not been grabbed or if its index is invalid. Returns the
|
||||
result of freeing the SymmetricKey otherwise. If there is an existing error
|
||||
in the caller, in which case this is likely used for cleanup, that error will
|
||||
be returned and the result of this shall be ignored. Caller retains ownership
|
||||
of *|key| but **|key| will be destroyed if *|key| is not NULL. */
|
||||
OEMCryptoResult OPKI_FreeKeyFromTable(SymmetricKey** key);
|
||||
|
||||
/* Clears and cleans up the key table. The key table must be reinitialized to be
|
||||
used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if the table has not been
|
||||
initialized or if there are any active keys still. Returns OEMCrypto_SUCCESS
|
||||
otherwise. */
|
||||
OEMCryptoResult OPKI_TerminateKeyTable(void);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_ */
|
||||
174
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_types.h
Normal file
174
oemcrypto/opk/oemcrypto_ta/oemcrypto_key_types.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_
|
||||
|
||||
#include "stdbool.h"
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
/* The type of the cryptographic key -- either AES or HMAC. Both used for
|
||||
validation and identification of a key blob. Note that root of trusts are not
|
||||
included in the scope of this interface. They shouldn't be represented by a
|
||||
SymmetricKey. Also note that RSA and ECC keys are a separate type and are
|
||||
treated separately. */
|
||||
typedef enum SymmetricKeyType {
|
||||
// Keys used to decrypt media content and for generic crypto operations. These
|
||||
// are AES-128, only use ECB.
|
||||
// TODO(b/171430591): These should use CTR/CBC instead of handling it
|
||||
// ourselves.
|
||||
CONTENT_KEY = (int)0x336592b0,
|
||||
// The key returned from the license server used to decrypt content keys found
|
||||
// in the media. This is AES-256 CBC and only used for decrypt.
|
||||
ENTITLEMENT_KEY = (int)0x375af9af,
|
||||
// A derived key used to verify HMAC signed responses from the server. This
|
||||
// is SHA2-256 HMAC and should only be used for verify.
|
||||
MAC_KEY_SERVER = (int)0xa09c9790,
|
||||
// A derived key used to sign HMAC signed requests to send to the server. This
|
||||
// is SHA2-256 HMAC and should only be used for sign.
|
||||
MAC_KEY_CLIENT = (int)0x05f09a35,
|
||||
// A derived key used to decrypt keys given from the server and to re-encrypt
|
||||
// the RSA private key for storage. This is AES-128 CBC.
|
||||
ENCRYPTION_KEY = (int)0x8976b781,
|
||||
// A key that is only used to derive other keys. This is typically an
|
||||
// AES-128-CMAC or AES-256-CMAC key, referred to as a session key in the
|
||||
// OEMCrypto specification.
|
||||
DERIVING_KEY = (int)0x6ebe27b7,
|
||||
} SymmetricKeyType;
|
||||
|
||||
/* Private key type used for DRM and OEM certificates. */
|
||||
typedef enum AsymmetricKeyType {
|
||||
// The provisioned device private-key.
|
||||
// RSA keys are used with both decryption and signing.
|
||||
DRM_RSA_PRIVATE_KEY = (int)0x2e912492,
|
||||
// ECC keys are used with both key exchange and signing.
|
||||
DRM_ECC_PRIVATE_KEY = (int)0x539c3183,
|
||||
// ED25519 keys are used in provisioning 4.0.
|
||||
PROV40_ED25519_PRIVATE_KEY = (int)0x495ffa5c,
|
||||
// TODO(b/180530495): Add OEM Cert private key.
|
||||
} AsymmetricKeyType;
|
||||
|
||||
/* The valid possible sizes of the crypto and private key. The name is the size
|
||||
in bits, while the value is the size in bytes. */
|
||||
typedef enum KeySize {
|
||||
UNKNOWN_KEY_SIZE = 0,
|
||||
KEY_SIZE_128 = 16,
|
||||
KEY_SIZE_160 = 20,
|
||||
KEY_SIZE_256 = 32,
|
||||
KEY_SIZE_384 = 48,
|
||||
KEY_SIZE_512 = 64,
|
||||
KEY_SIZE_1024 = 128,
|
||||
KEY_SIZE_2048 = 256,
|
||||
KEY_SIZE_3072 = 384,
|
||||
KEY_SIZE_4096 = 512,
|
||||
} KeySize;
|
||||
|
||||
/* Code often needs to convert an arbitrary size to a key size. This function
|
||||
provides a quick way to do the conversion if it is valid. Returns
|
||||
UNKNOWN_KEY_SIZE for invalid sizes. */
|
||||
static inline KeySize OPK_LengthToKeySize(size_t val) {
|
||||
switch (val) {
|
||||
case KEY_SIZE_128:
|
||||
return KEY_SIZE_128;
|
||||
case KEY_SIZE_160:
|
||||
return KEY_SIZE_160;
|
||||
case KEY_SIZE_256:
|
||||
return KEY_SIZE_256;
|
||||
case KEY_SIZE_384:
|
||||
return KEY_SIZE_384;
|
||||
case KEY_SIZE_512:
|
||||
return KEY_SIZE_512;
|
||||
case KEY_SIZE_1024:
|
||||
return KEY_SIZE_1024;
|
||||
case KEY_SIZE_2048:
|
||||
return KEY_SIZE_2048;
|
||||
case KEY_SIZE_3072:
|
||||
return KEY_SIZE_3072;
|
||||
case KEY_SIZE_4096:
|
||||
return KEY_SIZE_4096;
|
||||
default:
|
||||
return UNKNOWN_KEY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/180733509): We assume this is a packed structure.
|
||||
/* This is the format of a Widevine keybox. */
|
||||
typedef struct WidevineKeybox { /* 128 bytes total. */
|
||||
/* C character array identifying the device. Padded with NULL to fit array. */
|
||||
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;
|
||||
|
||||
/* Key Control Block Bit Masks: */
|
||||
#define CONTROL_OBSERVE_DATA_PATH (1 << 31)
|
||||
#define CONTROL_OBSERVE_HDCP (1 << 30)
|
||||
#define CONTROL_OBSERVE_CGMS (1 << 29)
|
||||
#define CONTROL_REQUIRE_ANTI_ROLLBACK_HARDWARE (1 << 28)
|
||||
#define CONTROL_ALLOW_HASH_VERIFICATION (1 << 24)
|
||||
#define SHARED_LICENSE (1 << 23)
|
||||
#define CONTROL_SRM_VERSION_REQUIRED (1 << 22)
|
||||
#define CONTROL_DISABLE_ANALOG_OUTPUT (1 << 21)
|
||||
#define CONTROL_SECURITY_PATCH_LEVEL_SHIFT 15
|
||||
#define CONTROL_SECURITY_PATCH_LEVEL_MASK \
|
||||
(0x3F << CONTROL_SECURITY_PATCH_LEVEL_SHIFT)
|
||||
#define CONTROL_REPLAY_MASK (0x03 << 13)
|
||||
#define CONTROL_NONCE_REQUIRED (0x01 << 13)
|
||||
#define CONTROL_NONCE_OR_ENTRY (0x02 << 13)
|
||||
#define CONTROL_HDCP_VERSION_SHIFT 9
|
||||
#define CONTROL_HDCP_VERSION_MASK (0x0F << CONTROL_HDCP_VERSION_SHIFT)
|
||||
#define CONTROL_ALLOW_ENCRYPT (1 << 8)
|
||||
#define CONTROL_ALLOW_DECRYPT (1 << 7)
|
||||
#define CONTROL_ALLOW_SIGN (1 << 6)
|
||||
#define CONTROL_ALLOW_VERIFY (1 << 5)
|
||||
#define CONTROL_DATA_PATH_SECURE (1 << 4)
|
||||
#define CONTROL_NONCE_ENABLED (1 << 3)
|
||||
#define CONTROL_HDCP_REQUIRED (1 << 2)
|
||||
#define CONTROL_CGMS_MASK 0x03
|
||||
#define CONTROL_CGMS_COPY_FREELY 0x00
|
||||
#define CONTROL_CGMS_COPY_ONCE 0x02
|
||||
#define CONTROL_CGMS_COPY_NEVER 0x03
|
||||
|
||||
/* Various constants and sizes */
|
||||
#define AES_BLOCK_SIZE 16
|
||||
#define KEY_CONTROL_SIZE 16
|
||||
#define KEY_ID_MAX_SIZE 16
|
||||
#define KEY_IV_SIZE 16
|
||||
#define KEY_PAD_SIZE 16
|
||||
#define KEY_SIZE 16
|
||||
#define MAC_KEY_SIZE 32
|
||||
#define KEYBOX_KEY_DATA_SIZE 72
|
||||
#define KEYBOX_DEVICE_ID_SIZE 32
|
||||
#define SRM_REQUIREMENT_SIZE 12
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#define SHA512_DIGEST_LENGTH 64
|
||||
/* TODO(b/145026434): The value of PKCS8_RSA_KEY_MAX_SIZE is purely from testing
|
||||
with openssl and is not derived from any specification. Generating 3072-bit
|
||||
PKCS8 RSA keys gave me keys of a max size of 1794 bytes so I added a bit of
|
||||
padding to make sure it's okay. This is NOT guaranteed to work for all DRM
|
||||
keys. */
|
||||
#define PKCS8_RSA_KEY_MAX_SIZE 2000
|
||||
/* Similar to RSA key size, this is from testing with OpenSSL/BoringSSL
|
||||
* serializing of a secp521r1 (largest of the supported curves) private
|
||||
* key. The encoding of an ECC key is ASN.1 encoded PKCS8 PrivateKeyInfo
|
||||
* containing ECPrivateKey structure defined in RFC5915. Only named curves
|
||||
* are supported, limiting the size of the key message. */
|
||||
#define PKCS8_ECC_KEY_MAX_SIZE 250
|
||||
|
||||
/* PKCS8_DRM_KEY_MAX_SIZE must be the larger of the two. */
|
||||
#if PKCS8_RSA_KEY_MAX_SIZE >= PKCS8_ECC_KEY_MAX_SIZE
|
||||
# define PKCS8_DRM_KEY_MAX_SIZE PKCS8_RSA_KEY_MAX_SIZE
|
||||
#else
|
||||
# define PKCS8_DRM_KEY_MAX_SIZE PKCS8_ECC_KEY_MAX_SIZE
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_ */
|
||||
16
oemcrypto/opk/oemcrypto_ta/oemcrypto_math.h
Normal file
16
oemcrypto/opk/oemcrypto_ta/oemcrypto_math.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Primary
|
||||
// License Agreement.
|
||||
|
||||
#ifndef OEMCRYPTO_MATH_H_
|
||||
#define OEMCRYPTO_MATH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint32_t OPK_MinU32(uint32_t a, uint32_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
static inline size_t OPK_MinUX(size_t a, size_t b) { return (a < b) ? a : b; }
|
||||
|
||||
#endif // OEMCRYPTO_MATH_H_
|
||||
93
oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.c
Normal file
93
oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_object_table.h"
|
||||
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
static void* UnsafeGetElem(OPKI_ObjectTable* table, uint32_t index) {
|
||||
return (char*)table->elems + index * table->elem_size;
|
||||
}
|
||||
|
||||
void* OPKI_AllocFromObjectTable(OPKI_ObjectTable* table, uint32_t* index) {
|
||||
if (!table) return NULL;
|
||||
if (table->next_free[0] == 0) {
|
||||
// This should be impossible, so this means we aren't initialized yet (since
|
||||
// the array should be filled with 0 initially).
|
||||
for (uint32_t i = 0; i < table->capacity; i++) {
|
||||
table->next_free[i] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t new_index = table->first_free;
|
||||
if (new_index == table->capacity) return NULL;
|
||||
if (index) *index = new_index;
|
||||
table->first_free = table->next_free[new_index];
|
||||
ABORT_IF(table->is_used[new_index], "Inconsistent free list");
|
||||
table->is_used[new_index] = true;
|
||||
return UnsafeGetElem(table, new_index);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeFromObjectTable(OPKI_ObjectTable* table, void* elem) {
|
||||
if (!table) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if (!elem) return OEMCrypto_SUCCESS;
|
||||
|
||||
const intptr_t diff = (intptr_t)elem - (intptr_t)table->elems;
|
||||
if (diff < 0 || diff % table->elem_size != 0) {
|
||||
LOGE("Attempt to free object not in table");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const uint32_t index = (uint32_t)(diff / table->elem_size);
|
||||
return OPKI_FreeFromObjectTableByIndex(table, index);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeFromObjectTableByIndex(OPKI_ObjectTable* table,
|
||||
uint32_t index) {
|
||||
if (!table) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if (index >= table->capacity) {
|
||||
LOGE("Attempt to free object not in table");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!table->is_used[index]) {
|
||||
LOGE("Attempt to free object that isn't being used");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
if (table->dtor) {
|
||||
OEMCryptoResult result = table->dtor(UnsafeGetElem(table, index));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
}
|
||||
|
||||
table->next_free[index] = table->first_free;
|
||||
table->first_free = index;
|
||||
table->is_used[index] = false;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
void* OPKI_GetFromObjectTable(OPKI_ObjectTable* table, uint32_t index) {
|
||||
if (!table || index >= table->capacity || !table->is_used[index]) {
|
||||
return NULL;
|
||||
}
|
||||
return UnsafeGetElem(table, index);
|
||||
}
|
||||
|
||||
void OPKI_UnsafeClearObjectTable(OPKI_ObjectTable* table) {
|
||||
if (!table) return;
|
||||
|
||||
for (uint32_t i = 0; i < table->capacity; i++) {
|
||||
table->next_free[i] = i + 1;
|
||||
table->is_used[i] = false;
|
||||
}
|
||||
table->first_free = 0;
|
||||
}
|
||||
|
||||
uint32_t OPKI_GetObjectTableUseCount(OPKI_ObjectTable* table) {
|
||||
if (!table) return 0;
|
||||
uint32_t ret = 0;
|
||||
for (uint32_t index = 0; index < table->capacity; index++) {
|
||||
if (table->is_used[index]) ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
83
oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.h
Normal file
83
oemcrypto/opk/oemcrypto_ta/oemcrypto_object_table.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_OBJECT_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_OBJECT_TABLE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint32_t
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct OPKI_ObjectTable {
|
||||
uint32_t capacity;
|
||||
size_t elem_size;
|
||||
uint32_t first_free;
|
||||
// Note, the argument pointer can be a pointer to any type.
|
||||
OEMCryptoResult (*dtor)(void*);
|
||||
void* elems;
|
||||
uint32_t* next_free;
|
||||
bool* is_used;
|
||||
} OPKI_ObjectTable;
|
||||
|
||||
/**
|
||||
* Defines a static table of objects of type |type_name| that will have
|
||||
* |max_count| number of elements and be named |var_name|. This can optionally
|
||||
* pass a destructor to call when the object is freed; this can be NULL.
|
||||
*/
|
||||
#define DEFINE_OBJECT_TABLE(var_name, type_name, max_count, dtor_arg) \
|
||||
static type_name var_name##_elems[(max_count)]; \
|
||||
/* Note these arrays are initialized to 0. */ \
|
||||
static uint32_t var_name##_next_free[(max_count)]; \
|
||||
static bool var_name##_is_used[(max_count)]; \
|
||||
static OPKI_ObjectTable var_name = { \
|
||||
.capacity = (max_count), \
|
||||
.elem_size = sizeof(type_name), \
|
||||
.dtor = dtor_arg, \
|
||||
.first_free = 0, \
|
||||
.elems = var_name##_elems, \
|
||||
.next_free = var_name##_next_free, \
|
||||
.is_used = var_name##_is_used, \
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new object from the given table. Returns NULL if there are
|
||||
* no more free elements. If |index| is not null, it will be filled with the
|
||||
* index of the new object.
|
||||
*/
|
||||
void* OPKI_AllocFromObjectTable(OPKI_ObjectTable* table, uint32_t* index);
|
||||
|
||||
/**
|
||||
* Frees an object and makes it available for allocation. If the table was
|
||||
* given a destructor, this calls it. If the destructor fails, the object is
|
||||
* still allocated in the table.
|
||||
*/
|
||||
OEMCryptoResult OPKI_FreeFromObjectTable(OPKI_ObjectTable* table, void* elem);
|
||||
|
||||
/** The same as FreeFromObjectTable, but gives an index instead. */
|
||||
OEMCryptoResult OPKI_FreeFromObjectTableByIndex(OPKI_ObjectTable* table,
|
||||
uint32_t index);
|
||||
|
||||
/** Gets the object at the given index, or NULL if not valid. */
|
||||
void* OPKI_GetFromObjectTable(OPKI_ObjectTable* table, uint32_t index);
|
||||
|
||||
/**
|
||||
* Deletes all objects from the table, allowing any to be used again. This
|
||||
* does NOT invoke destructors
|
||||
*/
|
||||
void OPKI_UnsafeClearObjectTable(OPKI_ObjectTable* table);
|
||||
|
||||
/** Gets the number of objects used from the given table. */
|
||||
uint32_t OPKI_GetObjectTableUseCount(OPKI_ObjectTable* table);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // OEMCRYPTO_TA_OEMCRYPTO_OBJECT_TABLE_H_
|
||||
78
oemcrypto/opk/oemcrypto_ta/oemcrypto_output.c
Normal file
78
oemcrypto/opk/oemcrypto_ta/oemcrypto_output.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_output.h"
|
||||
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_overflow.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
OEMCryptoResult OPK_ParseDestBufferDesc(
|
||||
const OEMCrypto_DestBufferDesc* incoming, OPK_OutputBuffer* outgoing,
|
||||
size_t* offset) {
|
||||
ABORT_IF_NULL(incoming);
|
||||
ABORT_IF_NULL(outgoing);
|
||||
ABORT_IF_NULL(offset);
|
||||
|
||||
switch (incoming->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
if (incoming->buffer.clear.clear_buffer == NULL ||
|
||||
incoming->buffer.clear.clear_buffer_length == 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
outgoing->type = OPK_CLEAR_INSECURE_OUTPUT_BUFFER;
|
||||
outgoing->buffer.clear_insecure = incoming->buffer.clear.clear_buffer;
|
||||
outgoing->size = incoming->buffer.clear.clear_buffer_length;
|
||||
*offset = 0;
|
||||
return OEMCrypto_SUCCESS;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
if (incoming->buffer.secure.secure_buffer_length == 0 ||
|
||||
incoming->buffer.secure.offset >=
|
||||
incoming->buffer.secure.secure_buffer_length) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
outgoing->type = OPK_SECURE_OUTPUT_BUFFER;
|
||||
outgoing->buffer.secure = incoming->buffer.secure.secure_buffer;
|
||||
outgoing->size = incoming->buffer.secure.secure_buffer_length;
|
||||
*offset = incoming->buffer.secure.offset;
|
||||
return OEMCrypto_SUCCESS;
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPK_CheckOutputBounds(const OPK_OutputBuffer* output_buffer,
|
||||
size_t offset, size_t size) {
|
||||
ABORT_IF_NULL(output_buffer);
|
||||
ABORT_IF_ZERO(size);
|
||||
ABORT_IF(!OPK_IsOutputBufferValid(output_buffer), "Invalid output buffer.");
|
||||
|
||||
const size_t max_allowed = WTPI_MaxOutputSizeForDecrypt();
|
||||
if (max_allowed != 0 && size > max_allowed) {
|
||||
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
|
||||
}
|
||||
|
||||
size_t total_size;
|
||||
if (OPK_AddOverflowUX(size, offset, &total_size)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (total_size > output_buffer->size) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool OPK_IsOutputBufferValid(const OPK_OutputBuffer* output_buffer) {
|
||||
ABORT_IF_NULL(output_buffer);
|
||||
return output_buffer->size > 0 &&
|
||||
(output_buffer->type == OPK_SECURE_OUTPUT_BUFFER ||
|
||||
(output_buffer->type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER &&
|
||||
output_buffer->buffer.clear_insecure != NULL));
|
||||
}
|
||||
39
oemcrypto/opk/oemcrypto_ta/oemcrypto_output.h
Normal file
39
oemcrypto/opk/oemcrypto_ta/oemcrypto_output.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_OUTPUT_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_OUTPUT_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
/* Initializes a new OPK_OutputBuffer and offset from an incoming
|
||||
OEMCrypto_DestBufferDesc or returns an error if this is impossible.
|
||||
|
||||
Returns OEMCrypto_ERROR_INVALID_CONTEXT if the incoming buffer is malformed.
|
||||
Returns OEMCrypto_ERROR_NOT_IMPLEMENTED if the incoming buffer is of a type
|
||||
not supported by OPK_OutputBuffer. Returns OEMCrypto_SUCCESS otherwise.*/
|
||||
OEMCryptoResult OPK_ParseDestBufferDesc(
|
||||
const OEMCrypto_DestBufferDesc* incoming, OPK_OutputBuffer* outgoing,
|
||||
size_t* offset);
|
||||
|
||||
/* Verifies that it is safe to write |size| bytes into |output_buffer| at offset
|
||||
|offset|. This function not only checks against whether the output buffer is
|
||||
large enough but also whether the size violates any of this device's limits.
|
||||
It also uses the porting layer to confirm that the output buffer is actually
|
||||
writeable with the given offset and size.
|
||||
|
||||
Returns OEMCrypto_ERROR_INVALID_CONTEXT if the combined size and offset
|
||||
cannot fit in a size_t or if the porting layer rejects the write. Returns
|
||||
OEMCrypto_ERROR_OUTPUT_TOO_LARGE if the write would exceed one of the
|
||||
system's limits. Returns OEMCrypto_ERROR_SHORT_BUFFER if there is not enough
|
||||
space in the buffer. Returns OEMCrypto_SUCCESS otherwise.*/
|
||||
OEMCryptoResult OPK_CheckOutputBounds(const OPK_OutputBuffer* output_buffer,
|
||||
size_t offset, size_t size);
|
||||
|
||||
/* Verifies that a given OPK_OutputBuffer is well-formed. */
|
||||
bool OPK_IsOutputBufferValid(const OPK_OutputBuffer* output_buffer);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_OUTPUT_H_ */
|
||||
63
oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.c
Normal file
63
oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_overflow.h"
|
||||
|
||||
#include "stdbool.h"
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#ifndef OPK_USE_BUILTIN_OVERFLOW
|
||||
|
||||
bool OPK_SubOverflowIX(int a, int b, int* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPK_SubOverflowU32(uint32_t a, uint32_t b, uint32_t* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPK_SubOverflowU64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPK_AddOverflowUX(size_t a, size_t b, size_t* c) {
|
||||
if (SIZE_MAX - a >= b) {
|
||||
if (c) {
|
||||
*c = a + b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPK_SubOverflowUX(size_t a, size_t b, size_t* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
62
oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.h
Normal file
62
oemcrypto/opk/oemcrypto_ta/oemcrypto_overflow.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_
|
||||
|
||||
#include "stdbool.h"
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_compiler_detection.h"
|
||||
|
||||
/* Defines operators that check for overflow in arithmetic.
|
||||
Uses the standard GNU builtins if defined and custom overflow functions
|
||||
otherwise. */
|
||||
|
||||
/* TODO(b/145244569): Unify this with the interface for the odk. */
|
||||
|
||||
#if __has_builtin(__builtin_add_overflow) || COMPATIBLE_WITH_GCC(5) || \
|
||||
COMPATIBLE_WITH_CLANG(3)
|
||||
|
||||
# define OPK_USE_BUILTIN_OVERFLOW 1
|
||||
|
||||
/* The builtins are available, so wrap them and apply NO_IGNORE_RESULT. */
|
||||
NO_IGNORE_RESULT static inline bool OPK_SubOverflowIX(int a, int b, int* c) {
|
||||
return __builtin_sub_overflow(a, b, c);
|
||||
}
|
||||
|
||||
NO_IGNORE_RESULT static inline bool OPK_SubOverflowU32(uint32_t a, uint32_t b,
|
||||
uint32_t* c) {
|
||||
return __builtin_sub_overflow(a, b, c);
|
||||
}
|
||||
|
||||
NO_IGNORE_RESULT static inline bool OPK_SubOverflowU64(uint32_t a, uint32_t b,
|
||||
uint32_t* c) {
|
||||
return __builtin_sub_overflow(a, b, c);
|
||||
}
|
||||
|
||||
NO_IGNORE_RESULT static inline bool OPK_AddOverflowUX(size_t a, size_t b,
|
||||
size_t* c) {
|
||||
return __builtin_add_overflow(a, b, c);
|
||||
}
|
||||
|
||||
NO_IGNORE_RESULT static inline bool OPK_SubOverflowUX(size_t a, size_t b,
|
||||
size_t* c) {
|
||||
return __builtin_sub_overflow(a, b, c);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* The builtins are not available, so use our implementations. */
|
||||
NO_IGNORE_RESULT bool OPK_SubOverflowIX(int a, int b, int* c);
|
||||
NO_IGNORE_RESULT bool OPK_SubOverflowU32(uint32_t a, uint32_t b, uint32_t* c);
|
||||
NO_IGNORE_RESULT bool OPK_SubOverflowU64(uint64_t a, uint64_t b, uint64_t* c);
|
||||
NO_IGNORE_RESULT bool OPK_AddOverflowUX(size_t a, size_t b, size_t* c);
|
||||
NO_IGNORE_RESULT bool OPK_SubOverflowUX(size_t a, size_t b, size_t* c);
|
||||
|
||||
#endif /* __has_builtin(__builtin_add_overflow) || COMPATIBLE_WITH_GCC(5) */
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_ */
|
||||
128
oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.c
Normal file
128
oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_serialized_usage_table.h"
|
||||
|
||||
#include "stddef.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_usage_table.h"
|
||||
|
||||
/* The buffer size we need to reserve for a signed header with the given number
|
||||
of entries.
|
||||
TODO(b/158720996): use serialization and allow variable sized headers. This
|
||||
code currently uses memcpy to serialize data. That works as long as we do not
|
||||
try to change message format or want to change the header size.
|
||||
*/
|
||||
size_t OPKI_SignedHeaderSize(int table_size UNUSED) {
|
||||
return sizeof(SignedSavedUsageHeader);
|
||||
}
|
||||
|
||||
size_t OPKI_SignedEntrySize() { return sizeof(SignedSavedUsageEntry); }
|
||||
|
||||
OEMCryptoResult OPKI_PackSignedUsageHeader(
|
||||
uint8_t* buffer, size_t buffer_size, const SignedSavedUsageHeader* header) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageHeader)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(buffer, header, sizeof(SignedSavedUsageHeader));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_PackUsageHeader(uint8_t* buffer, size_t buffer_size,
|
||||
const SavedUsageHeader* header) {
|
||||
if (buffer_size < sizeof(SavedUsageHeader)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(buffer, header, sizeof(SavedUsageHeader));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_PackSignedUsageEntry(uint8_t* buffer, size_t buffer_size,
|
||||
const SignedSavedUsageEntry* entry) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageEntry)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(buffer, entry, sizeof(SignedSavedUsageEntry));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_PackUsageEntry(uint8_t* buffer, size_t buffer_size,
|
||||
const SavedUsageEntry* entry) {
|
||||
if (buffer_size < sizeof(SavedUsageEntry)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(buffer, entry, sizeof(SavedUsageEntry));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackSavedCommonInfo(const uint8_t* buffer,
|
||||
size_t buffer_size,
|
||||
SavedCommonInfo* common_info) {
|
||||
if (buffer_size < sizeof(SavedCommonInfo)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(common_info, buffer, sizeof(SavedCommonInfo));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackSignedUsageHeader(const uint8_t* buffer,
|
||||
size_t buffer_size,
|
||||
SignedSavedUsageHeader* header) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageHeader)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(header, buffer, sizeof(SignedSavedUsageHeader));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackSignedUsageHeaderLegacy(
|
||||
const uint8_t* buffer, size_t buffer_size,
|
||||
SignedSavedUsageHeaderLegacy* header) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageHeaderLegacy)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(header, buffer, sizeof(SignedSavedUsageHeaderLegacy));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackUsageHeader(const uint8_t* buffer,
|
||||
size_t buffer_size,
|
||||
SavedUsageHeader* header) {
|
||||
if (buffer_size < sizeof(SavedUsageHeader)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(header, buffer, sizeof(SavedUsageHeader));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackSignedUsageEntry(const uint8_t* buffer,
|
||||
size_t buffer_size,
|
||||
SignedSavedUsageEntry* entry) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageEntry)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(entry, buffer, sizeof(SignedSavedUsageEntry));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackSignedUsageEntryLegacy(
|
||||
const uint8_t* buffer, size_t buffer_size,
|
||||
SignedSavedUsageEntryLegacy* entry) {
|
||||
if (buffer_size < sizeof(SignedSavedUsageEntryLegacy)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(entry, buffer, sizeof(SignedSavedUsageEntryLegacy));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_UnpackUsageEntry(const uint8_t* buffer, size_t buffer_size,
|
||||
SavedUsageEntry* entry) {
|
||||
if (buffer_size < sizeof(SavedUsageEntry)) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
memcpy(entry, buffer, sizeof(SavedUsageEntry));
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
207
oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.h
Normal file
207
oemcrypto/opk/oemcrypto_ta/oemcrypto_serialized_usage_table.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "wtpi_config_macros.h"
|
||||
|
||||
/* File types. */
|
||||
#define USAGE_TABLE_HEADER 0x68656164
|
||||
#define USAGE_TABLE_ENTRY 0x656e7472
|
||||
#define SIGNED_USAGE_TABLE_HEADER 0x48454144
|
||||
#define SIGNED_USAGE_TABLE_ENTRY 0x456e7472
|
||||
|
||||
#define MAX_PST_LENGTH 255
|
||||
|
||||
/* This can be changed to allow for newer code to load older files. */
|
||||
#define CURRENT_FILE_FORMAT_VERSION 2
|
||||
|
||||
typedef struct SavedCommonInfo {
|
||||
/* This should be one of these values: USAGE_TABLE_HEADER, USAGE_TABLE_ENTRY,
|
||||
* SIGNED_USAGE_TABLE_HEADER, SIGNED_USAGE_TABLE_ENTRY
|
||||
*/
|
||||
uint32_t file_type;
|
||||
/* Used for future backwards compatibility. */
|
||||
uint32_t format_version;
|
||||
} SavedCommonInfo;
|
||||
|
||||
/* This is the usage header, as saved to the file system, before encryption. */
|
||||
typedef struct SavedUsageHeader {
|
||||
SavedCommonInfo common_info;
|
||||
uint64_t master_generation_number;
|
||||
uint32_t table_size; /* Number of entries in the table. */
|
||||
/* These are the generation numbers of each entry. */
|
||||
uint64_t generation_numbers[MAX_NUMBER_OF_USAGE_ENTRIES];
|
||||
} SavedUsageHeader;
|
||||
|
||||
/* This is all of the data we wish to save as part of a usage table entry,
|
||||
* before encryption. */
|
||||
typedef struct SavedUsageEntry {
|
||||
SavedCommonInfo common_info;
|
||||
uint32_t index; /* Index into usage header. */
|
||||
uint64_t generation_number; /* Used to prevent rollback. */
|
||||
int64_t time_of_license_received; /* Time in seconds on system clock. */
|
||||
int64_t time_of_first_decrypt; /* Time in seconds on system clock. */
|
||||
int64_t time_of_last_decrypt; /* Time in seconds on system clock.. */
|
||||
/* Status of the entry or license, as documented in OEMCrypto spec. */
|
||||
enum OEMCrypto_Usage_Entry_Status status;
|
||||
/* Server Mac key wrapped for this device. */
|
||||
uint8_t mac_key_server[WRAPPED_MAC_KEY_SIZE];
|
||||
/* Client Mac key wrapped for this device. */
|
||||
uint8_t mac_key_client[WRAPPED_MAC_KEY_SIZE];
|
||||
/* Provider session token for this license. */
|
||||
uint8_t pst[MAX_PST_LENGTH + 1]; /* add 1 for padding. */
|
||||
uint8_t pst_length;
|
||||
} SavedUsageEntry;
|
||||
|
||||
/* TODO(b/158720996): This should be updated when we switch to using opk
|
||||
* serialization. */
|
||||
/* In order to encrypt the data, we'll copy it to a slightly larger buffer
|
||||
* that is a whole multiple of an AES block. */
|
||||
#define PADDED_HEADER_BUFFER_SIZE (16 * (1 + sizeof(SavedUsageHeader) / 16))
|
||||
#define PADDED_ENTRY_BUFFER_SIZE (16 * (1 + sizeof(SavedUsageEntry) / 16))
|
||||
|
||||
/* This is the usage header, as saved to the file system, after encryption and
|
||||
* signing. */
|
||||
typedef struct SignedSavedUsageHeader {
|
||||
SavedCommonInfo common_info;
|
||||
/* The size of the saved buffer. */
|
||||
size_t buffer_size;
|
||||
/* A SavedUsageHeader that was protected with WTPI_EncryptAndSign. */
|
||||
uint8_t buffer[PADDED_HEADER_BUFFER_SIZE + ENCRYPT_AND_SIGN_EXTRA];
|
||||
} SignedSavedUsageHeader;
|
||||
|
||||
/* This is a usage table entry, as saved to the file system, after encryption
|
||||
* and signing. */
|
||||
typedef struct SignedSavedUsageEntry {
|
||||
SavedCommonInfo common_info;
|
||||
/* The size of the saved buffer. */
|
||||
size_t buffer_size;
|
||||
/* A SavedUsageEntry that was protected with WTPI_EncryptAndSign. */
|
||||
uint8_t buffer[PADDED_ENTRY_BUFFER_SIZE + ENCRYPT_AND_SIGN_EXTRA];
|
||||
} SignedSavedUsageEntry;
|
||||
|
||||
/********** Legacy data formats **********/
|
||||
|
||||
/* This section defines the legacy data format used on the devices that
|
||||
* previously shipped with a pre-release version of OPK for the purpose of
|
||||
* backward-compatibility. The definitions below should NOT be updated since the
|
||||
* data is already in use on the devices. */
|
||||
|
||||
/* Legacy file format that pre-exists on previously shipped devices in the
|
||||
* fields. */
|
||||
#define LEGACY_FILE_FORMAT_VERSION 1
|
||||
|
||||
/* This is the usage header, as saved to the file system, before encryption. */
|
||||
typedef struct SavedUsageHeaderLegacy {
|
||||
SavedCommonInfo common_info;
|
||||
uint64_t master_generation_number;
|
||||
uint32_t table_size; /* Number of entries in the table. */
|
||||
/* These are the generation numbers of each entry. */
|
||||
uint64_t generation_numbers[MAX_NUMBER_OF_USAGE_ENTRIES];
|
||||
} SavedUsageHeaderLegacy;
|
||||
|
||||
/* This is all of the data we wish to save as part of a usage table entry,
|
||||
* before encryption. */
|
||||
typedef struct SavedUsageEntryLegacy {
|
||||
SavedCommonInfo common_info;
|
||||
uint32_t index; /* Index into usage header. */
|
||||
uint64_t generation_number; /* Used to prevent rollback. */
|
||||
int64_t time_of_license_received; /* Time in seconds on system clock. */
|
||||
int64_t time_of_first_decrypt; /* Time in seconds on system clock. */
|
||||
int64_t time_of_last_decrypt; /* Time in seconds on system clock.. */
|
||||
/* Status of the entry or license, as documented in OEMCrypto spec. */
|
||||
enum OEMCrypto_Usage_Entry_Status status;
|
||||
/* Server Mac key wrapped for this device. */
|
||||
uint8_t mac_key_server[WRAPPED_MAC_KEY_SIZE];
|
||||
/* Client Mac key wrapped for this device. */
|
||||
uint8_t mac_key_client[WRAPPED_MAC_KEY_SIZE];
|
||||
/* Provider session token for this license. */
|
||||
uint8_t pst[MAX_PST_LENGTH + 1]; /* add 1 for padding. */
|
||||
uint8_t pst_length;
|
||||
} SavedUsageEntryLegacy;
|
||||
|
||||
/* In order to encrypt the data, we'll copy it to a slightly larger buffer
|
||||
* that is a whole multiple of an AES block. */
|
||||
#define LEGACY_PADDED_HEADER_BUFFER_SIZE \
|
||||
(16 * (1 + sizeof(SavedUsageHeaderLegacy) / 16))
|
||||
#define LEGACY_PADDED_ENTRY_BUFFER_SIZE \
|
||||
(16 * (1 + sizeof(SavedUsageEntryLegacy) / 16))
|
||||
|
||||
/* This is the legacy usage header, as saved to the file system, after
|
||||
* encryption and signing, existing on the devices that previously shipped with
|
||||
* a pre-release version of OPK only. */
|
||||
typedef struct SignedSavedUsageHeaderLegacy {
|
||||
SavedCommonInfo common_info;
|
||||
/* The size of the saved buffer. At most PADDED_HEADER_BUFFER_SIZE. Must be
|
||||
* a multiple of 16, one AES block size. */
|
||||
uint32_t buffer_size;
|
||||
/* Signature of the buffer using a device unique key. */
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
/* IV used for encrypting the buffer. */
|
||||
uint8_t iv[KEY_IV_SIZE];
|
||||
/* An encrypted SavedUsageHeader. */
|
||||
uint8_t buffer[PADDED_HEADER_BUFFER_SIZE];
|
||||
} SignedSavedUsageHeaderLegacy;
|
||||
|
||||
/* This is a legacy usage table entry, as saved to the file system, after
|
||||
* encryption and signing, existing on the devices that previously shipped with
|
||||
* a pre-release version of OPK only. */
|
||||
typedef struct SignedSavedUsageEntryLegacy {
|
||||
SavedCommonInfo common_info;
|
||||
/* The size of the saved buffer. At most PADDED_HEADER_BUFFER_SIZE. Must be
|
||||
* a multiple of 16, one AES block size. */
|
||||
uint32_t buffer_size;
|
||||
/* Signature of the buffer using a device unique key. */
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
/* IV used for encrypting the buffer. */
|
||||
uint8_t iv[KEY_IV_SIZE];
|
||||
uint8_t buffer[PADDED_ENTRY_BUFFER_SIZE]; /* An encrypted SavedUsageEntry */
|
||||
} SignedSavedUsageEntryLegacy;
|
||||
|
||||
/********** End legacy data formats **********/
|
||||
|
||||
/* TODO(b/158720996): use serialization to turn these structures into messages
|
||||
* for saving. */
|
||||
/* Size of a serialized SignedSavedUsageHeader with the specified table size. */
|
||||
NO_IGNORE_RESULT size_t OPKI_SignedHeaderSize(int table_size);
|
||||
/* Size of a serialized SignedSavedUsageEntry. */
|
||||
NO_IGNORE_RESULT size_t OPKI_SignedEntrySize(void);
|
||||
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_PackSignedUsageHeader(
|
||||
uint8_t* buffer, size_t buffer_size, const SignedSavedUsageHeader* header);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_PackUsageHeader(
|
||||
uint8_t* buffer, size_t buffer_size, const SavedUsageHeader* header);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_PackSignedUsageEntry(
|
||||
uint8_t* buffer, size_t buffer_size, const SignedSavedUsageEntry* entry);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_PackUsageEntry(
|
||||
uint8_t* buffer, size_t buffer_size, const SavedUsageEntry* entry);
|
||||
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UnpackSignedUsageHeader(
|
||||
const uint8_t* buffer, size_t buffer_size, SignedSavedUsageHeader* header);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UnpackUsageHeader(
|
||||
const uint8_t* buffer, size_t buffer_size, SavedUsageHeader* header);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UnpackSignedUsageEntry(
|
||||
const uint8_t* buffer, size_t buffer_size, SignedSavedUsageEntry* entry);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UnpackUsageEntry(const uint8_t* buffer,
|
||||
size_t buffer_size,
|
||||
SavedUsageEntry* entry);
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UnpackSavedCommonInfo(
|
||||
const uint8_t* buffer, size_t buffer_size, SavedCommonInfo* common_info);
|
||||
|
||||
/* These functions are to handle backward-compatibility for the devices that
|
||||
* previously shipped with a pre-release version of OPK only. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_UnpackSignedUsageHeaderLegacy(const uint8_t* buffer, size_t buffer_size,
|
||||
SignedSavedUsageHeaderLegacy* header);
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_UnpackSignedUsageEntryLegacy(const uint8_t* buffer, size_t buffer_size,
|
||||
SignedSavedUsageEntryLegacy* header);
|
||||
|
||||
#endif // OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_
|
||||
1349
oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c
Normal file
1349
oemcrypto/opk/oemcrypto_ta/oemcrypto_session.c
Normal file
File diff suppressed because it is too large
Load Diff
355
oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h
Normal file
355
oemcrypto/opk/oemcrypto_ta/oemcrypto_session.h
Normal file
@@ -0,0 +1,355 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "odk_structs.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_entitled_key_session.h"
|
||||
#include "oemcrypto_key.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
#include "wtpi_crypto_asymmetric_interface.h"
|
||||
|
||||
/* The different states in which an OEMCrypto session can be in. */
|
||||
typedef enum OEMCryptoSessionState {
|
||||
SESSION_OPENED = (int)0xc159acf3,
|
||||
SESSION_PREPARING_REQUEST = (int)0x5bf5c800,
|
||||
SESSION_WAIT_FOR_PROVISIONING = (int)0xa93cc63e,
|
||||
SESSION_PROVISIONED = (int)0x95a11af7,
|
||||
SESSION_WAIT_FOR_LICENSE = (int)0x8cea3faa,
|
||||
SESSION_LICENSE_LOADED = (int)0xb813366b,
|
||||
SESSION_EXPIRED = (int)0x0c118e73,
|
||||
SESSION_PLAYING = (int)0x4895dd47,
|
||||
SESSION_USAGE_ENTRY_LOADED = (int)0xdc4483be,
|
||||
SESSION_INACTIVE = (int)0x512e593a,
|
||||
SESSION_CAST_RECEIVER = (int)0x738620df,
|
||||
SESSION_INVALID = (int)0x23a27071,
|
||||
SESSION_LOAD_OEM_RSA_KEY = (int)0x9d7cae94,
|
||||
SESSION_LOAD_DRM_RSA_KEY = (int)0xbc17f592,
|
||||
SESSION_INSTALL_OEM_PRIVATE_KEY = (int)0x902c5b0a,
|
||||
} OEMCryptoSessionState;
|
||||
|
||||
/* The API being executed for the current session. */
|
||||
typedef enum OEMCryptoSessionAPI {
|
||||
API_OPENSESSION = (int)0x45956770,
|
||||
API_CLOSESESSION = (int)0xa84dc5a7,
|
||||
API_GENERATENONCE = (int)0xb9f80df2,
|
||||
API_GENERATERSASIGNATURE = (int)0x450c7dd0,
|
||||
API_DERIVEKEYSFROMSESSIONKEY = (int)0xf29462c0,
|
||||
API_LOADKEYS = (int)0x55c0291c,
|
||||
API_LOADLICENSE = (int)0x3363f964,
|
||||
API_REFRESHKEYS = (int)0xc3f785fe,
|
||||
API_LOADENTITLEDCONTENTKEYS = (int)0x498bc417,
|
||||
API_SELECTKEY = (int)0xef2e58fb,
|
||||
API_DECRYPTCENC = (int)0xf5ad6301,
|
||||
API_GENERICENCRYPT = (int)0x3bd1f139,
|
||||
API_GENERICDECRYPT = (int)0xcfc2970a,
|
||||
API_GENERICSIGN = (int)0xb721196a,
|
||||
API_GENERICVERIFY = (int)0xe09fa38b,
|
||||
API_SETDECRYPTHASH = (int)0x427f7e9b,
|
||||
API_GETHASHERRORCODE = (int)0xde3477cc,
|
||||
API_QUERYKEYCONTROL = (int)0x7ab3659c,
|
||||
API_GENERATEDERIVEDKEYS = (int)0x59b1e187,
|
||||
API_LOADOEMPRIVATEKEY = (int)0x90b86535,
|
||||
API_PREPANDSIGN_LICENSE_REQUEST = (int)0x9e07085c,
|
||||
API_PREPANDSIGN_PROVISION_REQUEST = (int)0xd6247d24,
|
||||
API_PREPANDSIGN_RENEWAL_REQUEST = (int)0x7b55062c,
|
||||
API_LOADPROVISIONING = (int)0xe8c746c9,
|
||||
API_LOADDRMPRIVATEKEY = (int)0x573c7779,
|
||||
API_CREATENEWUSAGEENTRY = (int)0x1e328c13,
|
||||
API_REUSEUSAGEENTRY = (int)0x1a35e802,
|
||||
API_LOADUSAGEENTRY = (int)0x219c4914,
|
||||
API_UPDATEUSAGEENTRY = (int)0x4505ff72,
|
||||
API_DEACTIVATEUSAGEENTRY = (int)0x3a82ec1b,
|
||||
API_MOVEENTRY = (int)0xd7e8e3bd,
|
||||
API_REPORTUSAGE = (int)0x5478e587,
|
||||
API_LOADRENEWAL = (int)0xb096dc9a,
|
||||
API_CREATEENTITLEDKEYSESSION = (int)0x6d7d9bfc,
|
||||
API_INSTALLOEMPRIVATEKEY = (int)0xd6195c63,
|
||||
API_GENERATECERTIFICATEKEYPAIR = (int)0x30871a8a,
|
||||
} OEMCryptoSessionAPI;
|
||||
|
||||
typedef enum CertSignatureType {
|
||||
CERT_SIGNATURE_OEM = (int)0x386eebf3,
|
||||
CERT_SIGNATURE_DRM = (int)0x6b4684e3,
|
||||
} CertSignatureType;
|
||||
|
||||
typedef struct OEMCryptoSession {
|
||||
OEMCrypto_SESSION session_id;
|
||||
OEMCryptoSessionState state;
|
||||
AsymmetricKey* drm_private_key;
|
||||
/* This key can only be used to sign the generated cert key in
|
||||
* provisioning 4.*/
|
||||
AsymmetricKey* prov40_oem_private_key;
|
||||
SymmetricKey* mac_key_server;
|
||||
SymmetricKey* mac_key_client;
|
||||
SymmetricKey* encryption_key;
|
||||
bool refresh_valid;
|
||||
OEMCrypto_LicenseType license_type;
|
||||
uint32_t current_content_key_index;
|
||||
SymmetricKey* content_keys[CONTENT_KEYS_PER_SESSION];
|
||||
uint32_t num_content_keys;
|
||||
SymmetricKey* entitlement_keys[ENTITLEMENT_KEYS_PER_SESSION];
|
||||
uint32_t num_entitlement_keys;
|
||||
bool valid_srm_version;
|
||||
uint64_t timer_start;
|
||||
uint32_t allowed_schemes; /* For RSA signatures. */
|
||||
uint32_t prov40_oem_allowed_schemes; /* For RSA signatures. */
|
||||
bool decrypt_started; /* If the license has been used in this session. */
|
||||
ODK_NonceValues nonce_values;
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
uint8_t license_request_hash[ODK_SHA256_HASH_SIZE];
|
||||
/* |decrypt_hash| is only used by hash validation for content key session. */
|
||||
DecryptHash decrypt_hash;
|
||||
/* If |recent_decrypt| is true, then a usage report cannot be generated
|
||||
* without first updating the usage entry. It should be set to true whenever a
|
||||
* key is used. */
|
||||
bool recent_decrypt;
|
||||
/* The bare minimum state check to double-confirm that at most one call of
|
||||
* each of these function categories can be made during the lifetime of a
|
||||
* session. This is also enforced by the OPK session state machine. */
|
||||
bool nonce_created;
|
||||
bool request_signed;
|
||||
bool response_loaded;
|
||||
} OEMCryptoSession;
|
||||
|
||||
/* Initializes session context.
|
||||
Returns the result of initializing session values by ODK.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_InitializeSession(OEMCryptoSession* session, OEMCrypto_SESSION session_id);
|
||||
|
||||
/* Cleans up a session declaration by freeing any used keys and clearing any
|
||||
state so the session could be reused in a future OpenSession call.
|
||||
Returns the result of freeing the keys in the session.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_TerminateSession(OEMCryptoSession* session);
|
||||
|
||||
/* Asserts that the session state in |session| is set to the appropriate value
|
||||
that is needed to execute |api|.
|
||||
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE on failure, and OEMCrypto_SUCCESS on
|
||||
success.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_CheckStatePreCall(OEMCryptoSession* session, OEMCryptoSessionAPI api);
|
||||
|
||||
/* Sets the session state in |session| to the appropriate state for having just
|
||||
executed |api|.
|
||||
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE on failure, and OEMCrypto_SUCCESS on
|
||||
success.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_SetStatePostCall(OEMCryptoSession* session, OEMCryptoSessionAPI api);
|
||||
|
||||
/* Sets the nonce values in |session| to the value of |nonce|.
|
||||
Returns the result of setting nonce values by ODK, or
|
||||
OEMCrypto_ERROR_INVALID_CONTEXT on failure. Caller retains ownership of
|
||||
|session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_SetNonce(OEMCryptoSession* session,
|
||||
uint32_t nonce);
|
||||
|
||||
/* Loads the test RSA key into the given session. */
|
||||
OEMCryptoResult OPKI_LoadTestRSAKey(OEMCryptoSession* session);
|
||||
|
||||
/* Attempts to load the wrapped DRM private key |wrapped_key| into |session|'s
|
||||
|drm_private_key| field. |key_type| must be valid. |allowed_schemes| is only
|
||||
assigned for key types which support it.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_LoadDRMKey(OEMCryptoSession* session,
|
||||
AsymmetricKeyType key_type,
|
||||
const uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length,
|
||||
size_t key_size,
|
||||
uint32_t allowed_schemes);
|
||||
|
||||
/* Attempts to load the wrapped OEM private key |wrapped_key| into |session|'s
|
||||
|prov40_oem_private_key| field. |key_type| must be valid. |allowed_schemes|
|
||||
is only assigned for key types which support it.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_LoadProv40OEMKey(OEMCryptoSession* session, AsymmetricKeyType key_type,
|
||||
const uint8_t* wrapped_key, size_t wrapped_key_length,
|
||||
size_t key_size, uint32_t allowed_schemes);
|
||||
|
||||
/* Derives mac and encryption keys from the specific key. Uses AES-128-CMAC
|
||||
and |mac_ and enc_key_contexts| to derive and create a mac_key_server,
|
||||
mac_key_client, and encryption_key for the |session|.
|
||||
Returns the result of the derivation if it fails or the result of key
|
||||
creation.
|
||||
All lengths must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_DeriveMacAndEncryptionKeys(
|
||||
OEMCryptoSession* session, WTPI_K1_SymmetricKey_Handle master_key,
|
||||
const uint8_t* mac_key_context, size_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context, size_t enc_key_context_length);
|
||||
|
||||
/* Verifies |signature| of |message| with the mac_key_server stored in
|
||||
|session|. Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if the mac_key_server is
|
||||
invalid, the result of signature calculation if it fails,
|
||||
OEMCrypto_ERROR_SIGNATURE_FAILURE if the calculated and provided signatures
|
||||
don't match, and OEMCrypto_SUCCESS otherwise.
|
||||
|message_length| must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_VerifySignatureWithMacKeyServer(
|
||||
OEMCryptoSession* session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature);
|
||||
|
||||
/* Generates signature of |message| with the mac_key_client stored in
|
||||
|session| and places it in |signature|. Returns
|
||||
OEMCrypto_ERROR_UNKNOWN_FAILURE if the mac_key_client is invalid, the result
|
||||
of signature calculation if it fails, OEMCrypto_ERROR_SHORT_BUFFER if the
|
||||
provided |signature_length| < 32 bytes, and OEMCrypto_SUCCESS otherwise.
|
||||
|message_length| must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GenerateSignatureWithMacKeyClient(
|
||||
OEMCryptoSession* session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
/* Generates signature of |message| with the private key of OEM/DRM cert
|
||||
specified by |signature_type|, and places it in |signature|.
|
||||
Returns:
|
||||
OEMCrypto_SUCCESS if the message was signed by OEM/DRM cert key.
|
||||
OEMCrypto_ERROR_INVALID_KEY if the DRM key is RSA and the padding scheme
|
||||
is not 0x1 (RSASSA-PSS with SHA1) or if the DRM key is otherwise invalid
|
||||
OEMCrypto_ERROR_UNKNOWN_FAILURE if the result of RSA or ECC sign fails
|
||||
|message_length| must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GenerateCertSignature(
|
||||
OEMCryptoSession* session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
CertSignatureType signature_type);
|
||||
|
||||
/* Installs the key using the given data into the |session| depending on the
|
||||
license_type. Decrypts the |key_data| using the encryption_key in the session
|
||||
and then uses the first 128 bits of the decrypted key to decrypt
|
||||
|key_control|. Returns OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if the session
|
||||
cannot hold any more keys, OEMCrypto_ERROR_INVALID_CONTEXT if the key control
|
||||
block is invalid, OEMCrypto_ERROR_INVALID_NONCE if nonce is required and the
|
||||
provided nonce is not in the session's nonce table,
|
||||
OEMCrypto_ERROR_UNKNOWN FAILURE for all other failures, and OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
All lengths must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_InstallKey(
|
||||
OEMCryptoSession* session, const uint8_t* key_id, size_t key_id_length,
|
||||
const uint8_t* key_data, size_t key_data_length, const uint8_t* key_data_iv,
|
||||
const uint8_t* key_control, const uint8_t* key_control_iv);
|
||||
|
||||
/* Updates the mac_key_server and mac_key_client in |session|. Decrypts
|
||||
|enc_mac_keys| using the encryption_key and splits them into the two mac
|
||||
keys. Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if the encryption_key is not
|
||||
valid and the result of creating the mac keys otherwise.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UpdateMacKeys(OEMCryptoSession* session,
|
||||
const uint8_t* enc_mac_keys,
|
||||
const uint8_t* mac_keys_iv);
|
||||
|
||||
/* Given a |session| and either a raw or encrypted |key_control|, refreshes
|
||||
either the key with the matching |key_id| or all keys of the current license
|
||||
type. If |key_id| or |key_control_iv| are NULL, the key control is assumed to
|
||||
be unencrypted. If |key_id| is NULL, all keys of the current license type
|
||||
will have their durations updated.
|
||||
Returns OEMCrypto_ERROR_INVALID_CONTEXT if the key control is invalid,
|
||||
OEMCrypto_ERROR_INVALID_NONCE if there's no matching nonce,
|
||||
OEMCrypto_ERROR_NO_CONTENT_KEY if there is no key with the same key id,
|
||||
OEMCrypto_ERROR_UNKNOWN_FAILURE for all other failures, and OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
Caller retains ownership of all pointers, and |session| and |key_control|
|
||||
must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_RefreshKey(OEMCryptoSession* session,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length,
|
||||
const uint8_t* key_control,
|
||||
const uint8_t* key_control_iv);
|
||||
|
||||
/* Gets the current content key either from the |session|, or from the entitled
|
||||
key session |key_session| depending on the current session type, and places
|
||||
the key in |key|.
|
||||
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no valid current content
|
||||
key, OEMCrypto_ERROR_INVALID_CONTEXT if |session| or |key| is NULL, or if the
|
||||
license type doesn't match the one in |key_session|. Returns
|
||||
OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |session| and |key| and they must not be NULL.
|
||||
Caller retains ownership of |key_session| when it is not NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GetCurrentContentKey(
|
||||
OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session,
|
||||
SymmetricKey** key);
|
||||
|
||||
/* Gets the entitlement key by |key_id| and |key_id_length| from the entitlement
|
||||
|session|, and places the key in |key|.
|
||||
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any pointer is NULL, if
|
||||
|key_id_size| is zero, or if the license type is not
|
||||
OEMCrypto_EntitlementLicense. OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no
|
||||
entitlement key found. Returns OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of all parameters and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_GetEntitlementKey(OEMCryptoSession* session, const uint8_t* key_id,
|
||||
size_t key_id_size, SymmetricKey** key);
|
||||
|
||||
/* Gets the key control block of the |content_key| and places the result in
|
||||
|control_block|.
|
||||
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no valid entitlement key
|
||||
for the entitled content key, OEMCrypto_ERROR_INVALID_CONTEXT if
|
||||
|content_key|, |session| or |control_block| is NULL, or if the license type
|
||||
doesn't match the one in |key_session|. Returns OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |content_key|, |session| and |control_block| and
|
||||
they must not be NULL. Caller retains ownership of |key_session| when it is
|
||||
not NULL.*/
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GetKeyControlBlock(
|
||||
SymmetricKey* content_key, OEMCryptoSession* session,
|
||||
OEMCryptoEntitledKeySession* key_session, KeyControlBlock* control_block);
|
||||
|
||||
/* Checks whether the current content key selected in the |session|, or the
|
||||
current entitled content key selected in the |key_session| can be used
|
||||
in the operation given by |use_type| and with the given |buffer_type|.
|
||||
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no valid current content
|
||||
key, OEMCrypto_ERROR_INVALID_CONTEXT if |use_type| is not allowed by the
|
||||
key control block or if the replay mask is set in the key control block, or
|
||||
if the license type doesn't match the one in |key_session|. Returns
|
||||
OEMCrypto_DECRYPT_FAILED if the |buffer_type| is not allowed on the device,
|
||||
OEMCrypto_ERROR_KEY_EXPIRED if the duration in the key control block has
|
||||
passed, OEMCrypto_ERROR_INSUFFICIENT_HDCP if the HDCP requirements are not
|
||||
met, OEMCrypto_ERROR_ANALOG_OUTPUT if the analog display requirements are not
|
||||
met, and OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |session| and it must not be NULL. Caller retains
|
||||
ownership of |key_session| when it is not NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_CheckCurrentContentKeyUsage(
|
||||
OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session,
|
||||
uint32_t use_type, OPK_OutputBuffer_Type buffer_type);
|
||||
|
||||
/* Updates clock and timer values for the first time playback or during a
|
||||
continued playback. Updates usage entry status if necessary. Returns
|
||||
OEMCrypto_ERROR_KEY_EXPIRED if ODK timer expires or usage entry is
|
||||
deactivated, OEMCrypto_ERROR_UNKNOWN_FAILURE if the usage entry status is
|
||||
invalid or if the system clock is unavailable,
|
||||
OEMCrypto_ERROR_INVALID_CONTEXT if the ODK timer or clock is invalid, and
|
||||
OEMCrypto_SUCCESS otherwise. Caller retains ownership of |session| and it
|
||||
must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_UpdatePlaybackTimeAndUsageEntryStatus(OEMCryptoSession* session);
|
||||
|
||||
/* Decrypts a number of |samples_length| samples from given |samples| with the
|
||||
specified |pattern| using either the |session|'s current content key, or the
|
||||
|key_session|'s current entitled content key, depending on the license type.
|
||||
Returns the result of DecryptOneSample if failing to decrypt any sample,
|
||||
OEMCrypto_ERROR_INVALID_CONTEXT if the license type doesn't match the one in
|
||||
|key_session|. Returns OEMCrypto_SUCCESS otherwise. Lengths must not be 0.
|
||||
Caller retains ownership of |key_session| when it is not NULL. Caller retains
|
||||
ownership of all other parameters and they must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_DecryptSamples(
|
||||
OEMCryptoSession* session, OEMCryptoEntitledKeySession* key_session,
|
||||
const OEMCrypto_SampleDescription* samples, size_t samples_length,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
/* Gets the ODK nonce values for the given |session|. All parameters must not be
|
||||
NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GetNonceValues(
|
||||
const OEMCryptoSession* session, ODK_NonceValues* nonce_values);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_ */
|
||||
57
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.c
Normal file
57
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "oemcrypto_session_key_table.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_util.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
NO_IGNORE_RESULT static SymmetricKey** get_key_table(
|
||||
OEMCryptoSession* session, OEMCrypto_LicenseType license_type,
|
||||
uint32_t* num_keys) {
|
||||
if (session == NULL || num_keys == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (license_type) {
|
||||
case OEMCrypto_ContentLicense:
|
||||
*num_keys = session->num_content_keys;
|
||||
return session->content_keys;
|
||||
case OEMCrypto_EntitlementLicense:
|
||||
*num_keys = session->num_entitlement_keys;
|
||||
return session->entitlement_keys;
|
||||
}
|
||||
|
||||
// Execution can only reach this point if license_type was not a valid member
|
||||
// of the enumeration.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SymmetricKey* OPKI_FindKeyFromTable(OEMCryptoSession* session,
|
||||
bool is_content_key, const uint8_t* key_id,
|
||||
size_t key_id_length) {
|
||||
if (session == NULL || key_id == NULL || key_id_length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (!is_content_key &&
|
||||
session->license_type != OEMCrypto_EntitlementLicense) {
|
||||
LOGE("Cannot get entitlement key for content license");
|
||||
return NULL;
|
||||
}
|
||||
uint32_t num_keys;
|
||||
OEMCrypto_LicenseType license_type =
|
||||
is_content_key ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
|
||||
SymmetricKey** key_table = get_key_table(session, license_type, &num_keys);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
SymmetricKey* key = key_table[i];
|
||||
ABORT_IF(key == NULL, "Key at index %zu is NULL", i);
|
||||
if (key_id_length == key->key_id_size &&
|
||||
crypto_memcmp(key->key_id, key_id, key_id_length) == 0) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
23
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.h
Normal file
23
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_key_table.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_
|
||||
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_key.h"
|
||||
#include "oemcrypto_session.h"
|
||||
|
||||
/* Finds the key from the key table corresponding to the given |is_content_key|
|
||||
with the given |key_id| and |key_id_length|.
|
||||
Returns either the key if there is a match or NULL otherwise.
|
||||
|key_id_length| must be > 0 and |is_content_key| can only be false if the
|
||||
session has an OEMCrypto_EntitlementLicense.
|
||||
Caller retains ownership of all parameters and they must not be NULL. */
|
||||
NO_IGNORE_RESULT SymmetricKey* OPKI_FindKeyFromTable(OEMCryptoSession* session,
|
||||
bool is_content_key,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_ */
|
||||
105
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.c
Normal file
105
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_session_table.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_key.h"
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "oemcrypto_session_type.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
static OEMCryptoResult DtorTrampoline(void* session) {
|
||||
return OPKI_TerminateSession((OEMCryptoSession*)session);
|
||||
}
|
||||
|
||||
DEFINE_OBJECT_TABLE(session_table, OEMCryptoSession, MAX_NUMBER_OF_SESSIONS,
|
||||
&DtorTrampoline);
|
||||
|
||||
void OPKI_InitializeSessionTable(void) {
|
||||
OPKI_UnsafeClearObjectTable(&session_table);
|
||||
}
|
||||
|
||||
uint32_t OPKI_MaxNumberOfSessions(void) { return session_table.capacity; }
|
||||
|
||||
OEMCryptoResult OPKI_NumberOfOpenSessions(uint32_t* num_open_sessions) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(num_open_sessions);
|
||||
*num_open_sessions = OPKI_GetObjectTableUseCount(&session_table);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_GrabSession(OEMCrypto_SESSION* session_id) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(session_id);
|
||||
/* index is the entry position in session table. */
|
||||
uint32_t index;
|
||||
OEMCryptoSession* session = OPKI_AllocFromObjectTable(&session_table, &index);
|
||||
if (!session) {
|
||||
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
|
||||
}
|
||||
/* Check if too many oemcrypto sessions have been opened. This should never
|
||||
* happen as the |index| shall never go beyond (1u << SESSION_ID_TYPE_SHIFT)
|
||||
* and use the higher bits, which is reserved for session type. */
|
||||
if ((index >> SESSION_ID_TYPE_SHIFT) != 0) {
|
||||
LOGE("Could not allocate oemcrypto session with index: %u", index);
|
||||
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
|
||||
}
|
||||
*session_id = index;
|
||||
OEMCryptoResult result =
|
||||
OPKI_ApplySessionType(session_id, SESSION_TYPE_OEMCRYPTO);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
return OPKI_InitializeSession(session, *session_id);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_GetSession(OEMCrypto_SESSION session_id,
|
||||
OEMCryptoSession** session) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(session);
|
||||
if (OPKI_GetSessionType(session_id) != SESSION_TYPE_OEMCRYPTO) {
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
uint32_t index = (session_id & SESSION_ID_MASK);
|
||||
*session = OPKI_GetFromObjectTable(&session_table, index);
|
||||
if (!*session) {
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_FreeSession(OEMCrypto_SESSION session_id) {
|
||||
if (OPKI_GetSessionType(session_id) != SESSION_TYPE_OEMCRYPTO) {
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
uint32_t index = (session_id & SESSION_ID_MASK);
|
||||
return OPKI_FreeFromObjectTableByIndex(&session_table, index);
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_TerminateSessionTable(void) {
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
for (uint32_t i = 0; i < session_table.capacity; i++) {
|
||||
OEMCryptoSession* session = OPKI_GetFromObjectTable(&session_table, i);
|
||||
if (session) {
|
||||
result = OEMCrypto_ERROR_TERMINATE_FAILED;
|
||||
/* Attempt to free the session. */
|
||||
OEMCryptoResult free_result =
|
||||
OPKI_FreeFromObjectTableByIndex(&session_table, i);
|
||||
if (free_result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could not free session %u with error: %u", i, free_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool OPKI_NonceCollision(uint32_t nonce) {
|
||||
for (uint32_t i = 0; i < session_table.capacity; i++) {
|
||||
OEMCryptoSession* session = OPKI_GetFromObjectTable(&session_table, i);
|
||||
if (session && session->nonce_values.nonce == nonce) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
59
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.h
Normal file
59
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_table.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_session.h"
|
||||
#include "wtpi_config_interface.h"
|
||||
|
||||
/* Initializes the session table for future OpenSession calls. */
|
||||
void OPKI_InitializeSessionTable(void);
|
||||
|
||||
/* Gets the max number of sessions. */
|
||||
NO_IGNORE_RESULT uint32_t OPKI_MaxNumberOfSessions(void);
|
||||
|
||||
/* Gets the number of open sessions. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED
|
||||
if the session table has not been initialized and OEMCrypto_SUCCESS
|
||||
otherwise.
|
||||
Caller retains ownership of |num_open_sessions| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_NumberOfOpenSessions(uint32_t* num_open_sessions);
|
||||
|
||||
/* Attempts to grab an open entry in the session table and set |session_id|.
|
||||
Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session table has not been
|
||||
initialized and OEMCrypto_ERROR_TOO_MANY_SESSIONS if there are no sessions
|
||||
left to grab. Returns OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |session_id| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_GrabSession(OEMCrypto_SESSION* session_id);
|
||||
|
||||
/* Sets *|session| to the session specified by |session_id| in the session table
|
||||
if it is free. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session
|
||||
table has not been initialized and OEMCrypto_ERROR_INVALID_SESSION if the
|
||||
session has not been grabbed or if the |session_id| is invalid. Returns
|
||||
OEMCrypto_SUCCESS otherwise.
|
||||
Caller retains ownership of |session| and it must not be NULL. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_GetSession(OEMCrypto_SESSION session_id,
|
||||
OEMCryptoSession** session);
|
||||
|
||||
/* Given a non-free session |session_id|, attempts to free it so it can be
|
||||
reused. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session table has
|
||||
not been initialized and OEMCrypto_ERROR_INVALID_SESSION if the session has
|
||||
not been grabbed or if the |session_id| is invalid. Returns OEMCrypto_SUCCESS
|
||||
otherwise. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_FreeSession(OEMCrypto_SESSION session_id);
|
||||
|
||||
/* Clears and cleans up the session table. The session table must be
|
||||
reinitialized to be used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if the
|
||||
table has not been initialized or if there are any active sessions still.
|
||||
Returns OEMCrypto_SUCCESS otherwise. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_TerminateSessionTable(void);
|
||||
|
||||
/* Verify that the nonce is not the same as any in the session table. */
|
||||
NO_IGNORE_RESULT bool OPKI_NonceCollision(uint32_t nonce);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_ */
|
||||
26
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.c
Normal file
26
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.c
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "oemcrypto_session_type.h"
|
||||
|
||||
OEMCryptoSessionType OPKI_GetSessionType(OEMCrypto_SESSION session_id) {
|
||||
const uint32_t type_bits = (session_id >> SESSION_ID_TYPE_SHIFT);
|
||||
switch (type_bits) {
|
||||
case 0:
|
||||
return SESSION_TYPE_OEMCRYPTO;
|
||||
case 1:
|
||||
return SESSION_TYPE_ENTITLED_KEY;
|
||||
}
|
||||
return SESSION_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_ApplySessionType(OEMCrypto_SESSION* session_id,
|
||||
OEMCryptoSessionType type) {
|
||||
if (session_id == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
*session_id = ((uint32_t)type << SESSION_ID_TYPE_SHIFT) |
|
||||
((*session_id) & SESSION_ID_MASK);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
34
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.h
Normal file
34
oemcrypto/opk/oemcrypto_ta/oemcrypto_session_type.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_TYPE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_TYPE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
|
||||
typedef enum OEMCryptoSessionType {
|
||||
SESSION_TYPE_OEMCRYPTO = 0,
|
||||
SESSION_TYPE_ENTITLED_KEY = 1,
|
||||
SESSION_TYPE_UNKNOWN = 2,
|
||||
} OEMCryptoSessionType;
|
||||
|
||||
/* The lower SESSION_ID_TYPE_SHIFT bits of a Session ID is the actual session id
|
||||
* value. The rest higher bits are reserved for session type. */
|
||||
#define SESSION_ID_TYPE_SHIFT 28
|
||||
#define SESSION_ID_MASK ((1u << SESSION_ID_TYPE_SHIFT) - 1u)
|
||||
|
||||
/* Gets the session type from |session_id|. */
|
||||
NO_IGNORE_RESULT OEMCryptoSessionType
|
||||
OPKI_GetSessionType(OEMCrypto_SESSION session_id);
|
||||
|
||||
/* Sets session type in the higher bits of |session_id|.
|
||||
* Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or
|
||||
* invalid, and OEMCrypto_SUCCESS otherwise.
|
||||
* Caller retains ownership of all parameters.
|
||||
*/
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_ApplySessionType(OEMCrypto_SESSION* session_id, OEMCryptoSessionType type);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_TYPE_H_ */
|
||||
56
oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.gyp
Normal file
56
oemcrypto/opk/oemcrypto_ta/oemcrypto_ta.gyp
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright 2019 Google LLC.All Rights Reserved.This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine
|
||||
# License Agreement.
|
||||
|
||||
{
|
||||
'variables': {
|
||||
# Include directory that contains wtpi_config_macros.h.
|
||||
'config_macros_header_dir%': 'wtpi_reference',
|
||||
},
|
||||
'includes': [
|
||||
'../strict_compiler_flags.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'oemcrypto_ta',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'include_dirs': [
|
||||
'.',
|
||||
'../../include',
|
||||
'wtpi',
|
||||
'<(config_macros_header_dir)',
|
||||
],
|
||||
'sources': [
|
||||
'oemcrypto.c',
|
||||
'oemcrypto_asymmetric_key_table.c',
|
||||
'oemcrypto_entitled_key_session.c',
|
||||
'oemcrypto_entitled_key_session_table.c',
|
||||
'oemcrypto_key.c',
|
||||
'oemcrypto_key_control_block.c',
|
||||
'oemcrypto_key_table.c',
|
||||
'oemcrypto_object_table.c',
|
||||
'oemcrypto_output.c',
|
||||
'oemcrypto_overflow.c',
|
||||
'oemcrypto_serialized_usage_table.c',
|
||||
'oemcrypto_session.c',
|
||||
'oemcrypto_session_key_table.c',
|
||||
'oemcrypto_session_table.c',
|
||||
'oemcrypto_session_type.c',
|
||||
'oemcrypto_usage_table.c',
|
||||
'oemcrypto_wall_clock.c',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../odk/src/odk.gyp:odk',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'.',
|
||||
'../../include',
|
||||
'wtpi',
|
||||
'<(config_macros_header_dir)',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
1202
oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.c
Normal file
1202
oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.c
Normal file
File diff suppressed because it is too large
Load Diff
181
oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.h
Normal file
181
oemcrypto/opk/oemcrypto_ta/oemcrypto_usage_table.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_
|
||||
#define OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "odk.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
typedef enum UsageEntryStatus {
|
||||
USAGE_ENTRY_NONE = (int)0xacbad562,
|
||||
USAGE_ENTRY_NEW = (int)0x4545babc,
|
||||
USAGE_ENTRY_LOADED = (int)0x766bca12,
|
||||
USAGE_ENTRY_DEACTIVATED = (int)0xdacba37,
|
||||
} UsageEntryStatus;
|
||||
|
||||
/**
|
||||
* Clear out memory for the usage table. No other usage table functions may be
|
||||
* called before this. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_InitializeUsageTable(void);
|
||||
|
||||
/**
|
||||
* Erase data from usage table. No other usage table functions may be
|
||||
* called without calling OPKI_InitializeUsageTable. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_TerminateUsageTable(void);
|
||||
|
||||
/** Gets the usage entry status of the given session. */
|
||||
NO_IGNORE_RESULT UsageEntryStatus
|
||||
OPKI_GetUsageEntryStatus(OEMCrypto_SESSION session_id);
|
||||
|
||||
/** Create a new empty usage table header. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_CreateUsageTableHeader(
|
||||
uint8_t* header_buffer, size_t* header_buffer_length);
|
||||
|
||||
/** Load a usage table header. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length);
|
||||
|
||||
/**
|
||||
* Create a new usage table entry and attach it to the |session|. This may
|
||||
* return an error if the usage table is full, or if too many open sessions
|
||||
* have active usage entries. |session| must be open and not already have an
|
||||
* entry associated with it.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_CreateNewUsageEntry(
|
||||
OEMCrypto_SESSION session_id, uint32_t* usage_entry_number);
|
||||
|
||||
/**
|
||||
* Allows a |session| to take an existing usage entry. This may return an
|
||||
* error if the usage table is full, or if too many open sessions have active
|
||||
* usage entries. |session| must be open and not already have an entry
|
||||
* associated with it. All information related to the previous entry should be
|
||||
* cleared from the header. The new entry will be initialized with a
|
||||
* generation number equal to the master generation number, which will also be
|
||||
* stored in the header’s existing slot. Then the master generation number
|
||||
* will be incremented. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_ReuseUsageEntry(OEMCrypto_SESSION session_id, uint32_t usage_entry_number);
|
||||
|
||||
/**
|
||||
* Load a usage table entry and attach it to the |session|. This may return
|
||||
* an error if too many open sessions have active usage entries. |session|
|
||||
* must be open and not already have an entry associated with it. The
|
||||
* usage_entry_number must match that in the loaded entry.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_LoadUsageEntry(
|
||||
OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values,
|
||||
uint32_t usage_entry_number, const uint8_t* buffer, size_t buffer_length);
|
||||
|
||||
/**
|
||||
* Release the active usage entry associated with |session|.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id);
|
||||
|
||||
/**
|
||||
* Update all values in the usage entry associated with |session|. After
|
||||
* updating values, the generation numbers are all updated and the master
|
||||
* generation number is saved to persistent storage. Then the entry and the
|
||||
* usage table header are saved to the specified buffer. If the buffer lengths
|
||||
* are not large enough, none of the work above is completed -- instead the
|
||||
* lengths are updated and OEMCrypto_ERROR_SHORT_BUFFER is returned.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UpdateUsageEntry(
|
||||
OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values,
|
||||
uint8_t* header_buffer, size_t* header_buffer_length, uint8_t* entry_buffer,
|
||||
size_t* entry_buffer_length);
|
||||
|
||||
/**
|
||||
* Set the provider session token in the usage entry associated with
|
||||
* |session|. This is done when a license is first loaded.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_SetUsageEntryPST(
|
||||
OEMCrypto_SESSION session_id, const uint8_t* pst, size_t pst_length);
|
||||
|
||||
/**
|
||||
* Verify the provider session token in the usage entry associated with
|
||||
* |session|. This is done when a license is reloaded to verify the license
|
||||
* matches the usage entry.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_VerfiyUsageEntryPST(
|
||||
OEMCrypto_SESSION session_id, const uint8_t* pst, size_t pst_length);
|
||||
|
||||
/**
|
||||
* Set the mac keys in the usage entry associated with |session|.
|
||||
* This is done when a license is first loaded.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_SetUsageEntryMacKeys(
|
||||
OEMCrypto_SESSION session_id, WTPI_K1_SymmetricKey_Handle mac_key_server,
|
||||
WTPI_K1_SymmetricKey_Handle mac_key_client);
|
||||
|
||||
/**
|
||||
* Verify the mac keys in the usage entry associated with
|
||||
* |session|. This is done when a license is reloaded to verify the license
|
||||
* matches the usage entry.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_VerifyUsageEntryMacKeys(
|
||||
OEMCrypto_SESSION session_id, WTPI_K1_SymmetricKey_Handle mac_key_server,
|
||||
WTPI_K1_SymmetricKey_Handle mac_key_client);
|
||||
|
||||
/**
|
||||
* Set the recent_decrypt flag in the usage entry associated with |session|.
|
||||
* This is done when a continued playback occurs.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_SetUsageEntryRecentDecrypt(OEMCrypto_SESSION session_id);
|
||||
|
||||
/**
|
||||
* Mark the usage entry associated with |session| as deactivated. After
|
||||
* this, the license may not be used to decrypt content. Pointers must be
|
||||
* non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_DeactivateUsageEntry(
|
||||
OEMCrypto_SESSION session_id, ODK_ClockValues* clock_values,
|
||||
const uint8_t* pst, size_t pst_length);
|
||||
|
||||
/**
|
||||
* Generate a usage report from the entry associated with |session|.
|
||||
* |mac_key_client| can be null to use the stored MAC key instead;
|
||||
* otherwise, pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_ReportUsage(OEMCrypto_SESSION session_id,
|
||||
WTPI_K1_SymmetricKey_Handle mac_key_client, const uint8_t* pst,
|
||||
size_t pst_length, uint8_t* buffer, size_t* buffer_length);
|
||||
|
||||
/**
|
||||
* Mark the entry associated with |session| as modified and forbid a usage
|
||||
* report until the data has been saved. This is done on important events
|
||||
* like first decrypt and deactivation. Pointers must be non-null and are
|
||||
* owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_ForbidReportUsage(OEMCrypto_SESSION session_id);
|
||||
|
||||
/**
|
||||
* Sign |buffer| with the client mac key in the entry associated with
|
||||
* |session|. Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_SignReleaseRequest(
|
||||
OEMCrypto_SESSION session_id, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
/**
|
||||
* Move the usage entry associated with |session| to the new index in the
|
||||
* usage table header. The generation numbers are updated as specified in
|
||||
* the OEMCrypto spec. Pointers must be non-null and are owned by the
|
||||
* caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_MoveEntry(OEMCrypto_SESSION session_id,
|
||||
uint32_t new_index);
|
||||
|
||||
/**
|
||||
* Shrink the usage table to the size specified.
|
||||
* Pointers must be non-null and are owned by the caller. */
|
||||
NO_IGNORE_RESULT OEMCryptoResult
|
||||
OPKI_ShrinkUsageTableHeader(uint32_t new_entry_count, uint8_t* header_buffer,
|
||||
size_t* header_buffer_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_ */
|
||||
18
oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.c
Normal file
18
oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.c
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Primary
|
||||
// License Agreement.
|
||||
|
||||
#include "oemcrypto_wall_clock.h"
|
||||
|
||||
static uint64_t gPreviousWallClock = 0;
|
||||
|
||||
OEMCryptoResult OPK_SetWallClockTime(uint64_t time_in_s) {
|
||||
gPreviousWallClock = time_in_s;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPK_GetWallClockTime(uint64_t* time_in_s) {
|
||||
if (!time_in_s) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
*time_in_s = gPreviousWallClock;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
58
oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.h
Normal file
58
oemcrypto/opk/oemcrypto_ta/oemcrypto_wall_clock.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WALL_CLOCK_H_
|
||||
#define OEMCRYPTO_TA_WALL_CLOCK_H_
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is a buffer interface. It allows the transport layer to set the wall
|
||||
* clock whether the TA uses it or not. It also allows the TA to try to access
|
||||
* the wall clock, even if the transport layer is not providing it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the current wall clock time. Wall clock time is also called Unix time.
|
||||
*
|
||||
* The transport layer will call this function on each call from the REE to the
|
||||
* TEE in order to update the wall clock.
|
||||
*
|
||||
* @param[in] time_in_s: current wall clock time, in seconds since the Unix
|
||||
* epoch.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
*/
|
||||
OEMCryptoResult OPK_SetWallClockTime(uint64_t time_in_s);
|
||||
|
||||
/**
|
||||
* Get the most recent wall clock time. Wall clock time is also called Unix
|
||||
* time.
|
||||
*
|
||||
* The WTPI porting layer may use this to update the trusted time when the
|
||||
* hardware secure timer is not active. The porting layer may assume that this
|
||||
* is usually the correct time, but that it is not secure. This function should
|
||||
* not be used if the TrustedTime is based on a hardware-provided secure
|
||||
* wall-clock.
|
||||
*
|
||||
* @param[out] time_in_s: most recent wall clock time, in seconds since the Unix
|
||||
* epoch.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
*/
|
||||
OEMCryptoResult OPK_GetWallClockTime(uint64_t* time_in_s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WALL_CLOCK_H_ */
|
||||
37
oemcrypto/opk/oemcrypto_ta/wtpi/README.md
Normal file
37
oemcrypto/opk/oemcrypto_ta/wtpi/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# A note to Widevine Engineers
|
||||
|
||||
Some of the headers in wtpi/ directory are tested by the code in wtpi_test/.
|
||||
wtpi_test uses serialization/generator/scrape_interface.py to parse the WTPI
|
||||
interface declarations and generate serialization APIs such as:
|
||||
* OPK_Pack_SaveGenerationNumber_Request(),
|
||||
* OPK_Unpack_K1_DeriveKeyFromKeyHandle_Response(),
|
||||
* ...
|
||||
|
||||
In order for the types of the parameters of these WTPI interfaces to be
|
||||
correctly determined and inserted into the auto-generated
|
||||
OPK_Pack_* / OPK_Unpack_* functions, certain naming conventions have to be
|
||||
followed:
|
||||
|
||||
* To pack a variable length buffer X with type uint8_t*, the size of the
|
||||
array must be named as "X_length" or XLength".
|
||||
* If an output variable length buffer doesn't have an output size specified in
|
||||
the parameter list, and is supposed to have the same size as the input buffer,
|
||||
then the output buffer must be named as "out_buffer".
|
||||
|
||||
Below is an example following the naming convention above:
|
||||
```
|
||||
OEMCryptoResult WTPI_C1_SHA256(const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
```
|
||||
You can find more details in scrape_interface.py for what is looked for by the
|
||||
parser.
|
||||
|
||||
WTPI interfaces that are currently covered by wtpi_test:
|
||||
* wtpi_generation_number_interface.h,
|
||||
* wtpi_crypto_and_key_management_interface_layer1.h,
|
||||
* wtpi_crypto_asymmetric_interface.h,
|
||||
* wtpi_crc32_interface.h,
|
||||
|
||||
Please be cautious when updating parameter names in these interfaces. It can
|
||||
potentially break the auto-generated serialization functions used by the WTPI
|
||||
tests if the naming convention is not enforced.
|
||||
65
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_abort_interface.h
Normal file
65
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_abort_interface.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_ABORT_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_ABORT_INTERFACE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup abort Abort Interface
|
||||
*
|
||||
* Define an abort function that is called if there is an unrecoverable error.
|
||||
* This function should terminate the TA immediately. This function must never
|
||||
* return control-flow to the caller. If your platform has the standard `libc`
|
||||
* function `abort()`, it is recommended to just call `abort()`.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calling this function should abort the program execution. This function is
|
||||
* NOT debug-only. It should abort program execution even on release builds.
|
||||
*
|
||||
* Code should generally not call WTPI_Abort() directly. It should instead call
|
||||
* the wrapper macros ABORT() and ABORT_IF() that enforce best practices.
|
||||
*/
|
||||
void WTPI_Abort(void) NORETURN;
|
||||
|
||||
/// @}
|
||||
// The macros below are not documented as part of the WTPI package because they
|
||||
// are provided by Widevine, not by partners.
|
||||
|
||||
/* Logs the provided error message and aborts. All parameters are passed
|
||||
directly to LOGE(), so format strings may be used.
|
||||
|
||||
The infinite loop after the WTPI_Abort() call is there as a last-ditch safety
|
||||
in case the abort function somehow returns. */
|
||||
#define ABORT(...) \
|
||||
do { \
|
||||
LOGE(__VA_ARGS__); \
|
||||
WTPI_Abort(); \
|
||||
for (;;) { \
|
||||
} \
|
||||
} while (false);
|
||||
|
||||
/* This macro aborts if the first parameter evaluates to true. The remaining
|
||||
parameters are passed to ABORT() for logging. Note that parameters after
|
||||
the first are not evaluated unless the condition fails. */
|
||||
#define ABORT_IF(expression, ...) \
|
||||
if (UNLIKELY((expression))) { \
|
||||
ABORT(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_ABORT_INTERFACE_H_ */
|
||||
@@ -0,0 +1,76 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup secure-clock Monotonic Secure Clock
|
||||
|
||||
* Partners implementing a porting layer may either
|
||||
* 1. Implement wtpi_persistent_storage_layer2.h and
|
||||
* wtpi_clock_interface_layer2.h, and then use the reference implementation
|
||||
* wtpi_clock_and_gn_layer1.c for the clock and generation interfaces. This
|
||||
* is preferred if the hardware secure timer resets to 0 whenever the device
|
||||
* is inactive.
|
||||
* or
|
||||
* 2. Implement both this wtpi_clock_interface_layer1.h and
|
||||
* wtpi_generation_number_interface.h. This is preferred if the system has a
|
||||
* hardware secure wall clock.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the trusted time at the time of call and modifies |time_in_s|.
|
||||
*
|
||||
* The trusted time should be from a clock that is at least monotonic across
|
||||
* reboots, and is hardware protected while the TA is active. Ideally the
|
||||
* TrustedTime is a wall-clock that is hardware protected at all times, even
|
||||
* across reboots, if supported by the device.
|
||||
*
|
||||
* The zero time is not specified. For example, it may be seconds since initial
|
||||
* boot or seconds since the epoch. It may not be seconds since current boot
|
||||
* because that would not be increasing over system reboot.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] time_in_s: pointer to trusted time, in seconds.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if time_in_s is a null pointer
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s);
|
||||
|
||||
/**
|
||||
* Initialize the system clock.
|
||||
*/
|
||||
OEMCryptoResult WTPI_InitializeClock(void);
|
||||
|
||||
/**
|
||||
* Terminate the system clock.
|
||||
*/
|
||||
OEMCryptoResult WTPI_TerminateClock(void);
|
||||
|
||||
/**
|
||||
* Returns the clock type. See OEMCrypto documentation,
|
||||
* https://developers.google.com/widevine/drm/client/oemcrypto/v16/odk-timers
|
||||
* for the definition of insecure clock, secure timer, and secure clock.
|
||||
*/
|
||||
OEMCrypto_Clock_Security_Level WTPI_GetClockType(void);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER1_H_ */
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER2_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER2_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup secure-timer Non-monotonic Secure Clock
|
||||
*
|
||||
* Partners implementing a porting layer may either
|
||||
* 1. Implement wtpi_persistent_storage_layer2.h and this
|
||||
* wtpi_clock_interface_layer2.h, and then use the reference implementation
|
||||
* wtpi_clock_and_gn_layer1.c for the clock and generation interfaces. This
|
||||
* is preferred if the hardware secure timer resets to 0 whenever the device
|
||||
* is inactive.
|
||||
* or
|
||||
* 2. Implement both wtpi_clock_interface_layer1.h and
|
||||
* wtpi_generation_number_interface.h. This is preferred if the system has a
|
||||
* hardware secure wall clock.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves the value of the secure timer and stores it in |time_in_s|.
|
||||
*
|
||||
* This timer should be secure and monotonic, but we allow it to reset to
|
||||
* 0 whenever the system reboots.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] time_in_s: pointer to system time, in seconds.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if time_in_s is a null pointer
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetSecureTimer(uint64_t* time_in_s);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CLOCK_INTERFACE_LAYER2_H_ */
|
||||
212
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_config_interface.h
Normal file
212
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_config_interface.h
Normal file
@@ -0,0 +1,212 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CONFIG_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CONFIG_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "wtpi_config_macros.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup config Configuration and Output Control
|
||||
*
|
||||
* Configuration, feature support, and output controls.
|
||||
*
|
||||
* This interface covers two topics:
|
||||
*
|
||||
* * It gives the TA access to metadata about the device such as build info,
|
||||
* resource rating tier, etc.
|
||||
*
|
||||
* * It allows the TA to control and query the output control mechanisms such
|
||||
* as CGMS-A, HDCP, etc.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the security level of this TA.
|
||||
*/
|
||||
OEMCrypto_Security_Level WTPI_GetSecurityLevel(void);
|
||||
|
||||
/**
|
||||
* Returns the provisioning method configured for this TA.
|
||||
*/
|
||||
OEMCrypto_ProvisioningMethod WTPI_GetProvisioningMethod(void);
|
||||
|
||||
/**
|
||||
* Returns the resource rating tier associated with this device.
|
||||
*/
|
||||
uint32_t WTPI_GetResourceRatingTier(void);
|
||||
|
||||
typedef enum OPK_FeatureStatus {
|
||||
OPK_FEATURE_NOT_SUPPORTED = 0x69254167,
|
||||
OPK_FEATURE_ENABLED = 0x7961810e,
|
||||
OPK_FEATURE_DISABLED = 0x1427fa76,
|
||||
} OPK_FeatureStatus;
|
||||
|
||||
/**
|
||||
* Returns whether anti-rollback protections for the TEE software are enabled,
|
||||
* preventing rollback of the TEE software to an earlier version. If the device
|
||||
* does not support anti-rollback protections for the TEE software, this should
|
||||
* return `OPK_FEATURE_NOT_SUPPORTED`. If the device supports anti-rollback
|
||||
* protection of the TEE software, then this should return `OPK_FEATURE_ENABLED`
|
||||
* when that protection is enabled and `OPK_FEATURE_DISABLED` when that
|
||||
* protection is disabled.
|
||||
*/
|
||||
OPK_FeatureStatus WTPI_IsTAAntiRollbackEnabled(void);
|
||||
|
||||
/**
|
||||
* This function should return `false` if, for any reason, the device is not
|
||||
* production-ready, as defined by `OEMCrypto_ProductionReady()`. If the device
|
||||
* is fully locked-down and hardened, then it may return `true`.
|
||||
*
|
||||
* Note that returning `true` from this function is not sufficient for
|
||||
* `OEMCrypto_ProductionReady()` to indicate the device is production-ready.
|
||||
* If the OPK is built in debug mode or if `WTPI_IsTAAntiRollbackEnabled()`
|
||||
* indicates anti-rollback has been disabled, `OEMCrypto_ProductionReady()` may
|
||||
* report the device as not production-ready, even if this function returns
|
||||
* `true`.
|
||||
*
|
||||
* See the WTPI @ref logging interface for information about using the
|
||||
* preprocessor symbol OPK_IS_DEBUG to control debug logging. Having debug
|
||||
* logging enabled will cause OEMCrypto to report it is not production ready.
|
||||
*
|
||||
*/
|
||||
bool WTPI_IsProductionReady(void);
|
||||
|
||||
/**
|
||||
* Returns whether or not this device supports watermarking.
|
||||
*/
|
||||
OEMCrypto_WatermarkingSupport WTPI_GetWatermarkingSupport(void);
|
||||
|
||||
OEMCrypto_DTCP2_Capability WTPI_GetDTCP2Capability(void);
|
||||
|
||||
/**
|
||||
* Gets the current supported version of SRM for the device and sets the
|
||||
* |srm_version|. Returns OEMCrypto_SUCCESS if it was able to be fetched,
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT if |srm_version| is NULL, any
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise.
|
||||
*
|
||||
* @param[out] srm_version: pointer to SRM version.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetCurrentSRMVersion(uint32_t* srm_version);
|
||||
|
||||
/**
|
||||
* Returns whether the device has hardware protection preventing rollback of the
|
||||
* usage table.
|
||||
*/
|
||||
bool WTPI_IsAntiRollbackHWPresent(void);
|
||||
|
||||
/**
|
||||
* Returns whether or not the device was able to apply the CGMS protection for
|
||||
* the device. The |cgms_field| correlates to those under the Key Control Block
|
||||
* description in the OEMCrypto doc. If the cgms_field is invalid, return
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE. Even if this function is not called, the
|
||||
* device should attempt best effort for CGMS.
|
||||
*
|
||||
* @param[in] cgms_field: CGMS control bits.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
*/
|
||||
OEMCryptoResult WTPI_ApplyCGMS(uint8_t cgms_field);
|
||||
|
||||
/**
|
||||
* Returns whether CGMS is enabled for analog output for this device.
|
||||
*/
|
||||
bool WTPI_IsCGMS_AActive(void);
|
||||
|
||||
/**
|
||||
* Returns whether this device is capable of supporting 2-bit CGMS-A.
|
||||
*/
|
||||
bool WTPI_SupportsCGMS_A(void);
|
||||
|
||||
/**
|
||||
* Returns whether the device is capable of analog display.
|
||||
*/
|
||||
bool WTPI_HasAnalogDisplay(void);
|
||||
|
||||
/**
|
||||
* Returns whether analog display is enabled for this display.
|
||||
*/
|
||||
bool WTPI_IsAnalogDisplayActive(void);
|
||||
|
||||
/**
|
||||
* Returns whether the analog display is capable of being disabled. If this
|
||||
* device doesn't have analog display, return false.
|
||||
*/
|
||||
bool WTPI_CanDisableAnalogDisplay(void);
|
||||
|
||||
/**
|
||||
* Turn off analog display and return whether it was successful. If this device
|
||||
* doesn't have analog display, return false.
|
||||
*/
|
||||
bool WTPI_DisableAnalogDisplay(void);
|
||||
|
||||
/**
|
||||
* Returns the max buffer size/max subsample size in bytes allowed for
|
||||
* DecryptCENC. If there is no restriction, returns 0.
|
||||
*/
|
||||
size_t WTPI_MaxBufferSizeForDecrypt(void);
|
||||
|
||||
/**
|
||||
* Returns the max output size in bytes allowed for DecryptCENC and CopyBuffer.
|
||||
* If there is no restriction, returns 0.
|
||||
*/
|
||||
size_t WTPI_MaxOutputSizeForDecrypt(void);
|
||||
|
||||
/**
|
||||
* A closed platform can use clear buffers during decryption. A closed platform
|
||||
* is one where the entire OS is a Trusted Execution Environment and all buffers
|
||||
* are protected from the user. An obfuscated L3 build of OPK is *not*
|
||||
* considered a closed platform. If you intend to release a device as a closed
|
||||
* platform, you *must* share your design with Widevine and get approval before
|
||||
* launch.
|
||||
*/
|
||||
bool WTPI_IsClosedPlatform(void);
|
||||
|
||||
/**
|
||||
* Returns the current capability of the device. Look at the OEMCrypto
|
||||
* integration guide for full details on what the current capability entail.
|
||||
*/
|
||||
OEMCrypto_HDCP_Capability WTPI_CurrentHDCPCapability(void);
|
||||
|
||||
/**
|
||||
* Returns the maximum HDCP capability of the device. Look at the OEMCrypto
|
||||
* integration guide for full details on what the maximum capability entail.
|
||||
*/
|
||||
OEMCrypto_HDCP_Capability WTPI_MaxHDCPCapability(void);
|
||||
|
||||
/**
|
||||
* Returns the max buffer size allowed for OEMCrypto_Generic_*.
|
||||
* If there is no restriction, returns 0.
|
||||
*/
|
||||
size_t WTPI_MaxBufferSizeForGenericCrypto(void);
|
||||
|
||||
/**
|
||||
* Returns the max sample size allowed for OEMCrypto_DecryptCENC.
|
||||
* If there is no restriction, returns 0.
|
||||
*/
|
||||
size_t WTPI_MaxSampleSize(void);
|
||||
|
||||
/** Returns the type of certificates this device can support. See
|
||||
* OEMCrypto_SupportedCertificates in the integration guide for details on
|
||||
* return value.
|
||||
*/
|
||||
uint32_t WTPI_SupportedCertificates(void);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CONFIG_INTERFACE_H_ */
|
||||
92
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crc32_interface.h
Normal file
92
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_crc32_interface.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CRC32_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CRC32_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_output.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup crc32 CRC32
|
||||
*
|
||||
* This header defines a function for performing CRC32 on a buffer, possibly a
|
||||
* secure one. Most partners will want to use the Widevine-provided reference
|
||||
* implementation. But if you have a hardware CRC32 implementation, it may be
|
||||
* more performant, so you can implement Layer 1 yourself in that case. Also, if
|
||||
* your architecture does not allow secure buffers to be read from the OEMCrypto
|
||||
* TA, you will need to implement this yourself.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initializes the 32-bit |initial_hash| to the starting CRC-32 value.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] initial_hash: initial CRC value
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |initial_hash| is NULL
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
*/
|
||||
OEMCryptoResult WTPI_Crc32Init(uint32_t* initial_hash);
|
||||
|
||||
/**
|
||||
* Calculates the new crc-32 value given |in_length| bytes of |in| and the
|
||||
* previous CRC-32 value, |prev_crc|. Places the result in |new_crc|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] in: input buffer
|
||||
* @param[in] in_length: number of bytes to process
|
||||
* @param[in] prev_crc: previous CRC value to start from
|
||||
* @param[out] new_crc: new CRC value after processing input
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any pointers are NULL or
|
||||
* |in_length| is 0
|
||||
* @retval OEMCrypto_SUCCESS otherwise.
|
||||
*/
|
||||
OEMCryptoResult WTPI_Crc32Cont(const uint8_t* in, size_t in_length,
|
||||
uint32_t prev_crc, uint32_t* new_crc);
|
||||
|
||||
/**
|
||||
* Hashes the contents of an output buffer for the purposes of decrypt hash
|
||||
* verification. Calculates the new CRC-32 value given the previous CRC-32
|
||||
* value, |prev_crc|, and |in_length| bytes of the output buffer |in| starting
|
||||
* at offset |in_offset|. Places the result in |new_crc|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] in: input OutputBuffer type
|
||||
* @param[in] in_offset: offset into the data buffer where the CRC operation
|
||||
* should begin processing bytes
|
||||
* @param[in] in_length: number of bytes to process
|
||||
* @param[in] prev_crc: previous CRC hash
|
||||
* @param[out] new_crc: new CRC hash after bytes have been processed
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if |in| is a secure buffer and this
|
||||
* device does not support secure buffers
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |in_length| is 0 or any of the
|
||||
* pointers are NULL
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if the buffer is secure and the
|
||||
* handle is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_Crc32Cont_OutputBuffer(const OPK_OutputBuffer* in,
|
||||
size_t in_offset, size_t in_length,
|
||||
uint32_t prev_crc,
|
||||
uint32_t* new_crc);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CRC32_INTERFACE_H_ */
|
||||
@@ -0,0 +1,530 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_
|
||||
#define OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup crypto-key-1 Crypto and Key Management (Layer 1)
|
||||
*
|
||||
* This component handles loading encrypted keys, decrypting the keys, and using
|
||||
* the resulting keys to perform cryptographic operations.
|
||||
*
|
||||
* Decrypted keys should be protected from user-space. Ideally, they should be
|
||||
* protected even from a compromised TA, such as by only decrypting them inside
|
||||
* a separate TA or dedicated cryptography hardware that does not allow the key
|
||||
* material to be read back.
|
||||
*
|
||||
* Layer 1 of this component can be implemented directly. However, there are two
|
||||
* reference implementations of this component that may be useful to some
|
||||
* partners.
|
||||
*
|
||||
* * Layer 1 assumes it is possible to load every key up to the device’s
|
||||
* maximum supported number of keys simultaneously. However, many devices have
|
||||
* hardware that only supports having a small number of keys loaded
|
||||
* simultaneously. To support such hardware, there is
|
||||
* `crypto\_and\_key\_management\_layer1\_hw.c`. This reference
|
||||
* implementation stores keys in a large key table in memory and dynamically
|
||||
* loads and unloads keys from the crypto hardware’s smaller key table as
|
||||
* needed.
|
||||
*
|
||||
* * If you don’t have crypto hardware at all or if your crypto hardware is
|
||||
* very weak, you may instead use an OpenSSL-based reference implementation of
|
||||
* Layer 1 that does all crypto operations in software. This does come at a
|
||||
* security cost, as the decrypted key material is necessarily exposed to the
|
||||
* OEMCrypto TA.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Crypto and Key Management layer 1 serves as an abstraction of the
|
||||
* communication between the OEMCrypto TA and the crypto implementation. The
|
||||
* underlying crypto implementation can be either software-based such as
|
||||
* OpenSSL, or a hardware-backed cryptography solution.
|
||||
*
|
||||
* Partners implementing the Crypto and Key Management porting layer may either
|
||||
* 1. Implement wtpi_crypto_and_key_management_interface_layer2.h and
|
||||
* key_mapping_interface.h, and then use the reference implementation
|
||||
* wtpi_crypto_and_key_management_layer1_hw.c. This is preferred if there's a
|
||||
* hardware-backed crypto.
|
||||
* or
|
||||
* 2. Implement their own wtpi_crypto_and_key_management_interface_layer1.h, or
|
||||
* use the reference implementation
|
||||
* wtpi_crypto_and_key_management_layer1_openssl.c and
|
||||
* implement wtpi_device_key_access_interface.h and
|
||||
* wtpi_secure_buffer_access_interface.h. This is preferred if a software-based
|
||||
* crypto is used.
|
||||
*/
|
||||
|
||||
typedef struct wtpi_k1_symmetric_key_handle* WTPI_K1_SymmetricKey_Handle;
|
||||
|
||||
/**
|
||||
* Gets the key size from |key_handle| and places the result in |size|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to query for size
|
||||
* @param[out] size: output size value
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, or
|
||||
* |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
KeySize* size);
|
||||
|
||||
/**
|
||||
* Decrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and
|
||||
* places the result in |out_buffer|. |key_handle| is a handle to the AES key
|
||||
* used for decryption and |key_length| determines the number of bytes to use
|
||||
* from |key_handle|. |out_buffer| must be >= |in_buffer_length| bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to use for the decrypt operation
|
||||
* @param[in] key_length: length of key in bytes
|
||||
* @param[in] in_buffer: input data to decrypt
|
||||
* @param[in] in_buffer_length: length of input data in bytes
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out_buffer: output buffer for decrypted data, assumed to be the
|
||||
* same size as the input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |key_length| is not either 16 or 32 bytes, |key_length| is greater than the
|
||||
* size of the key, |in_buffer_length| is 0 or not a multiple of the AES block
|
||||
* size, or |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_AESCBCDecrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
size_t key_length,
|
||||
const uint8_t* in_buffer,
|
||||
size_t in_buffer_length,
|
||||
const uint8_t* iv, uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Encrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and
|
||||
* places the result in |out_buffer|. |key_handle| is a handle to the AES key
|
||||
* used for encryption. |out_buffer| must be >= |in_buffer_length| bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to use for the encrypt operation
|
||||
* @param[in] in_buffer: input data to encrypt
|
||||
* @param[in] in_buffer_length: length of input data in bytes
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out_buffer: output buffer for encrypted data, assumed to be the
|
||||
* same size as the input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |in_buffer_length| is 0 or not a multiple of the AES block size, or
|
||||
* |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_AESCBCEncrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* in_buffer,
|
||||
size_t in_buffer_length,
|
||||
const uint8_t* iv, uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |input_length| bytes of |input| using SHA1 as the
|
||||
* hash function and places the result in |out_buffer|. |key_handle| is a handle
|
||||
* to the key used in the derivation. |out_buffer| must be >= 20 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for the crypto operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 20 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the SHA256 hash of |input_length| bytes of |input|, placing
|
||||
* the result in |out_buffer|. |out_buffer| must be >= 32 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 32 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_SHA256(const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |input_length| bytes of |input| using SHA256 as
|
||||
* the hash function and places the result in |out_buffer|. |key_handle| is a
|
||||
* handle to the key used in the derivation. |out_buffer| must be >= 32 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for HMAC operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 32 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Verifies that the HMAC of |input_length| bytes of |input| matches
|
||||
* |signature| using SHA256 as the hash function. |key_handle| is a handle to
|
||||
* the key used in the derivation.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for HMAC operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[in] signature: signature to compare with input data after HMAC
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_SIGNATURE_FAILURE the signature isn't valid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* input,
|
||||
size_t input_length, const uint8_t* signature);
|
||||
|
||||
/**
|
||||
* This enum and struct form a tagged union for passing output buffers to
|
||||
* functions that write to them, such as decryption. Output buffers may be
|
||||
* clear, insecure buffers that are represented as direct memory pointers or
|
||||
* they may be secure buffers that are represented as a platform-specific
|
||||
* handle.
|
||||
*/
|
||||
typedef enum OPK_OutputBuffer_Type {
|
||||
OPK_CLEAR_INSECURE_OUTPUT_BUFFER = 0x77e790db,
|
||||
OPK_SECURE_OUTPUT_BUFFER = 0x7f3b7fc9,
|
||||
} OPK_OutputBuffer_Type;
|
||||
|
||||
typedef struct {
|
||||
/* Flag indicating whether the buffer is secure or not. */
|
||||
OPK_OutputBuffer_Type type;
|
||||
|
||||
/* If |type| is OPK_CLEAR_INSECURE_OUTPUT_BUFFER, this is a pointer to memory
|
||||
that can be written directly and should be accessed through the
|
||||
|clear_insecure| member of the union.
|
||||
|
||||
If |type| is OPK_SECURE_OUTPUT_BUFFER, this is a platform-specific handle
|
||||
to a secure buffer and should be accessed through the |secure| member of
|
||||
the union. */
|
||||
union {
|
||||
uint8_t* clear_insecure;
|
||||
void* secure;
|
||||
} buffer;
|
||||
|
||||
/* The total size of the buffer. */
|
||||
size_t size;
|
||||
} OPK_OutputBuffer;
|
||||
|
||||
/**
|
||||
* Requests that the porting layer copy some data from |in| into the output
|
||||
* buffer |out|, starting at offset |output_offset| and continuing for |size|
|
||||
* bytes. It is possible that the memory ranges of |in| and |out| will overlap.
|
||||
* The implementation of this function must behave correctly even if they
|
||||
* overlap.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data to be copied, in bytes
|
||||
* @param[in] out: output buffer
|
||||
* @param[in] output_offset: destination offset in output buffer
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED |out| is a secure buffer and this
|
||||
* device does not support secure buffers
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT |input_length| is 0 or any of the
|
||||
* pointer parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT |output_offset| + |input_length| is
|
||||
* greater than |out.size|
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT the buffer is secure and the
|
||||
* handle is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_CopyToOutputBuffer(const uint8_t* input,
|
||||
size_t input_length,
|
||||
const OPK_OutputBuffer* out,
|
||||
size_t output_offset);
|
||||
|
||||
/**
|
||||
* Generates |out_length| random bytes and places them in |out|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] out: output buffer
|
||||
* @param[in] out_length: number of bytes of random data to be generated
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE |out_length| is too big
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_C1_RandomBytes(uint8_t* out, size_t out_length);
|
||||
|
||||
/**
|
||||
* Initializes key management layer 1.
|
||||
*
|
||||
* @return OEMCrypto_SUCCESS or the result returned from the initialization of
|
||||
* the layer down below, if it exists.
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_InitializeKeyManagement(void);
|
||||
|
||||
/**
|
||||
* Terminates key management layer 2.
|
||||
*
|
||||
* @return OEMCrypto_SUCCESS or the result returned from the termination of
|
||||
* the layer down below, if it exists.
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_TerminateKeyManagement(void);
|
||||
|
||||
/**
|
||||
* Creates a layer 1 key handle from |size| bytes of |serialized_bytes| and the
|
||||
* |key_type|, and places the result in |out_key_handle|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] input: input key data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[in] key_type: type of key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* size is 0
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_CreateKeyHandle(
|
||||
const uint8_t* input, size_t input_length, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Creates a device specific key handle that is to be used in the specified
|
||||
* context. This key should be identical across reboots, but it should *not* be
|
||||
* the same on different devices. The key should be unique per-context and
|
||||
* per-key-type; however, MAC_KEY_CLIENT and MAC_KEY_SERVER must be the same
|
||||
* key. This key will be used to encrypt data and tie it to the current device.
|
||||
* |context| is the context this key will be used in the key derivation.
|
||||
* |out_key_type| the type of the key to be derived.
|
||||
* |out_key_size| is the size of the key to be derived, in bytes.
|
||||
* |out_key_handle| should be filled with the desired key handle.
|
||||
*
|
||||
* Note that, in the event a device is compromised, repaired, and goes through
|
||||
* keybox renewal, the device key used by this function should be changed as
|
||||
* well at the same time the device's keybox is renewed.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier providing context to the derivation
|
||||
* step
|
||||
* @param[in] out_key_type: type of key to produce
|
||||
* @param[out] out_key_handle: output key handle
|
||||
* @param[in] out_key_size: size of output key, in bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle(
|
||||
uint32_t context, SymmetricKeyType out_key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size);
|
||||
|
||||
/**
|
||||
* Decrypts |enc_key_length| bytes of |enc_key| using AES CBC with |iv| and the
|
||||
* decrypting key handle |decrypt_key_handle|, creates a layer 1 key handle from
|
||||
* the decrypted key with the specified type |key_type|, and places the result
|
||||
* in |out_key_handle|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] decrypt_key_handle: key handle for AES decryption
|
||||
* @param[in] enc_key: AES-encrypted key to be decrypted and turned into
|
||||
* a handle
|
||||
* @param[in] enc_key_length: length of AES-encrypted input key, in bytes
|
||||
* @param[in] iv: initialization vector
|
||||
* @param[in] key_type: type of key to produce from the AES-encrypted input
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |enc_key_length| is not either 16 or 32 bytes, or |decrypt_key_handle| is
|
||||
* invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key,
|
||||
size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Decrypts |enc_mac_keys_length| bytes of |enc_mac_keys| using AES CBC with
|
||||
* |iv| and the decrypting key handle |decrypt_key_handle|, from the decrypted
|
||||
* keys creates one MAC_KEY_SERVER layer 1 key handle, and one MAC_KEY_CLIENT
|
||||
* layer 1 key handle, and places the result in |out_mac_key_server| and
|
||||
* |out_mac_key_client|, respectively.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] decrypt_key_handle: key handle for AES decryption
|
||||
* @param[in] enc_mac_keys: AES-encrypted keys to be decrypted and turned into
|
||||
* handlese
|
||||
* @param[in] enc_mac_keys_length: length of AES-encrypted input, in bytes
|
||||
* @param[in] iv: initialization vector
|
||||
* @param[out] out_mac_key_server: output key handle for server
|
||||
* @param[out] out_mac_key_client: output key handle for client
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |enc_key_length| is not 64 bytes, or |decrypt_key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys,
|
||||
size_t enc_mac_keys_length, const uint8_t* iv,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_server,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_client);
|
||||
|
||||
/**
|
||||
* Derives a layer 1 key handle from input |key_handle| with the specified
|
||||
* context. The function derives either 128-bit key or 256-bit key.
|
||||
*
|
||||
* The derivation process for 128-bit key output:
|
||||
* 1. Using the input key handle |key_handle|, prepare an AES_CMAC 128-bit
|
||||
* operation.
|
||||
* 2. Feed |counter| into the CMAC.
|
||||
* 3. Feed |context_length| bytes from |context| into the CMAC.
|
||||
* 4. Create |out_key_handle| with the same process as
|
||||
* WTPI_K1_CreateKeyHandle(), using the result of the CMAC as the new input key
|
||||
* data.
|
||||
*
|
||||
* The derivation process for 256-bit key output:
|
||||
* 1. Using the input key handle |key_handle|, prepare an AES_CMAC 128-bit
|
||||
* operation.
|
||||
* 2. Feed |counter| into the CMAC.
|
||||
* 3. Feed |context_length| bytes from |context| into the CMAC.
|
||||
* 4. Prepare another AES_CMAC 128-bit operation with the same input key handle.
|
||||
* 5. Feed |counter+1| into the second CMAC.
|
||||
* 6. Feed |context_length| bytes from |context| into the second CMAC.
|
||||
* 7. Create |out_key_handle| with the same process as
|
||||
* WTPI_K1_CreateKeyHandle(), using the concatenated result of the first CMAC
|
||||
* operation and the second CMAC operation as the new input key data.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] key_handle: key handle for AES CMAC operation
|
||||
* @param[in] counter: input value used for CMAC. If this function is being
|
||||
* called multiple times to derive different keys from the same context, this
|
||||
* counter should be incremented +2 each time.
|
||||
* @param[in] context: input data for AES CMAC
|
||||
* @param[in] context_length: length of context data in bytes
|
||||
* @param[in] out_key_type: desired type of output key
|
||||
* @param[in] out_key_size: desired size of output key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |context_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_DeriveKeyFromKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, uint8_t counter,
|
||||
const uint8_t* context, size_t context_length,
|
||||
SymmetricKeyType out_key_type, KeySize out_key_size,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Wraps the |key_handle| into a buffer that can be saved to the file system.
|
||||
* The wrapping key must be device unique, and is derived with the specified
|
||||
* |context|. Caller ensures that |wrapped_key_length| is equal to the wrapped
|
||||
* key size specified in wtpi_config_macros.h.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] key_handle: key handle to be wrapped
|
||||
* @param[in] key_type: type of input key
|
||||
* @param[out] wrapped_key: output buffer
|
||||
* @param[in] wrapped_key_length: length of output buffer
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_WrapKey(uint32_t context,
|
||||
WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType key_type, uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length);
|
||||
|
||||
/**
|
||||
* Unwraps and creates a layer 1 key handle from |wrapped_key_length| bytes of
|
||||
* |wrapped_key| and the |key_type|, and places the result in |out_key_handle|.
|
||||
* The unwrapping key must be device unique, and is derived with the specified
|
||||
* |context|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] wrapped_key: key to be unwrapped
|
||||
* @param[in] wrapped_key_length: length of input key
|
||||
* @param[in] key_type: type of input key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* |wrapped_key_length| is not either 16 or 32 bytes
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_UnwrapIntoKeyHandle(
|
||||
uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length,
|
||||
SymmetricKeyType key_type, WTPI_K1_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Frees |key_handle| that was constructed from a previous call to
|
||||
* WTPI_K1_CreateKeyHandle.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to be freed
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_FreeKeyHandle(WTPI_K1_SymmetricKey_Handle key_handle);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_CRYPTO_AND_KEY_MANAGEMENT_LAYER1_H_ */
|
||||
@@ -0,0 +1,509 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CRYPTO_AND_KEY_MANAGEMENT_INTERFACE_LAYER2_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CRYPTO_AND_KEY_MANAGEMENT_INTERFACE_LAYER2_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup crypto-key-2 Crypto and Key Management (Layer 2)
|
||||
*
|
||||
* Crypto and Key Management layer 2 defines the interfaces between the
|
||||
* REFERENCE implementation of Crypto and Key Management layer 1
|
||||
* (wtpi_crypto_and_key_management_layer2_hw.c) and the hardware-backed
|
||||
* cryptography.
|
||||
*
|
||||
* Partners implementing the Crypto and Key Management porting layer may either
|
||||
* 1. Implement wtpi_crypto_and_key_management_interface_layer2.h and
|
||||
* key_mapping_interface.h, and then use the reference implementation
|
||||
* wtpi_crypto_and_key_management_layer1_hw.c. This is preferred if there's a
|
||||
* hardware-backed crypto.
|
||||
* or
|
||||
* 2. Implement their own wtpi_crypto_and_key_management_interface_layer1.h, or
|
||||
* use the reference implementation
|
||||
* wtpi_crypto_and_key_management_layer1_openssl.c and
|
||||
* implement wtpi_device_key_access_interface.h and
|
||||
* wtpi_secure_buffer_access_interface.h. This is preferred if a software-based
|
||||
* crypto is used.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct wtpi_k2_symmetric_key_handle* WTPI_K2_SymmetricKey_Handle;
|
||||
|
||||
/**
|
||||
* Returns true if |key_handle| it a valid key management layer 2 handle.
|
||||
* Otherwise returns false.
|
||||
*/
|
||||
bool WTPI_K2_IsKeyHandleValid(WTPI_K2_SymmetricKey_Handle key_handle);
|
||||
|
||||
/**
|
||||
* Checks whether |index| is a valid key slot in the key table of key
|
||||
* management layer 2.
|
||||
*
|
||||
* @param[in] index: key index
|
||||
*
|
||||
* @retval true if |index| is valid
|
||||
* @retval false otherwise
|
||||
*/
|
||||
bool WTPI_K2_IsKeyIndexValid(uint32_t index);
|
||||
|
||||
/**
|
||||
* Gets the key slot from |key_handle| and places the result in |index|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to query for index
|
||||
* @param[out] index: output key index
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL, or
|
||||
* if |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_GetKeyIndex(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
uint32_t* index);
|
||||
|
||||
/**
|
||||
* Gets the key type from |key_handle| and places the result in |type|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to query for type
|
||||
* @param[out] type: output key type
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL, or
|
||||
* if |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_GetKeyType(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType* type);
|
||||
|
||||
/**
|
||||
* Gets the key size from |key_handle| and places the result in |size|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to query for size
|
||||
* @param[out] size: output key size
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL, or
|
||||
* |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_GetKeySize(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
KeySize* size);
|
||||
|
||||
/**
|
||||
* Encrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and
|
||||
* places the result in |out_buffer|. |key_handle| is a handle to the AES key
|
||||
* used for encryption. |out_buffer| must be >= |in_buffer_length| bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to use for the encrypt operation
|
||||
* @param[in] in_buffer: input data to encrypt
|
||||
* @param[in] in_buffer_length: length of input data in bytes
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out_buffer: output buffer for encrypted data, assumed to be the
|
||||
* same size as the input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |in_buffer_length| is 0 or not a multiple of the AES block size, or
|
||||
* |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_AESCBCEncrypt(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* in_buffer,
|
||||
size_t in_buffer_length,
|
||||
const uint8_t* iv, uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Decrypts |in_buffer_length| bytes of |in_buffer| using AES CBC and |iv| and
|
||||
* places the result in |out_buffer|. |key_handle| is a handle to the AES key
|
||||
* used for decryption and |key_length| determines the number of bytes to use
|
||||
* from |key_handle|. |out_buffer| must be >= |in_buffer_length| bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to use for the decrypt operation
|
||||
* @param[in] key_length: length of key in bytes
|
||||
* @param[in] in_buffer: input data to decrypt
|
||||
* @param[in] in_buffer_length: length of input data in bytes
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out_buffer: output buffer for decrypted data, assumed to be the
|
||||
* same size as the input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |key_length| is not either 16 or 32 bytes, |key_length| is greater than the
|
||||
* size of the key, |in_buffer_length| is 0 or not a multiple of the AES block
|
||||
* size, or |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_AESCBCDecrypt(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
size_t key_length,
|
||||
const uint8_t* in_buffer,
|
||||
size_t in_buffer_length,
|
||||
const uint8_t* iv, uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Decrypts |in_buffer_length| bytes of |in_buffer| using AES CTR and |iv| and
|
||||
* places the result in |out_buffer|. |key_handle| is a handle to the AES key
|
||||
* used for decryption. |out_buffer| must be >= |in_buffer_length| bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to use for the decrypt operation
|
||||
* @param[in] in_buffer: input data to decrypt
|
||||
* @param[in] in_buffer_length: length of input data in bytes
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[in] block_offset: starting offset of an AES block
|
||||
* @param[out] out_buffer: output buffer for decrypted data, assumed to be the
|
||||
* same size as the input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |in_buffer_length| is 0, |block_offset| is greater than the AES block
|
||||
* size, or |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_AESCTRDecrypt(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* in_buffer,
|
||||
size_t in_buffer_length,
|
||||
const uint8_t* iv, size_t block_offset,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |input_length| bytes of |input| using SHA1 as the
|
||||
* hash function and places the result in |out_buffer|. |key_handle| is a handle
|
||||
* to the key used in the derivation. |out_buffer| must be >= 20 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for the crypto operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 20 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_HMAC_SHA1(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the SHA256 hash of |input_length| bytes of |input|, placing
|
||||
* the result in |out_buffer|. |out_buffer| must be >= 32 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 32 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_SHA256(const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |input_length| bytes of |input| using SHA256 as
|
||||
* the hash function and places the result in |out_buffer|. |key_handle| is a
|
||||
* handle to the key used in the derivation. |out_buffer| must be >= 32 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for HMAC operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[out] out_buffer: output buffer, assumed to be >= 32 bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_HMAC_SHA256(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* input, size_t input_length,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Verifies that the HMAC of |input_length| bytes of |input| matches
|
||||
* |signature| using SHA256 as the hash function. |key_handle| is a handle to
|
||||
* the key used in the derivation.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle for HMAC operation
|
||||
* @param[in] input: input data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[in] signature: signature to compare with input data after HMAC
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL or
|
||||
* |input_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_SIGNATURE_FAILURE the signature isn't valid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_HMAC_SHA256_Verify(
|
||||
WTPI_K2_SymmetricKey_Handle key_handle, const uint8_t* input,
|
||||
size_t input_length, const uint8_t* signature);
|
||||
|
||||
/**
|
||||
* Generates |out_length| random bytes and places them in |out|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] out: output buffer
|
||||
* @param[in] out_length: number of bytes of random data to be generated
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE |out_length| is too big
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_C2_RandomBytes(uint8_t* out, size_t out_length);
|
||||
|
||||
/**
|
||||
* Initializes key management layer 2.
|
||||
*
|
||||
* @return OEMCrypto_SUCCESS if the key table is initialized successfully
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_InitializeKeyManagement(void);
|
||||
|
||||
/**
|
||||
* Terminates key management layer 2.
|
||||
*
|
||||
* @return OEMCrypto_SUCCESS if the key table is destroyed successfully
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_TerminateKeyManagement(void);
|
||||
|
||||
/**
|
||||
* Creates a layer 2 key handle from |input_length| bytes of |input| and the
|
||||
* |key_type|, and places the result in |out_key_handle|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] input: input key data
|
||||
* @param[in] input_length: length of input data in bytes
|
||||
* @param[in] key_type: type of key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* size is 0
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_CreateKeyHandle(
|
||||
const uint8_t* input, size_t input_length, SymmetricKeyType key_type,
|
||||
WTPI_K2_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Creates a device specific key handle that is to be used in the specified
|
||||
* context. This key should be identical across reboots, but it should *not* be
|
||||
* the same on different devices. The key should be unique per-context and
|
||||
* per-key-type; however, MAC_KEY_CLIENT and MAC_KEY_SERVER must be the same
|
||||
* key. This key will be used to encrypt data and tie it to the current device.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier providing context to the derivation
|
||||
* step
|
||||
* @param[in] out_key_type: type of key to produce
|
||||
* @param[out] out_key_handle: output key handle
|
||||
* @param[in] out_key_size: size of output key, in bytes
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_DeriveDeviceKeyIntoHandle(
|
||||
uint32_t context, SymmetricKeyType out_key_type,
|
||||
WTPI_K2_SymmetricKey_Handle* out_key_handle, KeySize out_key_size);
|
||||
|
||||
/**
|
||||
* Decrypts |enc_key_length| bytes of |enc_key| using AES CBC with |iv| and the
|
||||
* decrypting key handle |decrypt_key_handle|, creates a layer 2 key handle from
|
||||
* the decrypted key with the specified type |key_type|, and places the result
|
||||
* in |out_key_handle|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] decrypt_key_handle: key handle for AES decryption
|
||||
* @param[in] enc_key: AES-encrypted key to be decrypted and turned into
|
||||
* a handle
|
||||
* @param[in] enc_key_length: length of AES-encrypted input key, in bytes
|
||||
* @param[in] iv: initialization vector
|
||||
* @param[in] key_type: type of key to produce from the AES-encrypted input
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |enc_key_length| is not either 16 or 32 bytes, or |decrypt_key_handle| is
|
||||
* invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_AESDecryptAndCreateKeyHandle(
|
||||
WTPI_K2_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key,
|
||||
size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type,
|
||||
WTPI_K2_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Decrypts |enc_mac_keys_length| bytes of |enc_mac_keys| using AES CBC with
|
||||
* |iv| and the decrypting key handle |decrypt_key_handle|, from the decrypted
|
||||
* keys creates one MAC_KEY_SERVER layer 2 key handle, and one MAC_KEY_CLIENT
|
||||
* layer 2 key handle, and places the result in |out_mac_key_server| and
|
||||
* |out_mac_key_client|, respectively.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] decrypt_key_handle: key handle for AES decryption
|
||||
* @param[in] enc_mac_keys: AES-encrypted keys to be decrypted and turned into
|
||||
* handlese
|
||||
* @param[in] enc_mac_keys_length: length of AES-encrypted input, in bytes
|
||||
* @param[in] iv: initialization vector
|
||||
* @param[out] out_mac_key_server: output key handle for server
|
||||
* @param[out] out_mac_key_client: output key handle for client
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |enc_key_length| is not 64 bytes, or |decrypt_key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_AESDecryptAndCreateKeyHandleForMacKeys(
|
||||
WTPI_K2_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys,
|
||||
size_t enc_mac_keys_length, const uint8_t* iv,
|
||||
WTPI_K2_SymmetricKey_Handle* out_mac_key_server,
|
||||
WTPI_K2_SymmetricKey_Handle* out_mac_key_client);
|
||||
|
||||
/**
|
||||
* Derives a layer 2 key handle from input |key_handle| with the specified
|
||||
* context. The function derives either 128-bit key or 256-bit key.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] key_handle: key handle for AES CMAC operation
|
||||
* @param[in] counter: input value used for CMAC. If this function is being
|
||||
* called multiple times to derive different keys from the same context, this
|
||||
* counter should be incremented +2 each time.
|
||||
* @param[in] context: input data for AES CMAC
|
||||
* @param[in] context_length: length of context data in bytes
|
||||
* @param[in] out_key_type: desired type of output key
|
||||
* @param[in] out_key_size: desired size of output key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL,
|
||||
* |context_length| is 0, or |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_DeriveKeyFromKeyHandle(
|
||||
WTPI_K2_SymmetricKey_Handle key_handle, uint8_t counter,
|
||||
const uint8_t* context, size_t context_length,
|
||||
SymmetricKeyType out_key_type, KeySize out_key_size,
|
||||
WTPI_K2_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Wraps the |key_handle| into a buffer that can be saved to the file system.
|
||||
* The wrapping key must be device unique, and is derived with the specified
|
||||
* |context|. Caller ensures that |wrapped_key_length| is equal to the wrapped
|
||||
* key size specified in wtpi_config_macros.h.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] key_handle: key handle to be wrapped
|
||||
* @param[in] key_type: type of input key
|
||||
* @param[out] wrapped_key: output buffer
|
||||
* @param[in] wrapped_key_length: length of output buffer
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_WrapKey(uint32_t context,
|
||||
WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType key_type, uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length);
|
||||
|
||||
/**
|
||||
* Unwraps and creates a layer 2 key handle from |wrapped_key_length| bytes of
|
||||
* |wrapped_key| and the |key_type|, and places the result in |out_key_handle|.
|
||||
* The unwrapping key must be device unique, and is derived with the specified
|
||||
* |context|.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] wrapped_key: key to be unwrapped
|
||||
* @param[in] wrapped_key_length: length of input key
|
||||
* @param[in] key_type: type of input key
|
||||
* @param[out] out_key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* |wrapped_key_length| is not either 16 or 32 bytes
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_UnwrapIntoKeyHandle(
|
||||
uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length,
|
||||
SymmetricKeyType key_type, WTPI_K2_SymmetricKey_Handle* out_key_handle);
|
||||
|
||||
/**
|
||||
* Encrypts the layer 2 |key_handle| with |encrypt_key_handle| and |iv|, and
|
||||
* places the result into a buffer |out_buffer| that can be saved in the layer 1
|
||||
* key table.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] key_handle: key handle to be encrypted
|
||||
* @param[in] encrypt_key_handle: encrypting key handle
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out_buffer: output buffer
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL, or
|
||||
* |key_handle| is invalid
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
*/
|
||||
// TODO(b/158766099): This can be removed once we finish the implementation of
|
||||
// WTPI_K2_WrapKey()
|
||||
OEMCryptoResult WTPI_K2_EncryptKeyHandle(
|
||||
WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
WTPI_K2_SymmetricKey_Handle encrypt_key_handle, const uint8_t* iv,
|
||||
uint8_t* out_buffer);
|
||||
|
||||
/**
|
||||
* Frees |key_handle| that was constructed from a previous call to
|
||||
* WTPI_K2_CreateKeyHandle.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: key handle to be freed
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT |key_handle| is invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_K2_FreeKeyHandle(WTPI_K2_SymmetricKey_Handle key_handle);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CRYPTO_AND_KEY_MANAGEMENT_INTERFACE_LAYER2_H_ */
|
||||
@@ -0,0 +1,390 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup asymmetric-crypto Asymmetric Cryptography
|
||||
*
|
||||
* This component handles loading decrypted asymmetric keys, using the keys to
|
||||
* perform cryptographic operations, and wrapping the keys before saving to
|
||||
* persistent storage.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Forward declaration of a pointer to an implementation-specific struct holding
|
||||
* all asymmetric key information.
|
||||
*/
|
||||
typedef struct tee_asymmetric_key_handle* WTPI_AsymmetricKey_Handle;
|
||||
|
||||
/**
|
||||
* Creates a key handle from |input_length| bytes of |input| and the
|
||||
* |key_type|, and places the result in |key_handle|.
|
||||
*
|
||||
* If the key type is DRM_RSA_PRIVATE_KEY, then |input| must be a DER-encoded
|
||||
* PKCS8 RSA private key.
|
||||
*
|
||||
* If the key type is DRM_ECC_PRIVATE_KEY, then |input| must be a DER-encoded
|
||||
* PKCS8 ECPrivateKey.
|
||||
*
|
||||
* If the key type is PROV40_ED25519_PRIVATE_KEY, then |input| must be a raw
|
||||
* key.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] input: DER-encoded PKCS8 private key byte array
|
||||
* @param[in] input_length: length of the input data
|
||||
* @param[in] key_type: type of asymmetric key
|
||||
* @param[out] key_handle: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or
|
||||
* |input_length| is 0
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY if |serialized_bytes| does not point
|
||||
* to a valid private key of the specified |key_type|
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures/
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_CreateAsymmetricKeyHandle(
|
||||
const uint8_t* input, size_t input_length, AsymmetricKeyType key_type,
|
||||
WTPI_AsymmetricKey_Handle* key_handle);
|
||||
|
||||
/**
|
||||
* Creates a key handle from |input_length| bytes of |input| and the
|
||||
* |key_type|, and places the result in |key_handle|.
|
||||
*
|
||||
* |*allowed_schemes| should be filled with the padding schemes that can be used
|
||||
* with the key.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] input: wrapped asymmetric key
|
||||
* @param[in] input_length: size of the wrapped asymmetric key
|
||||
* @param[in] key_type: type of asymmetric key
|
||||
* @param[out] key_handle: output key handle
|
||||
* @param[out] allowed_schemes: allowed padding schemes
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or
|
||||
* |input_length| is 0
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY if |key_type| is DRM_PRIVATE_RSA_KEY
|
||||
* and |serialized_bytes| is an invalid PKCS8 RSA private key; or if |key_type|
|
||||
* DRM_ECC_PRIVATE_KEY and |serialized_bytes| is not a valid ECPrivateKey
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_UnwrapIntoAsymmetricKeyHandle(
|
||||
const uint8_t* input, size_t input_length, AsymmetricKeyType key_type,
|
||||
WTPI_AsymmetricKey_Handle* key_handle, uint32_t* allowed_schemes);
|
||||
|
||||
/**
|
||||
* Frees |key_handle| that was constructed from a previous call to
|
||||
* WTPI_CreateAsymmetricKeyHandle.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key_handle: previously allocated key handle
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_handle| is NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_FreeAsymmetricKeyHandle(
|
||||
WTPI_AsymmetricKey_Handle key_handle);
|
||||
|
||||
/**
|
||||
* Calculates the buffer size needed to wrap the given private key. This is
|
||||
* given the number of bytes in the encrypted private key.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] enc_private_key_length: length of encrypted private key to be
|
||||
* wrapped
|
||||
* @param[in] key_type: type of asymmetric key
|
||||
* @param[out] buffer_size: output result with required wrapping size
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |buffer_size| is NULL, or if there
|
||||
* is an overflow when computing the |buffer_size|
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetWrappedAsymmetricKeySize(size_t enc_private_key_length,
|
||||
AsymmetricKeyType key_type,
|
||||
size_t* buffer_size);
|
||||
|
||||
// TODO(b/185149406): Consider using WTPI_AsymmetricKey_Handle instead to avoid
|
||||
// passing clear keys around.
|
||||
/**
|
||||
* Wraps the key data into a buffer that can be saved to the file system. The
|
||||
* wrapping must be device unique.
|
||||
*
|
||||
* Caller retains ownership of |clear_key| and |output| and they must
|
||||
* not be NULL. Caller ensures that size is at least as big as the wrapped key
|
||||
* size specified in WTPI_GetWrappedAsymmetricKeySize.
|
||||
*
|
||||
* This is given the clear, PKCS8-padded key and the key may be prefixed with
|
||||
* "SIGN" and a 4-byte code for the padding schemes.
|
||||
*
|
||||
* @param[out] output: destination buffer that will contain the wrapped key data
|
||||
* @param[in] output_length: length of destination buffer
|
||||
* @param[in] key_type: type of asymmetric key
|
||||
* @param[in] clear_key: DER-encoded PKCS8 RSA private key data with 8 bytes of
|
||||
* prefix data or PKCS8 ECPrivateKey (no prefix data).
|
||||
* @param[in] clear_key_length: length of input data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the pointers are NULL,
|
||||
* |clear_key_length| is 0
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER output_length is too small
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_WrapAsymmetricKey(uint8_t* output, size_t output_length,
|
||||
AsymmetricKeyType key_type,
|
||||
const uint8_t* clear_key,
|
||||
size_t clear_key_length);
|
||||
|
||||
/**
|
||||
* Sign |message_length| bytes of |message| with the given RSA key handle using
|
||||
* the given |padding scheme| and place the result in |signature|.
|
||||
* |key| is a handle to the RSA key used for signing.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: handle with RSA key required to sign
|
||||
* @param[in] message: input data to be signed
|
||||
* @param[in] message_length: length of data to be signed
|
||||
* @param[out] signature: destination buffer for output signature
|
||||
* @param[in,out] signature_length: size of destination buffer
|
||||
* @param[in] padding_scheme: RSA padding scheme used for signing
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|
||||
* |signature| is NULL, in which case it sets |signature_length| to the
|
||||
* appropriate length
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of
|
||||
* the pointers except |signature| are NULL
|
||||
* @retval OEMCrypto_ERROR_INVALID_KEY if the padding_scheme provided is not
|
||||
* supported
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_RSASign(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme);
|
||||
|
||||
/**
|
||||
* Decrypts |input_length| bytes of |input| and places it in |out|. The padding
|
||||
* scheme shall only be PKCS1 OAEP. |key| is a handle to the RSA key used for
|
||||
* decryption.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: handle with RSA key required to decrypt
|
||||
* @param[in] input: input data to be decrypted
|
||||
* @param[in] input_length: length of data to be decrypted
|
||||
* @param[out] out: destination buffer for decrypted data
|
||||
* @param[in,out] out_length: size of destination buffer
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|
||||
* |input_length| is 0
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_length| is too small, in which
|
||||
* case it sets |out_length| to the appropriate length
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_RSADecrypt(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* input, size_t input_length,
|
||||
uint8_t* out, size_t* out_length);
|
||||
|
||||
/**
|
||||
* Sign |message_length| bytes of |message| with the given ECC key handle using
|
||||
* the ECDSA with a curve specific hashing algorithm and place the result in
|
||||
* |signature|. |key| is a handle to the ECC key used for signing. The
|
||||
* resulting signature must be ASN.1 encoded as specified in RFC 3279 2.2.3.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: handle with ECC key, required for signing.
|
||||
* @param[in] message: input data to be signed
|
||||
* @param[in] message_length: length of input data in bytes
|
||||
* @param[out] signature: destination buffer for signature
|
||||
* @param[in,out] signature_length: size of |signature| buffer, may be
|
||||
* modified based on used/required space of output.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|
||||
* |signature| is NULL, in which case it sets |signature_length| to the
|
||||
* appropriate length
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of
|
||||
* the pointers except |signature| are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_ECCSign(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
/**
|
||||
* Derives the ECC-based session key using Widevine ECDH. The derived
|
||||
* key is placed into |session_key|. This key is a AES-256 to be used
|
||||
* as the key in the CMAC function used in the MAC and ENC key session
|
||||
* processes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: handle with ECC key, required for derivation.
|
||||
* @param[in] key_source: an ephemeral ECC public key used in ECDH.
|
||||
* @param[in] key_source_length: length of |key_source|
|
||||
* @param[out] session_key: destination buffer for derivated session key
|
||||
* @param[in,out] session_key_length: size of |session_key| buffer, may
|
||||
* be modified based on used/required space of output.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |session_key_length| is too small or
|
||||
* if |session_key| is NULL, in which case it sets |session_key_length|
|
||||
* to the appropriate length
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_source_length| is 0 or if
|
||||
* any of the pointers except |session_key| are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_ECCDeriveSessionKey(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* key_source,
|
||||
size_t key_source_length,
|
||||
uint8_t* session_key,
|
||||
size_t* session_key_length);
|
||||
|
||||
/**
|
||||
* Gets the maximum RSA, ECC or ED25519 signature size from |key| and places the
|
||||
* result in |signature_length|.
|
||||
* Note, it is possible that the signature generate by this key be a few bytes
|
||||
* shorter depending on the actual signature value.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: input key handle
|
||||
* @param[out] signature_length: max size of the signature generated
|
||||
* by |key|.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetSignatureSize(WTPI_AsymmetricKey_Handle key,
|
||||
size_t* signature_length);
|
||||
|
||||
/**
|
||||
* Sign |message_length| bytes of |message| with the given ED25519 key handle
|
||||
* and place the result in |signature|. |key| is a handle to the ED25519 key
|
||||
* used for signing.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] key: handle with ED25519 key, required for signing.
|
||||
* @param[in] message: input data to be signed
|
||||
* @param[in] message_length: length of input data in bytes
|
||||
* @param[out] signature: destination buffer for signature
|
||||
* @param[in,out] signature_length: size of |signature| buffer, may be
|
||||
* modified based on used/required space of output.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|
||||
* |signature| is NULL, in which case it sets |signature_length| to the
|
||||
* appropriate length
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of
|
||||
* the pointers except |signature| are NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_ED25519Sign(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
/**
|
||||
* Writes the boot certificate chain (BCC) in provisioning 4.0 to |out|, with
|
||||
* number of bytes specified in |out_length|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] out: output buffer to write BCC
|
||||
* @param[in,out] out_length: size of bcc buffer, may be modified based on
|
||||
* used/required space of output.
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |out_length| is NULL
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_length| is too small, or |out|
|
||||
* is NULL, in which case it sets |out_length| to the appropriate length
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE any other failures
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length);
|
||||
|
||||
/**
|
||||
* Generate a pair of RSA or ECC key pair, which will be used in the certificate
|
||||
* signing request as specified in provisioning 4. The output key type must be
|
||||
* either RSA or ECC, which should be specified in |key_type|.
|
||||
*
|
||||
* Returns
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER if |wrapped_private_key_length| or
|
||||
* |public_key_length| is too small, or if |wrapped_private_key| or |public_key|
|
||||
* is NULL, in which case the appropriate length should be returned.
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT if |key_type|, |wrapped_private_key_length|
|
||||
* or |public_key_length| is NULL.
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures.
|
||||
* OEMCrypto_SUCCESS otherwise.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] key_type: Type (RSA or EC) of the generated key pair.
|
||||
* @param[out] wrapped_private_key: The generated private key, wrapped with
|
||||
* encryption key for storage.
|
||||
* @param[in,out] wrapped_private_key_length: size of |wrapped_private_key|
|
||||
* buffer, may be modified based on used/required space of output.
|
||||
* @param[out] public_key: The generated public key.
|
||||
* @param[in,out] public_key_length: size of |public_key_length| buffer, may be
|
||||
* modified based on used/required space of output.
|
||||
*/
|
||||
OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair(
|
||||
AsymmetricKeyType* key_type, uint8_t* wrapped_private_key,
|
||||
size_t* wrapped_private_key_length, uint8_t* public_key,
|
||||
size_t* public_key_length);
|
||||
|
||||
/**
|
||||
* Used the device specific asymmetric key pair as specified in provisioning 4
|
||||
* to sign |message| and write the COSE_SIGN1 format signature to |signature|.
|
||||
* Note that the device key must be unique per individual device. The key must
|
||||
* be identical across reboots.
|
||||
*
|
||||
* Caller retains ownership of all parameters.
|
||||
*
|
||||
* @param[in] message: input data to be signed
|
||||
* @param[in] message_length: length of input data in bytes
|
||||
* @param[out] signature: destination buffer for signature
|
||||
* @param[in,out] signature_length: size of |signature| buffer, may be
|
||||
* modified based on used/required space of output.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|
||||
* |signature| is NULL, in which case it sets |signature_length| to the
|
||||
* appropriate length.
|
||||
* @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE if |message_length| is too large.
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |message_length| is 0 or if any of
|
||||
* the pointers except |signature| are NULL.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures.
|
||||
*/
|
||||
OEMCryptoResult WTPI_DeviceKeyCoseSign1(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CRYPTO_ASYMMETRIC_INTERFACE_H_ */
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "oemcrypto_output.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup decrypt-sample Sample Decrypt
|
||||
*
|
||||
* Data arrives in OEMCrypto in the form of samples made up of smaller
|
||||
* subsamples. Interpreting the subsamples to know which bytes should be left
|
||||
* clear or decrypted and what IV to use for the latter is non-trivial. Some
|
||||
* devices have dedicated hardware for doing decryption of a full sample in one
|
||||
* pass, skipping and decrypting bytes as appropriate. On such devices,
|
||||
* decrypting the subsamples piecemeal is inefficient. But other devices can
|
||||
* only decrypt one portion of a subsample at a time.
|
||||
*
|
||||
* The Sample Decryption interface defines decryption in terms of full
|
||||
* samples. If your device has hardware support for full-sample decryption, you
|
||||
* will get the best performance by implementing this layer yourself.
|
||||
*
|
||||
* However, if your device does not have support for hardware full-sample
|
||||
* decryption, you may find it simpler to use the reference implementation of
|
||||
* this layer. This implementation will handle interpreting the subsamples for
|
||||
* you and send the decryption to the WTPI’s other cryptography interfaces one
|
||||
* subsample at a time.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Decrypt Sample layer defines the interface between the OEMCrypto TA and the
|
||||
* implementation of sample decryption.
|
||||
*
|
||||
* Partners implementing the Decrypt Sample porting layer may either
|
||||
* 1. Implement their own wtpi_decrypt_sample_interface.h. This is preferred if
|
||||
* the device has hardware support for full-sample decryption, or
|
||||
* 2. Use the reference implementation wtpi_decrypt_sample.c. This is preferred
|
||||
* when there is no hardware support for full-sample decryption. The reference
|
||||
* implementation will split the subsamples and decrypt them individually using
|
||||
* the crypto_and_key_management_interface_layer1 component.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Decrypts one sample |sample| with the specified |pattern| and |cipher_mode|
|
||||
* using the |key_handle| to the current content key, into buffer
|
||||
* |output_buffer| starting at an offset |output_offset|.
|
||||
* Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL or
|
||||
* invalid, or the result of DecryptSubsample if failing to decrypt any
|
||||
* subsample, and OEMCrypto_SUCCESS otherwise.
|
||||
* Caller retains ownership of all parameters.
|
||||
*/
|
||||
OEMCryptoResult WTPI_DecryptSample(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const OEMCrypto_SampleDescription* sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
const OPK_OutputBuffer* output_buffer, size_t output_offset,
|
||||
OEMCryptoCipherMode cipher_mode);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_DECRYPT_SAMPLE_INTERFACE_H_ */
|
||||
@@ -0,0 +1,49 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_DEVICE_KEY_ACCESS_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_DEVICE_KEY_ACCESS_INTERFACE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup dev-key-access Device Key Access
|
||||
*
|
||||
* This is a lower level of the WTPI device key package. The OPK does not
|
||||
* directly call functions in this API. Partners have the option to implement
|
||||
* this API and use Widevine's reference implementation of the layer 1 interface
|
||||
* which wraps around the functions in this API, or instead implement all of the
|
||||
* [Device Keys](@ref dev-key) functions.
|
||||
*
|
||||
* Note that, in the event a device is compromised, repaired, and goes through
|
||||
* keybox renewal, the device key used by these functions should be changed as
|
||||
* well at the same time the device's keybox is renewed.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Returns the device unique key.
|
||||
*
|
||||
* @return The device unique key.
|
||||
* */
|
||||
const uint8_t* WTPI_GetDeviceKey(void);
|
||||
|
||||
/** Returns the size of the device key.
|
||||
*
|
||||
* @return The size of the device unique key.
|
||||
* */
|
||||
KeySize WTPI_GetDeviceKeySize(void);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_DEVICE_KEY_ACCESS_INTERFACE_H_ */
|
||||
161
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_interface.h
Normal file
161
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_device_key_interface.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_DEVICE_KEY_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_DEVICE_KEY_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup dev-key Device Keys
|
||||
*
|
||||
* This is the top layer of the porting layer. The OPK directly calls functions
|
||||
* in this file. Partners have the option to implement these functions directly,
|
||||
* or use the reference version of the device key interface functions, and
|
||||
* instead implement the device key access functions.
|
||||
*
|
||||
* Note that, in the event a device is compromised, repaired, and goes through
|
||||
* keybox renewal, the device key used by these functions should be changed as
|
||||
* well at the same time the device's keybox is renewed.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* The types of device specific keys that can be generated are as follows. */
|
||||
|
||||
/** A device unique key for encrypting/signing the usage table data. This key
|
||||
* must be unique to this device so that usage tables may not be copied from one
|
||||
* device to another.
|
||||
* This should be used as a key derivation context in
|
||||
* WTPI_K1_DeriveDeviceKeyIntoHandle().
|
||||
*/
|
||||
#define DEVICE_KEY_WRAP_USAGE_TABLE 0x22d8fdcf
|
||||
|
||||
/** A device unique key for encrypting/signing the private key in the DRM
|
||||
* certificate. This key must be unique to this device so that a DRM certificate
|
||||
* not be copied from one device to another.
|
||||
* This should be used as a key derivation context in
|
||||
* WTPI_K1_DeriveDeviceKeyIntoHandle().
|
||||
*/
|
||||
#define DEVICE_KEY_WRAP_DRM_CERT 0x1db2a411
|
||||
|
||||
/** A device unique key for encrypting the internal key used by the
|
||||
* implementation of the key management layer. This should be used as a key
|
||||
* derivation context in WTPI_K1_DeriveDeviceKeyIntoHandle().
|
||||
*/
|
||||
#define DEVICE_KEY_WRAP_INTERNAL_KEY 0x604e77a1
|
||||
|
||||
/** A device unique key for encrypting the mac keys in usage entry.
|
||||
*/
|
||||
#define DEVICE_KEY_WRAP_MAC_KEY 0x125cc98d
|
||||
|
||||
/** A device unique key seed used in provisioning 4.0 to derive a ED25519 key
|
||||
* pair, which serves as the device root key.
|
||||
*/
|
||||
#define DEVICE_KEY_PROV40_SEED 0x5b459913
|
||||
|
||||
/**
|
||||
* Gets the size (in bytes) of the buffer needed by WTPI_EncryptAndSign to
|
||||
* handle a buffer of the given size (in bytes). The return value should
|
||||
* include |in_size| in the result.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] in_size: size of the input buffer to be encrypted and signed
|
||||
* @param[out] wrapped_size: output result with required wrapping size
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |wrapped_size| is NULL, or if
|
||||
* there is an overflow when computing the |wrapped_size|
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetEncryptAndSignSize(uint32_t context, size_t in_size,
|
||||
size_t* wrapped_size);
|
||||
|
||||
/**
|
||||
* Encrypts the given buffer and signs it in a way that can be verified later.
|
||||
* How this is done is implementation-defined. The encryption should be
|
||||
* device-specific so it can't be used on another device. This should check the
|
||||
* buffer size and return OEMCrypto_ERROR_SHORT_BUFFER if there isn't enough
|
||||
* space. The input needs to be padded to a multiple of 16 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] data: input buffer to be encrypted and signed
|
||||
* @param[in] data_size: size of the input buffer
|
||||
* @param[out] out: output buffer
|
||||
* @param[in,out] out_size: size of output buffer
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_size| is too small
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_EncryptAndSign(uint32_t context, const uint8_t* data,
|
||||
size_t data_size, uint8_t* out,
|
||||
size_t* out_size);
|
||||
|
||||
/**
|
||||
* Verifies the buffer has a valid signature and decrypts it into the given
|
||||
* buffer. This should return OEMCrypto_ERROR_SIGNATURE_FAILURE if the
|
||||
* signature fails. This should check the buffer size and return
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER if there isn't enough space. If the input is
|
||||
* padded, the padding is included in the output.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] context: 32-bit identifier to act as context
|
||||
* @param[in] wrapped: input buffer which is encrypted and signed
|
||||
* @param[in] wrapped_size: size of the input buffer
|
||||
* @param[out] out: output buffer
|
||||
* @param[in,out] out_size: size of output buffer
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if |out_size| is too small
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_VerifyAndDecrypt(uint32_t context, const uint8_t* wrapped,
|
||||
size_t wrapped_size, uint8_t* out,
|
||||
size_t* out_size);
|
||||
|
||||
/**
|
||||
* This function is only required to be implemented on devices that previously
|
||||
* shipped with a pre-release version of OPK, for the purpose of addressing
|
||||
* backward-compatibility issue with older wrapping method and formats of usage
|
||||
* table header and entry data, which already exists on those devices and cannot
|
||||
* be handled by the current WTPI_VerifyAndDecrypt() above. Returns
|
||||
* OEMCrypto_ERROR_NOT_IMPLEMENTED otherwise.
|
||||
*
|
||||
* Verifies the |wrapped| buffer has a valid signature identical to |signature|,
|
||||
* and decrypts it into the given buffer |out|. If the input is padded, the
|
||||
* padding is included in the output.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] wrapped: input buffer which is encrypted and signed
|
||||
* @param[in] wrapped_size: size of the input buffer
|
||||
* @param[in] signature: signature to be verified against
|
||||
* @param[in] iv: initialization vector for AES operation
|
||||
* @param[out] out: output buffer
|
||||
*
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL
|
||||
* @retval OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature validation fails
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if no legacy data is present
|
||||
* @retval OEMCrypto_SUCCESS otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_VerifyAndDecryptUsageData_Legacy(const uint8_t* wrapped,
|
||||
size_t wrapped_size,
|
||||
const uint8_t* signature,
|
||||
const uint8_t* iv,
|
||||
uint8_t* out);
|
||||
|
||||
/// @}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_DEVICE_KEY_INTERFACE_H_ */
|
||||
@@ -0,0 +1,123 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_1_H_
|
||||
#define OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_1_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup dev-renewal-1 Device Renewal (Layer 1)
|
||||
*
|
||||
* If a device is compromised and patched, it is necessary to rotate the secret
|
||||
* keys on the device in order to renew the device to full functionality. This
|
||||
* component handles access to the data required in order to rotate the keys and
|
||||
* other information of the device after a compromise. Renewing a device
|
||||
* requires updating the system ID to a new value (received from Widevine) and
|
||||
* providing a 16-byte shared renewal key, which must be secret and must not
|
||||
* have been revealed by the compromise.
|
||||
*
|
||||
* This API is used by the reference implementation of Root of Trust Layer 1,
|
||||
* but it is designed to also be useful for writing your own WTPI
|
||||
* implementations that need to perform renewal.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initializes the Device Renewal component. The pre-renewal system ID is
|
||||
* provided in case it is useful for looking up the renewal info. (For instance,
|
||||
* if the same TA code is being shared by multiple devices and the code needs
|
||||
* to know which device it is running on.) This function will be called by the
|
||||
* reference implementation of Root of Trust Layer 1. If you are not using the
|
||||
* reference implementation of Root of Trust Layer 1 but you _are_ using this
|
||||
* API in your own code, you must be sure to call this initialization function
|
||||
* before using any of the other functions in this component.
|
||||
*
|
||||
* On devices that have never been compromised and renewed, this function should
|
||||
* return `OEMCrypto_ERROR_NOT_IMPLEMENTED`.
|
||||
*
|
||||
* @param[in] old_system_id: the old, pre-renewal system ID
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS The call succeeded and the Device Renewal component
|
||||
* is initialized.
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED The device has never been compromised
|
||||
* and renewed. The TA should skip performing renewal.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_InitializeDeviceRenewal(uint32_t old_system_id);
|
||||
|
||||
/**
|
||||
* Terminates the Device Renewal component and releases any resources allocated
|
||||
* during initialization. This function will be called by the reference
|
||||
* implementation of Root of Trust Layer 1. If you are not using the reference
|
||||
* implementation of Root of Trust Layer 1 but you _are_ using this API in your
|
||||
* own code, you must be sure to call this function during termination.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS The call succeeded and the Device Renewal component
|
||||
* is terminated. Or, the device has never been compromised and renewed.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_TerminateDeviceRenewal(void);
|
||||
|
||||
/**
|
||||
* Gets the new, post-renewal system ID of the device. This operation may be
|
||||
* called many times by the TA code, so it should ideally be cheap. If getting
|
||||
* the new system ID is expensive or should only be done once per TA launch,
|
||||
* consider using the reference implementation of Layer 1 to cache this value
|
||||
* and instead implement the Layer 2 API.
|
||||
*
|
||||
* On devices that have never been compromised and renewed, this function should
|
||||
* return `OEMCrypto_ERROR_NOT_IMPLEMENTED`.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] new_system_id: The function should set this to the post-renewal
|
||||
* system ID of the device. This pointer will never be NULL.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS The call succeeded and all out values are filled
|
||||
* in.
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED The device has never been compromised
|
||||
* and renewed. The TA should skip performing renewal.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetRenewalSystemId(uint32_t* new_system_id);
|
||||
|
||||
/**
|
||||
* Loads the shared renewal key for the device into a key handle. This operation
|
||||
* may be called many times by the TA code, so it should ideally be cheap. If
|
||||
* getting the renewal key is expensive or should only be done once per TA
|
||||
* launch, consider using the reference implementation of Layer 1 to cache this
|
||||
* value and instead implement the Layer 2 API.
|
||||
*
|
||||
* On devices that have never been compromised and renewed, this function should
|
||||
* return `OEMCrypto_ERROR_NOT_IMPLEMENTED`.
|
||||
*
|
||||
* Caller retains ownership of all pointers. The caller is responsible for
|
||||
* freeing the key handle when they are done using it.
|
||||
*
|
||||
* @param[out] renewal_key: A pointer to a key handle that should be populated
|
||||
* with the renewal key. This pointer will never be NULL.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS The call succeeded and all out values are filled
|
||||
* in.
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED The device has never been compromised
|
||||
* and renewed. The TA should skip performing renewal.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_LoadRenewalKey(WTPI_K1_SymmetricKey_Handle* renewal_key);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_1_H_ */
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_2_H_
|
||||
#define OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_2_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup dev-renewal-2 Device Renewal (Layer 2)
|
||||
*
|
||||
* On some devices, accessing the renewal info is expensive or should otherwise
|
||||
* only be done once per initialization of OEMCrypto. For these devices, the
|
||||
* reference implementation of Device Renewal Layer 1 is provided, which calls
|
||||
* this Layer 2 component. The function in this API will only be called once per
|
||||
* initialization, after which its results will be cached by the reference
|
||||
* implementation of Layer 1. The only exception is if a new keybox is
|
||||
* factory-installed on the device, in which case this function will be called
|
||||
* again with the new system ID from the new keybox.
|
||||
*
|
||||
* The constraints on the data returned are the same as for Layer 1. Renewing a
|
||||
* device requires updating the system ID to a new value (received from
|
||||
* Widevine) and providing a 16-byte shared renewal key, which must be secret
|
||||
* and must not have been revealed by the compromise.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the current system ID and renewal key for the device if the device has
|
||||
* been compromised and renewed. The old system ID is provided for reference, in
|
||||
* case it is useful when looking up the renewal information. This function
|
||||
* should blindly return `OEMCrypto_ERROR_NOT_IMPLEMENTED` on devices that have
|
||||
* never been renewed.
|
||||
*
|
||||
* This function will be called once every time that OEMCrypto is initialized
|
||||
* and once every time a new keybox is factory-installed on the device.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] old_system_id: The pre-renewal system ID of the device.
|
||||
* @param[out] new_system_id: The function should set this to the post-renewal
|
||||
* system ID of the device. This pointer will never be NULL.
|
||||
* @param[out] renewal_key: A buffer that should be filled with the 16-byte
|
||||
* renewal key. This pointer will never be NULL.
|
||||
* @param[in] renewal_key_length: The length of the renewal_key buffer, which
|
||||
* will always be 16.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS The call succeeded and all out values are filled
|
||||
* in.
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED The device has never been compromised
|
||||
* and renewed. The TA should skip performing renewal.
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetRenewalInfo(uint32_t old_system_id,
|
||||
uint32_t* new_system_id,
|
||||
uint8_t* renewal_key,
|
||||
size_t renewal_key_length);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_DEVICE_RENEWAL_INTERFACE_LAYER_2_H_ */
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup gen-number Generation Number
|
||||
*
|
||||
* OEMCrypto needs to store the usage table’s generation number in persistent
|
||||
* storage with antirollback protection. Persistent storage for the generation
|
||||
* number should have antirollback protection, such as RPMB.
|
||||
*
|
||||
* Partners implementing a porting layer may either
|
||||
* 1. Implement wtpi_persistent_storage.h and wtpi_clock_interface_layer2.h,
|
||||
* and then use the reference implementation wtpi_clock_and_gn_layer1.c for
|
||||
* the clock and generation interfaces. This is preferred if the hardware
|
||||
* secure timer resets to 0 whenever the device is inactive.
|
||||
* or
|
||||
* 2. Implement both wtpi_clock_interface_layer1.h and
|
||||
* this wtpi_generation_number_interface.h. This is preferred if the system
|
||||
* has a hardware secure wall clock.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare to load the generation number. If the generation number is loaded
|
||||
* asynchronously, this should initialize that process so that the next call to
|
||||
* WTPI_LoadGenerationNumber does not block for too long.
|
||||
*
|
||||
* By "too long" we mean that initialization should not be blocked for more than
|
||||
* about 3 seconds. In a normal situation, we expect initialization to take on
|
||||
* the order of 10 to 100 milliseconds or less. This is a soft requirement. If
|
||||
* there is a good reason for a device to take much longer to initialize, then
|
||||
* the implementer may allow this to block as long as needed. It is the
|
||||
* implementer's responsibility to consider the user experience.
|
||||
*
|
||||
* The generation number should be stored in secure persistent storage. By
|
||||
* *persistent* we mean that the generation number should not be changed by
|
||||
* shutting down and later restarting the system. By *secure* we mean that the
|
||||
* user should not be able to modify the generation number. In particular, the
|
||||
* user should not be able to revert the generation number to a previous value.
|
||||
*
|
||||
* Returns OEMCrypto_SUCCESS on success. On failure, initialization of the TA
|
||||
* will fail.
|
||||
*/
|
||||
OEMCryptoResult WTPI_PrepareGenerationNumber(void);
|
||||
|
||||
/**
|
||||
* Load the usage table generation number. This is called once, on first use of
|
||||
* the usage table. This is expected to block until a value is available. It is
|
||||
* the porting interface's responsibility to fail if this blocks too
|
||||
* long. Returns OEMCrypto_SUCCESS on success. Other return values will fail the
|
||||
* OEMCrypto initialization. If the generation number has never been saved
|
||||
* before, a random number should be generated -- this is NOT an error.
|
||||
*
|
||||
* @param[out] value: pointer to generation number.
|
||||
*/
|
||||
OEMCryptoResult WTPI_LoadGenerationNumber(uint64_t* value);
|
||||
|
||||
/**
|
||||
* Save the generation number.
|
||||
*
|
||||
* If the generation number is saved asynchronously, then it is OK for the first
|
||||
* call to this function to begin a save process and then return
|
||||
* OEMCrypto_SUCCESS. Subsequent calls will verify that the previous save has
|
||||
* completed successfully. If the previous save resulted in an error, then this
|
||||
* call will return an error. If the previous call has not completed, then this
|
||||
* call should block until the previous save finishes before initializing a new
|
||||
* save. If the previous call was successful, then this call will initialize a
|
||||
* new save and return OEMCrypto_SUCCESS. It is the porting interface's
|
||||
* responsibility to fail if this blocks too long.
|
||||
*
|
||||
* If the generation number is saved synchronously, then this function should
|
||||
* attempt to save the generation number and return OEMCrypto_SUCCESS if the
|
||||
* save was successful.
|
||||
*
|
||||
* Returns OEMCrypto_SUCCESS on success. Other return values will fail the
|
||||
* current attempt to save the usage table. If a failure actually indicates that
|
||||
* the previous save had failed, then the usage table will be saved with a
|
||||
* generation number skew of 1. When the usage table is loaded with a generation
|
||||
* skew of 1, it will result in a warning, but not a failure.
|
||||
*
|
||||
* @param[in] value: generation number.
|
||||
*/
|
||||
OEMCryptoResult WTPI_SaveGenerationNumber(uint64_t value);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_GENERATION_NUMBER_INTERFACE_H_ */
|
||||
75
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_idle_interface.h
Normal file
75
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_idle_interface.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_IDLE_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_IDLE_INTERFACE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup idle Idle State Control
|
||||
*
|
||||
* This interface allows the TEE to respond to device idleness signals from the
|
||||
* REE. OEMCrypto may want to reduce power consumption or other resources during
|
||||
* periods of idleness. For example, it may be possible to reduce the CPU clock
|
||||
* rates.
|
||||
*
|
||||
* The functions in this component are the direct counterparts of their
|
||||
* OEMCrypto equivalents and have all the same requirements and guarantees. All
|
||||
* functions in this component are optional. Devices that do not want to respond
|
||||
* to idleness or that have other mechanisms for handling idleness may choose to
|
||||
* return `OEMCrypto_ERROR_NOT_IMPLEMENTED` from all these functions.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Notifies the device integration that the device has entered some form of idle
|
||||
* state.
|
||||
*
|
||||
* This function is equivalent to `OEMCrypto_Idle()` and has all the same
|
||||
* requirements and guarantees, including the need to coordinate the meaning of
|
||||
* `os_specific_code` values with the REE OS vendor. On Android, the only
|
||||
* supported `os_specific_code` is `0`.
|
||||
*
|
||||
* @param[in] state: the idle state the device has entered
|
||||
* @param[in] os_specific_code: an OS-specific code from the REE OS
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if the device does not implement idle
|
||||
* state functionality
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_Idle(OEMCrypto_IdleState state, uint32_t os_specific_code);
|
||||
|
||||
/**
|
||||
* Notifies the device integration that the device has awoken from all
|
||||
* previously-reported idle states.
|
||||
*
|
||||
* This function is equivalent to `OEMCrypto_Wake()` and has all the same
|
||||
* requirements and guarantees.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if the device does not implement idle
|
||||
* state functionality
|
||||
* @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED if the device cannot recover from
|
||||
* being idle
|
||||
* @retval OEMCrypto_ERROR_SESSION_LOST_STATE if the device cannot recover from
|
||||
* being idle
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_Wake(void);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_IDLE_INTERFACE_H_ */
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @mainpage Widevine TEE Porting Layer API
|
||||
*
|
||||
* The Widevine TEE Porting Interface (WTPI) is a collection of headers that
|
||||
* define the interface between the OPK TA code and system-specific or
|
||||
* hardware-specific features. Together, they define the abstraction layer
|
||||
* between the TA code and the hardware. Each header file defines a set of
|
||||
* related functions. It is your responsibility to provide an implementation for
|
||||
* each WTPI function. The TA will not compile without implementations of all
|
||||
* the WTPI components.
|
||||
*
|
||||
* See the [integration guide](../../../integration#wtpi-components) for a list
|
||||
* of all WTPI components.
|
||||
*/
|
||||
|
||||
/** @defgroup init-term Initialization/Termination
|
||||
*
|
||||
* This interface defines functions that will be called when the OEMCrypto is
|
||||
* initialized or terminated. They can be used to do any initial setup or
|
||||
* cleanup that you need to do for the TA to be operational. It is also
|
||||
* acceptable for these functions to do nothing.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set up for any work needed for initializing the TA-specific components of the
|
||||
* TEE. Returns OEMCrypto_SUCCESS on success and an error, which will be logged,
|
||||
* on failure. */
|
||||
OEMCryptoResult WTPI_Initialize(void);
|
||||
|
||||
/**
|
||||
* Set up for any work needed for terminating the TA-specific components of the
|
||||
* TEE. Returns OEMCrypto_SUCCESS on success and an error, which will be logged,
|
||||
* on failure. */
|
||||
OEMCryptoResult WTPI_Terminate(void);
|
||||
|
||||
/// @}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_INITIALIZE_TERMINATE_INTERFACE_H_ */
|
||||
70
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_logging_interface.h
Normal file
70
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_logging_interface.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_LOGGING_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_LOGGING_INTERFACE_H_
|
||||
|
||||
#include "oemcrypto_api_macros.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup logging Logging
|
||||
*
|
||||
* The OPK TA can log both debug and error messages. Log messages are sent to
|
||||
* this API.
|
||||
*
|
||||
* Turn on debug logging by compiling with the switch -DOPK_IS_DEBUG. A
|
||||
* production device should not have debug logging turned on. If debug logging
|
||||
* is turned on, `OEMCrypto_ProductionReady` will return an error code.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Log priorities that should be logged. LOG_DEBUG should only be logged on
|
||||
* non-production devices.
|
||||
*/
|
||||
typedef enum LogPriority {
|
||||
LOG_NONE = (int)0xf7bd0dc7,
|
||||
LOG_ERROR = (int)0x1098fa73,
|
||||
LOG_DEBUG = (int)0x2b898c5a,
|
||||
} LogPriority;
|
||||
|
||||
/** Log the string at the specified log priority.
|
||||
*
|
||||
* If possible, the file, function and line number should also be
|
||||
* logged. Logging should use the format string using standard printf formatting
|
||||
* and the following parameters.
|
||||
*
|
||||
* @param[in] file: the name of the file containing the log statement.
|
||||
* @param[in] function: the name of the function containing the log statement.
|
||||
* @param[in] line: the line number of the log statement.
|
||||
* @param[in] level: the log level of the log statement.
|
||||
* @param[in] fmt: printf style log format.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 5, 6)))
|
||||
#endif
|
||||
void WTPI_Log(const char* file, const char* function, int line, LogPriority level, const char* fmt, ...);
|
||||
|
||||
/// @}
|
||||
|
||||
#if OPK_IS_DEBUG
|
||||
# define LOGE(...) \
|
||||
WTPI_Log(__FILE__, __func__, __LINE__, LOG_ERROR, __VA_ARGS__)
|
||||
# define LOGD(...) \
|
||||
WTPI_Log(__FILE__, __func__, __LINE__, LOG_DEBUG, __VA_ARGS__)
|
||||
#else
|
||||
# define LOGE(...)
|
||||
# define LOGD(...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_LOGGING_INTERFACE_H_ */
|
||||
109
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_persistent_storage.h
Normal file
109
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_persistent_storage.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_PERSISTENT_STORAGE_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_PERSISTENT_STORAGE_INTERFACE_H_
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup persistent Persistent Storage
|
||||
*
|
||||
* Partners implementing a porting layer may either
|
||||
* 1. Implement this wtpi_persistent_storage.h and
|
||||
* wtpi_clock_interface_layer2.h, and then use the reference implementation
|
||||
* wtpi_clock_and_gn_layer1.c for the clock and generation interfaces. This
|
||||
* is preferred if the hardware secure timer resets to 0 whenever the device
|
||||
* is inactive.
|
||||
* or
|
||||
* 2. Implement both wtpi_clock_interface_layer1.h and
|
||||
* wtpi_generation_number_interface.h. This is preferred if the system has a
|
||||
* hardware secure wall clock.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare to load the stored data. If the data is loaded asynchronously,
|
||||
* this should initialize that process so that the next call to
|
||||
* WTPI_LoadPersistentData does not block for too long.
|
||||
*
|
||||
* See WTPI_PrepareGenerationNumber() for a definition of "too long".
|
||||
*
|
||||
* The values should be stored in secure persistent storage. By *persistent* we
|
||||
* mean that the value should not be changed by shutting down and later
|
||||
* restarting the system. By *secure* we mean that the user should not be able
|
||||
* to modify the values.
|
||||
*
|
||||
* Returns OEMCrypto_SUCCESS on success. On failure, initialization of the TA
|
||||
* will fail.
|
||||
*/
|
||||
OEMCryptoResult WTPI_PrepareStoredPersistentData(void);
|
||||
|
||||
/**
|
||||
* Load the persistent data. This is called once, on first use of the clock or
|
||||
* generation number. This is expected to block until a value is available. It
|
||||
* is the porting interface's responsibility to fail if this blocks too long. In
|
||||
* order to support data layout changes across upgrade, this function should
|
||||
* return less data than requested if necessary. If the available data is less
|
||||
* than |*data_length|, it should fill |data| with the available data and it
|
||||
* should change |*data_length| to be the amount of available data, and it
|
||||
* should return OEMCrypto_SUCCESS. If there is more data available than will
|
||||
* fit in |data|, then it should return OEMCrypto_ERROR_SHORT_BUFFER.
|
||||
*
|
||||
* The layer above this function is responsible for handling data format changes
|
||||
* and backwards compatibility across upgrades.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS data was loaded.
|
||||
* @retval OPK_ERROR_NO_PERSISTENT_DATA No data available.
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER more than |data_length| data available.
|
||||
*
|
||||
* @param[out] data: pointer to persistent data.
|
||||
* @param[in,out] data_length: on input, the size of the data buffer, on out
|
||||
* the amount of data that was loaded.
|
||||
*/
|
||||
OEMCryptoResult WTPI_LoadPersistentData(uint8_t* data, size_t* data_length);
|
||||
|
||||
/**
|
||||
* Save the persistent data.
|
||||
*
|
||||
* If the data is saved asynchronously, then it is OK for the first
|
||||
* call to this function to begin a save process and then return
|
||||
* OEMCrypto_SUCCESS. Subsequent calls will verify that the previous save has
|
||||
* completed successfully. If the previous save resulted in an error, then this
|
||||
* call will return an error. If the previous call has not completed, then this
|
||||
* call should block until the previous save finishes before initializing a new
|
||||
* save. If the previous call was successful, then this call will initialize a
|
||||
* new save and return OEMCrypto_SUCCESS. It is the porting interface's
|
||||
* responsibility to fail if this blocks too long.
|
||||
*
|
||||
* If the data is saved synchronously, then this function should attempt to save
|
||||
* the generation number and return OEMCrypto_SUCCESS if the save was
|
||||
* successful.
|
||||
*
|
||||
* Returns OEMCrypto_SUCCESS on success. Other return values will fail the
|
||||
* current attempt to save the usage table. If a failure actually indicates that
|
||||
* the previous save had failed, then the usage table will be saved with a
|
||||
* generation number skew of 1. When the usage table is loaded with a generation
|
||||
* skew of 1, it will result in a warning, but not a failure.
|
||||
*
|
||||
*
|
||||
* @param[in] data: persistent data.
|
||||
* @param[in] data_length: the amount of data to save.
|
||||
*/
|
||||
OEMCryptoResult WTPI_StorePersistentData(const uint8_t* data,
|
||||
size_t data_length);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_PERSISTENT_STORAGE_INTERFACE_H_ */
|
||||
@@ -0,0 +1,129 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_
|
||||
#define OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup layer1-rot Layer 1 Root Of Trust
|
||||
* Keybox access functions called directly by OPK code.
|
||||
*
|
||||
* This is the top layer of the porting layer. The OPK directly calls
|
||||
* functions in this file. Partners have the option to implement these functions
|
||||
* directly, or use the reference version of the layer 1 functions, and instead
|
||||
* implement the layer 2 functions.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Initialize all necessary data. */
|
||||
OEMCryptoResult WTPI_InitializeKeybox(void);
|
||||
|
||||
/** Terminate anything that needs terminating. */
|
||||
OEMCryptoResult WTPI_TerminateKeybox(void);
|
||||
|
||||
/**
|
||||
* Unwrap and store the buffer in non-persistent memory reserved for ROT. This
|
||||
* is used once in the factory to install the real keybox. If possible, in order
|
||||
* to avoid buffer overflow attacks, we recommend partners to keep this separate
|
||||
* from the device key in the keybox, so that when this accessed, the device key
|
||||
* is not exposed. It may be wise to make this function a no-op when the device
|
||||
* is not in factory mode.
|
||||
*
|
||||
* @param[in] input: keybox to be installed
|
||||
* @param[in] length: size of the keybox
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |input| is NULL
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if the input length is too small
|
||||
*/
|
||||
OEMCryptoResult WTPI_UnwrapValidateAndInstallKeybox(const uint8_t* input,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* Attempt to validate the current keybox loaded.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_BAD_MAGIC if magic field is not "kbox"
|
||||
* @retval OEMCrypto_ERROR_BAD_CRC if computed CRC is not equivalent to stored
|
||||
* CRC
|
||||
*/
|
||||
OEMCryptoResult WTPI_ValidateKeybox(void);
|
||||
|
||||
/**
|
||||
* Get the 72 byte encrypted key data from the current keybox. |key_data| must
|
||||
* be >= 72 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] key_data: output test key data
|
||||
* @param[in] length: size of the key data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |key_data| is NULL
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetKeyDataFromKeybox(uint8_t* key_data, size_t length);
|
||||
|
||||
/**
|
||||
* Load a test keybox to be used until the next OEMCrypto_Terminate call.
|
||||
* |test_keybox| must be >= 128 bytes.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] test_keybox: output test keybox
|
||||
* @param[in] length: size of the keybox
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |test_keybox| is NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_LoadTestKeybox(const uint8_t* test_keybox, size_t length);
|
||||
|
||||
/**
|
||||
* Create a key handle from the device key in the keybox. The caller is
|
||||
* responsible for calling WTPI_K1_FreeKeyHandle on out when the key is no
|
||||
* longer needed.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] out: output key handle
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |out| is NULL
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_K1_CreateKeyHandleFromKeybox(
|
||||
WTPI_K1_SymmetricKey_Handle* out);
|
||||
|
||||
/**
|
||||
* Gets the device id from the current provisioning method. If the keybox is
|
||||
* used, sets |device_id| with the device id in keybox.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[out] device_id: output device ID
|
||||
* @param[in] device_id_length: size of device ID
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT if |device_id| is NULL, or
|
||||
* |device_id_length| is smaller than KEYBOX_DEVICE_ID_SIZE
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetDeviceIDFromKeybox(uint8_t* device_id,
|
||||
size_t device_id_length);
|
||||
|
||||
/// @}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER1_H_ */
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_
|
||||
#define OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup layer2-rot Layer 2 Root Of Trust
|
||||
* Hardware level access to root of trust.
|
||||
*
|
||||
* These functions provide a simple interface to the hardware secure persistent
|
||||
* storage. Partners may choose to implement these functions and use the layer
|
||||
* 1 code provided by Widevine to present a higher level interface.
|
||||
*
|
||||
* These functions should be easier to implement, but using the layer 1
|
||||
* functions may reduce performance or security.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Use the system specific key to unwrap the root of trust buffer. This
|
||||
* function is used in the factory.
|
||||
*
|
||||
* @param[in] input: buffer with encrypted root of trust.
|
||||
* @param[in] input_length: the length of the input.
|
||||
* @param[out] output: buffer where clear root of trust will be written.
|
||||
* @param[in,out] output_length: on input, it's the buffer size, on output it's
|
||||
* how much was unwrapped. If output_length is too short for the entire root
|
||||
* of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be returned.
|
||||
*/
|
||||
OEMCryptoResult WTPI_UnwrapRootOfTrust(const uint8_t* input,
|
||||
size_t input_length, uint8_t* output,
|
||||
size_t* output_length);
|
||||
|
||||
/** Save the buffer to secure permanent storage.
|
||||
* To prevent accidentally destroying the keybox, production devices should
|
||||
* return an error code if this function is called when the device is not in
|
||||
* "factory mode".
|
||||
*
|
||||
*@param[in] keybox: buffer of the keybox.
|
||||
*@param[in] length: on input, it's the buffer size, on output it's the actual
|
||||
* size loaded into the buffer. If length is too short for the entire root
|
||||
* of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be returned.
|
||||
* Otherwise, set length to the size of the root of trust loaded.
|
||||
*/
|
||||
OEMCryptoResult WTPI_SaveRootOfTrust(const uint8_t* keybox, size_t length);
|
||||
|
||||
/** Read the buffer from secure permanent storage.
|
||||
*
|
||||
*@param[in] keybox: buffer of the keybox.
|
||||
*@param[in,out] length: on input, it's the buffer size, on output it's the
|
||||
* actual size loaded into the buffer. If length is too short for the entire
|
||||
* root of trust, the error code OEMCrypto_ERROR_SHORT_BUFFER shall be
|
||||
* returned. Otherwise, set length to the size of the root of trust loaded.
|
||||
*/
|
||||
OEMCryptoResult WTPI_LoadRootOfTrust(uint8_t* keybox, size_t* length);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_ROOT_OF_TRUST_INTERFACE_LAYER2_H_ */
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_
|
||||
#define OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup secure-buffer Secure Buffer Access
|
||||
*
|
||||
* Interface used by the reference
|
||||
* [sample decryption interface](@ref decrypt-sample) to access secure buffers.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the secure buffer address given a pointer to the secure memory with an
|
||||
* offset, and places it in |out_addr|.
|
||||
*
|
||||
* Caller retains ownership of all pointers.
|
||||
*
|
||||
* @param[in] secure: handle to the secure buffer
|
||||
* @param[in] offset: offset in the secure buffer
|
||||
* @param[out] out_addr: output secure address
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT any of the parameters are NULL or
|
||||
* invalid
|
||||
*/
|
||||
OEMCryptoResult WTPI_GetSecureBufferAddress(void* secure, size_t offset,
|
||||
uint8_t** out_addr);
|
||||
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_SECURE_BUFFER_ACCESS_H_ */
|
||||
284
oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c
Normal file
284
oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "cose_util.h"
|
||||
|
||||
#include "dice/cbor_writer.h"
|
||||
#include "dice/config.h"
|
||||
#include "dice/dice.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
|
||||
/******************************************************************************
|
||||
The following are copied from open-dice library cbor_cert_op.c
|
||||
*******************************************************************************/
|
||||
|
||||
#if DICE_PUBLIC_KEY_SIZE != 32
|
||||
# error "Only Ed25519 is supported; 32 bytes needed to store the public key."
|
||||
#endif
|
||||
#if DICE_SIGNATURE_SIZE != 64
|
||||
# error "Only Ed25519 is supported; 64 bytes needed to store the signature."
|
||||
#endif
|
||||
|
||||
// Max size of the COSE_Sign1 protected attributes.
|
||||
#define DICE_MAX_PROTECTED_ATTRIBUTES_SIZE 16
|
||||
|
||||
static DiceResult DiceCoseEncodePublicKey(
|
||||
void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
|
||||
size_t buffer_size, uint8_t* buffer, size_t* encoded_size) {
|
||||
(void)context_not_used;
|
||||
|
||||
// Constants per RFC 8152.
|
||||
const int64_t kCoseKeyKtyLabel = 1;
|
||||
const int64_t kCoseKeyAlgLabel = 3;
|
||||
const int64_t kCoseKeyOpsLabel = 4;
|
||||
const int64_t kCoseOkpCrvLabel = -1;
|
||||
const int64_t kCoseOkpXLabel = -2;
|
||||
const int64_t kCoseKeyTypeOkp = 1;
|
||||
const int64_t kCoseAlgEdDSA = -8;
|
||||
const int64_t kCoseKeyOpsVerify = 2;
|
||||
const int64_t kCoseCrvEd25519 = 6;
|
||||
|
||||
struct CborOut out;
|
||||
CborOutInit(buffer, buffer_size, &out);
|
||||
CborWriteMap(/*num_pairs=*/5, &out);
|
||||
// Add the key type.
|
||||
CborWriteInt(kCoseKeyKtyLabel, &out);
|
||||
CborWriteInt(kCoseKeyTypeOkp, &out);
|
||||
// Add the algorithm.
|
||||
CborWriteInt(kCoseKeyAlgLabel, &out);
|
||||
CborWriteInt(kCoseAlgEdDSA, &out);
|
||||
// Add the KeyOps.
|
||||
CborWriteInt(kCoseKeyOpsLabel, &out);
|
||||
CborWriteArray(/*num_elements=*/1, &out);
|
||||
CborWriteInt(kCoseKeyOpsVerify, &out);
|
||||
// Add the curve.
|
||||
CborWriteInt(kCoseOkpCrvLabel, &out);
|
||||
CborWriteInt(kCoseCrvEd25519, &out);
|
||||
// Add the public key.
|
||||
CborWriteInt(kCoseOkpXLabel, &out);
|
||||
CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE, public_key, &out);
|
||||
if (CborOutOverflowed(&out)) {
|
||||
return kDiceResultBufferTooSmall;
|
||||
}
|
||||
*encoded_size = CborOutSize(&out);
|
||||
return kDiceResultOk;
|
||||
}
|
||||
|
||||
static DiceResult EncodeProtectedAttributes(size_t buffer_size, uint8_t* buffer,
|
||||
size_t* encoded_size) {
|
||||
// Constants per RFC 8152.
|
||||
const int64_t kCoseHeaderAlgLabel = 1;
|
||||
const int64_t kCoseAlgEdDSA = -8;
|
||||
|
||||
struct CborOut out;
|
||||
CborOutInit(buffer, buffer_size, &out);
|
||||
CborWriteMap(/*num_pairs=*/1, &out);
|
||||
// Add the algorithm.
|
||||
CborWriteInt(kCoseHeaderAlgLabel, &out);
|
||||
CborWriteInt(kCoseAlgEdDSA, &out);
|
||||
if (CborOutOverflowed(&out)) {
|
||||
return kDiceResultBufferTooSmall;
|
||||
}
|
||||
*encoded_size = CborOutSize(&out);
|
||||
return kDiceResultOk;
|
||||
}
|
||||
|
||||
static DiceResult EncodeCoseTbs(const uint8_t* protected_attributes,
|
||||
size_t protected_attributes_size,
|
||||
const uint8_t* payload, size_t payload_size,
|
||||
const uint8_t* aad, size_t aad_size,
|
||||
size_t buffer_size, uint8_t* buffer,
|
||||
size_t* encoded_size) {
|
||||
struct CborOut out;
|
||||
CborOutInit(buffer, buffer_size, &out);
|
||||
// TBS is an array of four elements.
|
||||
CborWriteArray(/*num_elements=*/4, &out);
|
||||
// Context string field.
|
||||
CborWriteTstr("Signature1", &out);
|
||||
// Protected attributes from COSE_Sign1.
|
||||
CborWriteBstr(protected_attributes_size, protected_attributes, &out);
|
||||
// Additional authenticated data.
|
||||
CborWriteBstr(aad_size, aad, &out);
|
||||
// Payload from COSE_Sign1.
|
||||
CborWriteBstr(payload_size, payload, &out);
|
||||
if (CborOutOverflowed(&out)) {
|
||||
return kDiceResultBufferTooSmall;
|
||||
}
|
||||
*encoded_size = CborOutSize(&out);
|
||||
return kDiceResultOk;
|
||||
}
|
||||
|
||||
static DiceResult EncodeCoseSign1(const uint8_t* protected_attributes,
|
||||
size_t protected_attributes_size,
|
||||
const uint8_t* payload, size_t payload_size,
|
||||
const uint8_t signature[DICE_SIGNATURE_SIZE],
|
||||
size_t buffer_size, uint8_t* buffer,
|
||||
size_t* encoded_size) {
|
||||
struct CborOut out;
|
||||
CborOutInit(buffer, buffer_size, &out);
|
||||
// COSE_Sign1 is an array of four elements.
|
||||
CborWriteArray(/*num_elements=*/4, &out);
|
||||
// Protected attributes.
|
||||
CborWriteBstr(protected_attributes_size, protected_attributes, &out);
|
||||
// Empty map for unprotected attributes.
|
||||
CborWriteMap(/*num_pairs=*/0, &out);
|
||||
// Payload.
|
||||
CborWriteBstr(payload_size, payload, &out);
|
||||
// Signature.
|
||||
CborWriteBstr(/*num_elements=*/DICE_SIGNATURE_SIZE, signature, &out);
|
||||
if (CborOutOverflowed(&out)) {
|
||||
return kDiceResultBufferTooSmall;
|
||||
}
|
||||
*encoded_size = CborOutSize(&out);
|
||||
return kDiceResultOk;
|
||||
}
|
||||
/******************************************************************************
|
||||
The codes above are copied from open-dice library cbor_cert_op.c
|
||||
*******************************************************************************/
|
||||
|
||||
OEMCryptoResult DiceCoseSignAndEncodeSign1(
|
||||
const uint8_t* payload, size_t payload_size,
|
||||
WTPI_AsymmetricKey_Handle private_key, size_t buffer_size, uint8_t* buffer,
|
||||
size_t* encoded_size) {
|
||||
if (payload == NULL || payload_size == 0 || private_key == NULL ||
|
||||
buffer_size == 0 || buffer == NULL || encoded_size == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
*encoded_size = 0;
|
||||
|
||||
// The encoded protected attributes are used in the TBS and the final
|
||||
// COSE_Sign1 structure.
|
||||
uint8_t protected_attributes[DICE_MAX_PROTECTED_ATTRIBUTES_SIZE];
|
||||
size_t protected_attributes_size = 0;
|
||||
DiceResult dice_result = EncodeProtectedAttributes(
|
||||
sizeof(protected_attributes), protected_attributes,
|
||||
&protected_attributes_size);
|
||||
if (dice_result != kDiceResultOk) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
// Construct a To-Be-Signed (TBS) structure based on the relevant fields of
|
||||
// the COSE_Sign1.
|
||||
dice_result = EncodeCoseTbs(
|
||||
protected_attributes, protected_attributes_size, payload, payload_size,
|
||||
/*aad=*/NULL, /*aad_size=*/0, buffer_size, buffer, encoded_size);
|
||||
if (dice_result != kDiceResultOk) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
// Sign the TBS with the authority key.
|
||||
uint8_t signature[DICE_SIGNATURE_SIZE];
|
||||
size_t signature_length = sizeof(signature);
|
||||
OEMCryptoResult result = WTPI_ED25519Sign(private_key, buffer, *encoded_size,
|
||||
signature, &signature_length);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// The final certificate is an untagged COSE_Sign1 structure.
|
||||
dice_result = EncodeCoseSign1(protected_attributes, protected_attributes_size,
|
||||
payload, payload_size, signature, buffer_size,
|
||||
buffer, encoded_size);
|
||||
if (dice_result != kDiceResultOk) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult GenerateEncodedBccPayload(
|
||||
const uint8_t* encoded_public_key, size_t encoded_public_key_size,
|
||||
uint8_t* out, size_t* out_length) {
|
||||
ABORT_IF_NULL(encoded_public_key);
|
||||
ABORT_IF_ZERO(encoded_public_key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
ABORT_IF_NULL(out_length);
|
||||
ABORT_IF_ZERO(*out_length);
|
||||
|
||||
const int64_t kSubjectPublicKeyLabel = -4670552;
|
||||
const int64_t kKeyUsageLabel = -4670553;
|
||||
const uint8_t kKeyUsageCertSign = 32;
|
||||
|
||||
struct CborOut cbor_out;
|
||||
CborOutInit(out, *out_length, &cbor_out);
|
||||
CborWriteMap(/*num_pairs=*/2, &cbor_out);
|
||||
// Add the subject public key.
|
||||
CborWriteInt(kSubjectPublicKeyLabel, &cbor_out);
|
||||
CborWriteBstr(encoded_public_key_size, encoded_public_key, &cbor_out);
|
||||
// Add the key usage.
|
||||
CborWriteInt(kKeyUsageLabel, &cbor_out);
|
||||
CborWriteBstr(/*data_size=*/1, &kKeyUsageCertSign, &cbor_out);
|
||||
if (CborOutOverflowed(&cbor_out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
*out_length = CborOutSize(&cbor_out);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
// The minimum buffer length required to hold the generated BCC. The generated
|
||||
// BCC should have a fixed size in phase 1. In this implementation, it happens
|
||||
// to be 180 bytes.
|
||||
#define BCC_TOTAL_LENGTH 180
|
||||
|
||||
OEMCryptoResult BuildBootCertificateChain(const uint8_t* public_key,
|
||||
size_t public_key_length,
|
||||
AsymmetricKeyType key_type,
|
||||
WTPI_AsymmetricKey_Handle private_key,
|
||||
uint8_t* out, size_t* out_length) {
|
||||
if (public_key == NULL || out_length == NULL || private_key == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (out == NULL || *out_length < BCC_TOTAL_LENGTH) {
|
||||
*out_length = BCC_TOTAL_LENGTH;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
// Can only support ED25519 key.
|
||||
if (key_type != PROV40_ED25519_PRIVATE_KEY ||
|
||||
public_key_length != DICE_PUBLIC_KEY_SIZE) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Bcc = [
|
||||
// COSE_Key, ; Root public key
|
||||
// + COSE_Sign1, ; Bcc entries. Only one self-signed cert in phase 1.
|
||||
// ];
|
||||
// Write BCC array header.
|
||||
struct CborOut cbor_out;
|
||||
CborOutInit(out, *out_length, &cbor_out);
|
||||
CborWriteArray(/*num_elements=*/2, &cbor_out);
|
||||
if (CborOutOverflowed(&cbor_out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
size_t out_cursor = CborOutSize(&cbor_out);
|
||||
|
||||
// Encode first entry in the array which is a COSE_Key.
|
||||
// The encoded public key will be copied to COSE_Sign1 later.
|
||||
const uint8_t* encoded_public_key = out + out_cursor;
|
||||
size_t encoded_public_key_size = 0;
|
||||
DiceResult dice_result = DiceCoseEncodePublicKey(
|
||||
/*context_not_used=*/NULL, public_key, *out_length - out_cursor,
|
||||
out + out_cursor, &encoded_public_key_size);
|
||||
if (dice_result != kDiceResultOk) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
out_cursor += encoded_public_key_size;
|
||||
|
||||
// Encode second entry in the array which is a COSE_Sign1.
|
||||
// The payload can not exceed total length.
|
||||
uint8_t bcc_payload[BCC_TOTAL_LENGTH];
|
||||
size_t bcc_payload_size = sizeof(bcc_payload);
|
||||
OEMCryptoResult result =
|
||||
GenerateEncodedBccPayload(encoded_public_key, encoded_public_key_size,
|
||||
bcc_payload, &bcc_payload_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
size_t encoded_cose_sign1_size = 0;
|
||||
result = DiceCoseSignAndEncodeSign1(
|
||||
bcc_payload, bcc_payload_size, private_key, *out_length - out_cursor,
|
||||
out + out_cursor, &encoded_cose_sign1_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
out_cursor += encoded_cose_sign1_size;
|
||||
|
||||
*out_length = out_cursor;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
33
oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.h
Normal file
33
oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_COSE_UTIL_H_
|
||||
#define OEMCRYPTO_TA_COSE_UTIL_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "wtpi_crypto_asymmetric_interface.h"
|
||||
|
||||
/**
|
||||
* Generate a COSE_SIGN1 format signature of |payload| with |private_key| and
|
||||
* wirte the signature to |buffer|. Only ED25519 key is currently supported.
|
||||
* Caller retains ownership of all pointers and they must not be NULL.
|
||||
*/
|
||||
OEMCryptoResult DiceCoseSignAndEncodeSign1(
|
||||
const uint8_t* payload, size_t payload_size,
|
||||
WTPI_AsymmetricKey_Handle private_key, size_t buffer_size, uint8_t* buffer,
|
||||
size_t* encoded_size);
|
||||
|
||||
/**
|
||||
* Build a self signed boot certificate chain (BCC) with the provided
|
||||
* |public_key| and |private_key|, and write the BCC to |out|. Only ED25519 key
|
||||
* is currently supported. Caller retains ownership of all pointers and they
|
||||
* must not be NULL.
|
||||
*/
|
||||
OEMCryptoResult BuildBootCertificateChain(const uint8_t* public_key,
|
||||
size_t public_key_length,
|
||||
AsymmetricKeyType key_type,
|
||||
WTPI_AsymmetricKey_Handle private_key,
|
||||
uint8_t* out, size_t* out_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_COSE_UTIL_H_ */
|
||||
186
oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.c
Normal file
186
oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "crypto_util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
static bool AESEncryptBlock(const uint8_t* in, const uint8_t* key,
|
||||
size_t key_size, uint8_t* out) {
|
||||
ABORT_IF_NULL(in);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(key, (unsigned int)(key_size * 8), &aes_key);
|
||||
AES_encrypt(in, out, &aes_key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_AESCBCEncrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out) {
|
||||
ABORT_IF_NULL(in);
|
||||
ABORT_IF_ZERO(in_length);
|
||||
ABORT_IF_NULL(iv);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(key, (unsigned int)(key_size * 8), &aes_key);
|
||||
uint8_t iv_buffer[KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, iv, KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(in, out, in_length, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_AESCBCDecrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out) {
|
||||
ABORT_IF_NULL(in);
|
||||
ABORT_IF_ZERO(in_length);
|
||||
ABORT_IF_NULL(iv);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key(key, (unsigned int)(key_size * 8), &aes_key);
|
||||
uint8_t iv_buffer[KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, iv, KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(in, out, in_length, &aes_key, iv_buffer, AES_DECRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_AESCTRDecrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size,
|
||||
size_t block_offset, uint8_t* out) {
|
||||
ABORT_IF_NULL(in);
|
||||
ABORT_IF_ZERO(in_length);
|
||||
ABORT_IF_NULL(iv);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
ABORT_IF(block_offset >= AES_BLOCK_SIZE, "Invalid block offset");
|
||||
|
||||
/* TODO(b/171430591): There's no reason we should reimplement part of AES-CTR
|
||||
ourselves when BoringSSL can just do it for us. */
|
||||
uint8_t current_iv[AES_BLOCK_SIZE];
|
||||
memcpy(current_iv, &iv[0], AES_BLOCK_SIZE);
|
||||
size_t l = 0;
|
||||
while (l < in_length) {
|
||||
uint8_t aes_output[AES_BLOCK_SIZE];
|
||||
if (!AESEncryptBlock(current_iv, key, key_size, aes_output)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t n = block_offset; n < AES_BLOCK_SIZE && l < in_length;
|
||||
++n, ++l) {
|
||||
out[l] = aes_output[n] ^ in[l];
|
||||
}
|
||||
/* Increment the lower 8 bytes of the current_iv only. */
|
||||
for (size_t n = 15; n > 7; --n) {
|
||||
if (++current_iv[n] != 0) break;
|
||||
}
|
||||
block_offset = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_DeriveKeyWithCMAC(const uint8_t* key, KeySize key_size,
|
||||
uint8_t counter, const uint8_t* context,
|
||||
size_t context_length, KeySize out_key_size,
|
||||
uint8_t* out) {
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_NULL(context);
|
||||
ABORT_IF_ZERO(context_length);
|
||||
ABORT_IF_NULL(out);
|
||||
ABORT_IF((key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) ||
|
||||
(out_key_size != KEY_SIZE_128 && out_key_size != KEY_SIZE_256),
|
||||
"Invalid key size");
|
||||
|
||||
const EVP_CIPHER* cipher;
|
||||
switch (key_size) {
|
||||
case KEY_SIZE_128:
|
||||
cipher = EVP_aes_128_cbc();
|
||||
break;
|
||||
case KEY_SIZE_256:
|
||||
cipher = EVP_aes_256_cbc();
|
||||
break;
|
||||
default:
|
||||
ABORT("Invalid key size");
|
||||
}
|
||||
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
if (!cmac_ctx) return false;
|
||||
|
||||
for (uint8_t index = 0; index < out_key_size; index += KEY_SIZE_128) {
|
||||
if (!CMAC_Init(cmac_ctx, key, key_size, cipher, 0)) {
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CMAC_Update(cmac_ctx, &counter, sizeof(counter))) {
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
counter++;
|
||||
|
||||
if (!CMAC_Update(cmac_ctx, context, context_length)) {
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
size_t reslen = 0;
|
||||
if (!CMAC_Final(cmac_ctx, out + index, &reslen)) {
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
if (reslen != KEY_SIZE_128) {
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_HMAC_SHA1(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out) {
|
||||
ABORT_IF_NULL(message);
|
||||
ABORT_IF_ZERO(message_length);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
|
||||
unsigned int md_len = SHA_DIGEST_LENGTH;
|
||||
if (!HMAC(EVP_sha1(), key, (size_t)key_size, message, message_length, out,
|
||||
&md_len)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OPKI_HMAC_SHA256(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out) {
|
||||
ABORT_IF_NULL(message);
|
||||
ABORT_IF_ZERO(message_length);
|
||||
ABORT_IF_NULL(key);
|
||||
ABORT_IF_ZERO(key_size);
|
||||
ABORT_IF_NULL(out);
|
||||
|
||||
unsigned int md_len = SHA256_DIGEST_LENGTH;
|
||||
if (!HMAC(EVP_sha256(), key, (size_t)key_size, message, message_length, out,
|
||||
&md_len)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
66
oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.h
Normal file
66
oemcrypto/opk/oemcrypto_ta/wtpi_reference/crypto_util.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_CRYPTO_UTIL_H_
|
||||
#define OEMCRYPTO_TA_CRYPTO_UTIL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
/**
|
||||
* Encrypts |in_length| bytes of |in| using AES CBC and |iv| and places the
|
||||
* result in |out|. |key_size| is the size in bytes.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_AESCBCEncrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out);
|
||||
|
||||
/**
|
||||
* Decrypts |in_length| bytes of |in| using AES CBC and |iv| and places the
|
||||
* result in |out|. |key_size| is the size in bytes.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_AESCBCDecrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out);
|
||||
|
||||
/**
|
||||
* Decrypts |in_length| bytes of |in| using AES CTR and |iv| and places the
|
||||
* result in |out|. |key_size| is the size in bytes.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_AESCTRDecrypt(const uint8_t* in, size_t in_length, const uint8_t* iv,
|
||||
const uint8_t* key, size_t key_size,
|
||||
size_t block_offset, uint8_t* out);
|
||||
|
||||
/**
|
||||
* Calculates a 16-byte or 32-byte CMAC authentication code using |key| and
|
||||
* |context| and places the resulting key in |out|. |key_size| must be either
|
||||
* KEY_SIZE_128 or KEY_SIZE_256.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_DeriveKeyWithCMAC(const uint8_t* key, KeySize key_size,
|
||||
uint8_t counter, const uint8_t* context,
|
||||
size_t context_length, KeySize out_key_size,
|
||||
uint8_t* out);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |message_length| bytes of |message| using SHA1 as the
|
||||
* hash function and places the result in |out|.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_HMAC_SHA1(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out);
|
||||
|
||||
/**
|
||||
* Calculates the HMAC of |message_length| bytes of |message| using SHA256 as
|
||||
* the hash function and places the result in |out|.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
bool OPKI_HMAC_SHA256(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* key, size_t key_size, uint8_t* out);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_CRYPTO_UTIL_H_ */
|
||||
335
oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.c
Normal file
335
oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.c
Normal file
@@ -0,0 +1,335 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "ecc_util.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/crypto.h"
|
||||
#include "openssl/ec.h"
|
||||
#include "openssl/ecdsa.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "rsa_util.h"
|
||||
#include "string.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
/* The curve secp256r1 was originally named prime256v1 in the X9.62
|
||||
specification. Renaming to match server naming conventions. */
|
||||
#define NIST_P256 NID_X9_62_prime256v1 /* secp256r1 */
|
||||
#define NIST_P384 NID_secp384r1
|
||||
#define NIST_P521 NID_secp521r1
|
||||
|
||||
/* ECC DRM keys generate a 256-bit AES-CMAC key for the derivation of
|
||||
other message keys. */
|
||||
#define ECC_SESSION_KEY_SIZE KEY_SIZE_256
|
||||
|
||||
/* Returns the OpenSSL defined curve ID. Otherwise 0 is returned.
|
||||
Caller may assume that any error details will be logged. */
|
||||
static int GetCurveId(const EC_KEY* key) {
|
||||
const EC_GROUP* group = EC_KEY_get0_group(key);
|
||||
if (group == NULL) {
|
||||
LOGE("EC_KEY_get0_group returned NULL");
|
||||
dump_ssl_error();
|
||||
return 0;
|
||||
}
|
||||
const int curve = EC_GROUP_get_curve_name(group);
|
||||
switch (curve) {
|
||||
case 0: /* No NID or error */
|
||||
/* This is possible if the provided deserialized key used deprecated
|
||||
curve parameters. Although it is possible that the curve parameters
|
||||
are valid, keys using curve parameters should be rejected. */
|
||||
LOGE("Key does not have named curve");
|
||||
dump_ssl_error();
|
||||
return 0;
|
||||
case NIST_P256:
|
||||
case NIST_P384:
|
||||
case NIST_P521:
|
||||
return curve;
|
||||
default: /* some other curve */
|
||||
LOGE("Unsupported key curve: nid = %d", curve);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckECCKey(const EC_KEY* ecc_key) {
|
||||
ABORT_IF(ecc_key == NULL, "ecc_key is NULL");
|
||||
if (EC_KEY_check_key(ecc_key) == 1) return true;
|
||||
LOGE("ECC key not valid");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeserializeECCPrivateKey(const uint8_t* serialized_bytes, size_t size,
|
||||
EC_KEY** ecc_key) {
|
||||
ABORT_IF(serialized_bytes == NULL || size <= 0 || ecc_key == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
BIO* bio = BIO_new_mem_buf(serialized_bytes, (int)size);
|
||||
if (bio == NULL) {
|
||||
LOGE("Failed to allocate BIO buffer");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
/* Step 1: Deserializes PKCS8 PrivateKeyInfo containing an ECC key. */
|
||||
PKCS8_PRIV_KEY_INFO* priv_info = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
||||
BIO_free(bio);
|
||||
bio = NULL;
|
||||
if (priv_info == NULL) {
|
||||
LOGE("Failed to parse private key");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
/* Step 2: Convert to EC_KEY. */
|
||||
EVP_PKEY* pkey = EVP_PKCS82PKEY(priv_info);
|
||||
PKCS8_PRIV_KEY_INFO_free(priv_info);
|
||||
priv_info = NULL;
|
||||
if (pkey == NULL) {
|
||||
LOGE("Failed to convert PKCS8 to EVP");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
EC_KEY* key = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
if (key == NULL) {
|
||||
LOGE("Failed to get ECC key");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
/* Step 3: Verify key parameters and curve family. */
|
||||
const int check = EC_KEY_check_key(key);
|
||||
if (check == 0) {
|
||||
LOGE("ECC key parameters are invalid");
|
||||
EC_KEY_free(key);
|
||||
return false;
|
||||
} else if (check == -1) {
|
||||
LOGE("Failed to check ECC key");
|
||||
dump_ssl_error();
|
||||
EC_KEY_free(key);
|
||||
return false;
|
||||
}
|
||||
if (GetCurveId(key) == 0) {
|
||||
/* GetCurveId() will print any error logs */
|
||||
EC_KEY_free(key);
|
||||
return false;
|
||||
}
|
||||
/* Required for IETF compliance. Only required for if re-serializing. */
|
||||
EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED);
|
||||
*ecc_key = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECCSignECDSA(EC_KEY* ecc_key, const uint8_t* message,
|
||||
size_t message_length, uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
ABORT_IF(ecc_key == NULL || message == NULL || message_length <= 0 ||
|
||||
signature == NULL || signature_length == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
const size_t expected_signature_length = ECDSA_size(ecc_key);
|
||||
ABORT_IF(*signature_length < expected_signature_length,
|
||||
"signature_length is not long enough");
|
||||
ABORT_IF(*signature_length > UINT_MAX, "signature_length is too long");
|
||||
/* Hash the message using the appropriate hash for the given curve.
|
||||
SHA-512 is the largest hash of the expected curve. */
|
||||
const int curve = GetCurveId(ecc_key);
|
||||
ABORT_IF(curve == 0, "Invalid ECC key");
|
||||
uint8_t hash[SHA512_DIGEST_LENGTH];
|
||||
size_t hash_length = 0;
|
||||
const uint8_t* hash_res = NULL;
|
||||
switch (curve) {
|
||||
case NIST_P256:
|
||||
hash_res = SHA256(message, message_length, hash);
|
||||
hash_length = SHA256_DIGEST_LENGTH;
|
||||
break;
|
||||
case NIST_P384:
|
||||
hash_res = SHA384(message, message_length, hash);
|
||||
hash_length = SHA384_DIGEST_LENGTH;
|
||||
break;
|
||||
case NIST_P521:
|
||||
hash_res = SHA512(message, message_length, hash);
|
||||
hash_length = SHA512_DIGEST_LENGTH;
|
||||
break;
|
||||
}
|
||||
if (hash_res == NULL || hash_length == 0) {
|
||||
LOGE("Failed to hash message");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
/* Compute ECDSA point */
|
||||
unsigned int actual_signature_length = (unsigned int)*signature_length;
|
||||
if (!ECDSA_sign(/* type = DEFAULT */ 0, hash, hash_length, signature,
|
||||
&actual_signature_length, ecc_key)) {
|
||||
LOGE("Failed to generate signature");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
*signature_length = (size_t)actual_signature_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This KDF function is defined by OEMCrypto ECC specification.
|
||||
Function signature is based on the |kdf| parameter of
|
||||
ECDH_compute_key(). This function assumes that all pointer
|
||||
parameters are not null. */
|
||||
static void* WidevineEccKdf(const void* secret, size_t secret_length, void* key,
|
||||
size_t* key_size) {
|
||||
ABORT_IF(*key_size < SHA256_DIGEST_LENGTH,
|
||||
"key_size should be large enough for SHA256 digest");
|
||||
if (SHA256((const uint8_t*)secret, secret_length, key) == NULL) {
|
||||
dump_ssl_error();
|
||||
return NULL;
|
||||
}
|
||||
*key_size = ECC_SESSION_KEY_SIZE;
|
||||
return key;
|
||||
}
|
||||
|
||||
bool ECCWidevineECDHSessionKey(EC_KEY* ecc_key, const uint8_t* key_source,
|
||||
size_t key_source_length, uint8_t* session_key,
|
||||
size_t* session_key_length) {
|
||||
ABORT_IF(ecc_key == NULL || key_source == NULL || key_source_length <= 0 ||
|
||||
session_key == NULL || session_key_length == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
ABORT_IF(*session_key_length < ECC_SESSION_KEY_SIZE,
|
||||
"session_key_length is not long enough");
|
||||
/* First deserialize the |key_source|. */
|
||||
const uint8_t* tp = key_source;
|
||||
EC_KEY* public_key = d2i_EC_PUBKEY(NULL, &tp, key_source_length);
|
||||
if (public_key == NULL) {
|
||||
LOGE("d2i_EC_PUBKEY return NULL");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
EC_KEY_set_conv_form(public_key, POINT_CONVERSION_UNCOMPRESSED);
|
||||
/* Verify it is of the same curve. */
|
||||
const int private_curve = GetCurveId(ecc_key);
|
||||
ABORT_IF(private_curve == 0, "Invalid ECC key");
|
||||
const int public_curve = GetCurveId(public_key);
|
||||
if (public_curve != private_curve) {
|
||||
LOGE("key_source is incompatible for ECDH: private = %d, public = %d",
|
||||
private_curve, public_curve);
|
||||
EC_KEY_free(public_key);
|
||||
return false;
|
||||
}
|
||||
/* Compute ECDH using Widevine KDF. */
|
||||
const int ecdh_res = ECDH_compute_key(session_key, ECC_SESSION_KEY_SIZE,
|
||||
EC_KEY_get0_public_key(public_key),
|
||||
ecc_key, WidevineEccKdf);
|
||||
EC_KEY_free(public_key);
|
||||
if (ecdh_res != ECC_SESSION_KEY_SIZE) {
|
||||
LOGE("Unexpected ECDH_compute_key result: %d", ecdh_res);
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
*session_key_length = ECC_SESSION_KEY_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SerializeEccPrivateKey(EC_KEY* ec_key, uint8_t* key_data,
|
||||
size_t* key_data_length) {
|
||||
if (ec_key == NULL || key_data == NULL || key_data_length == NULL) {
|
||||
return false;
|
||||
}
|
||||
EVP_PKEY* evp_key = NULL;
|
||||
PKCS8_PRIV_KEY_INFO* priv_info = NULL;
|
||||
BIO* bio = NULL;
|
||||
|
||||
evp_key = EVP_PKEY_new();
|
||||
if (evp_key == NULL || EVP_PKEY_set1_EC_KEY(evp_key, ec_key) != 1) {
|
||||
goto cleanup;
|
||||
}
|
||||
priv_info = EVP_PKEY2PKCS8(evp_key);
|
||||
if (priv_info == NULL) {
|
||||
goto cleanup;
|
||||
}
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (bio == NULL || i2d_PKCS8_PRIV_KEY_INFO_bio(bio, priv_info) != 1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char* out_contents;
|
||||
const long out_length = BIO_get_mem_data(bio, &out_contents);
|
||||
if (out_length <= 0L || out_contents == NULL) {
|
||||
goto cleanup;
|
||||
}
|
||||
const size_t out_len = (size_t)out_length;
|
||||
|
||||
if (out_len > *key_data_length) {
|
||||
*key_data_length = out_len;
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(key_data, out_contents, out_len);
|
||||
*key_data_length = out_len;
|
||||
|
||||
PKCS8_PRIV_KEY_INFO_free(priv_info);
|
||||
EVP_PKEY_free(evp_key);
|
||||
BIO_vfree(bio);
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
dump_ssl_error();
|
||||
if (priv_info != NULL) PKCS8_PRIV_KEY_INFO_free(priv_info);
|
||||
if (evp_key != NULL) EVP_PKEY_free(evp_key);
|
||||
if (bio != NULL) BIO_vfree(bio);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool SerializeEccPublicKey(EC_KEY* ec_key, uint8_t* key_data,
|
||||
size_t* key_data_length) {
|
||||
if (ec_key == NULL || key_data == NULL || key_data_length == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* key_ptr = NULL;
|
||||
const int key_size = i2d_EC_PUBKEY(ec_key, &key_ptr);
|
||||
if (key_size <= 0 || key_ptr == NULL) {
|
||||
dump_ssl_error();
|
||||
goto cleanup;
|
||||
}
|
||||
if ((size_t)key_size > *key_data_length) {
|
||||
*key_data_length = key_size;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(key_data, key_ptr, key_size);
|
||||
*key_data_length = key_size;
|
||||
OPENSSL_free(key_ptr);
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
if (key_ptr != NULL) OPENSSL_free(key_ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewEccKeyPair(uint8_t* private_key_data, size_t* private_key_data_length,
|
||||
uint8_t* public_key_data, size_t* public_key_data_length) {
|
||||
EC_KEY* key_pair = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (key_pair == NULL || EC_KEY_generate_key(key_pair) != 1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
EC_KEY_set_asn1_flag(key_pair, OPENSSL_EC_NAMED_CURVE);
|
||||
EC_KEY_set_conv_form(key_pair, POINT_CONVERSION_UNCOMPRESSED);
|
||||
|
||||
if (!SerializeEccPrivateKey(key_pair, private_key_data,
|
||||
private_key_data_length)) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (!SerializeEccPublicKey(key_pair, public_key_data,
|
||||
public_key_data_length)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
EC_KEY_free(key_pair);
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
dump_ssl_error();
|
||||
if (key_pair != NULL) EC_KEY_free(key_pair);
|
||||
return false;
|
||||
}
|
||||
75
oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.h
Normal file
75
oemcrypto/opk/oemcrypto_ta/wtpi_reference/ecc_util.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_ECC_UTIL_H_
|
||||
#define OEMCRYPTO_TA_ECC_UTIL_H_
|
||||
|
||||
#include "stdbool.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#include "openssl/ec.h"
|
||||
|
||||
/* Checks to see that |ecc_key| is a valid ECC key. Returns false if not a
|
||||
valid key.
|
||||
Caller retains ownership of |ecc_key| and it must not be NULL. */
|
||||
bool CheckECCKey(const EC_KEY* ecc_key);
|
||||
|
||||
/* Attempts to deserialize |size| bytes of |serialized_bytes| into an ECC
|
||||
key and store the result in |ecc_key|.
|
||||
The provided |serialized_bytes| must contain a valid ASN.1 DER encoded
|
||||
PKCS8 PrivateKeyInfo containing an ECPrivateKey using named curves
|
||||
(non-parameterized). Only supported curves by this API are secp256r1
|
||||
(required for ECC), secp384r1 (optional) and secp521r1 (optional).
|
||||
Note: If the public key is not included, then it is computed from
|
||||
the private key.
|
||||
Returns false if |serialized_bytes| can not be deserialized.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool DeserializeECCPrivateKey(const uint8_t* serialized_bytes, size_t size,
|
||||
EC_KEY** ecc_key);
|
||||
|
||||
/* Signs |message_length| bytes of |message| using |ecc_key| using ECDSA
|
||||
as defined in SEC.1. The hash algorithm used depends on which curve
|
||||
is used by |ecc_key|:
|
||||
- SHA-256 / secp256r1 (required)
|
||||
- SHA-384 / secp384r1 (optional support)
|
||||
- SHA-512 / secp521r1 (optional support)
|
||||
The output |signature| will be an ASN.1 DER encoded ECDSA-Sig-Value.
|
||||
Returns false if the signature could not be computed.
|
||||
|message_length| must be > 0 and *|signature_length| must be larger
|
||||
or equal to ECDSA_size(ecc_key).
|
||||
Note: It is common that the final ECDSA signature to be slightly
|
||||
smaller than indicated by ECDSA_size(ecc_key), check *|signature_length|
|
||||
if length has been truncated.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool ECCSignECDSA(EC_KEY* ecc_key, const uint8_t* message,
|
||||
size_t message_length, uint8_t* signature,
|
||||
size_t* signature_length);
|
||||
|
||||
/* Derives the OEMCrypto session key used for deriving other keys.
|
||||
The provided |key_source| should be the value provided to OEMCrypto
|
||||
by OEMCrypto_DeriveKeysFromSessionKey().
|
||||
For ECC based DRM certificates, |key_source| will be an ASN.1 DER
|
||||
encoded SubjectPublicKey containing an ephemeral ECC public key
|
||||
used for deriving the session key via ECDH using a Widevine-specific
|
||||
Key Derivation Function (KDF). The Widevine KDF is simply a SHA-256
|
||||
applied to the raw ECDH shared secret.
|
||||
The output |session_key| will be an AES-256 to be used for
|
||||
CMAC-AES-256 algorithm. *|session_key_length| should be at least 32
|
||||
bytes.
|
||||
Returns false if the provided |key_source| is not a properly encoded
|
||||
ECC public key, or it belongs to a curve different than |ecc_key|.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool ECCWidevineECDHSessionKey(EC_KEY* ecc_key, const uint8_t* key_source,
|
||||
size_t key_source_length, uint8_t* session_key,
|
||||
size_t* session_key_length);
|
||||
|
||||
/**
|
||||
* Generates a new pair of serialized EC SECP256R1 keys. The serialization
|
||||
* format is ASN.1 DER encoded PKCS8 PrivateKeyInfo/SubjectPublicKeyInfo.
|
||||
* |private_key_data_length| and |public_key_data_length| indicate the buffer
|
||||
* size, and will be updated to the actual size used. */
|
||||
bool NewEccKeyPair(uint8_t* private_key_data, size_t* private_key_data_length,
|
||||
uint8_t* public_key_data, size_t* public_key_data_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_ECC_UTIL_H_ */
|
||||
@@ -0,0 +1,91 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_KEY_MAPPING_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_KEY_MAPPING_INTERFACE_H_
|
||||
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer2.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This header file defines the interfaces for pairing/unpairing a
|
||||
* WTPI_K1_SymmetricKey_Handle defined in Crypto and Key Management layer 1,
|
||||
* with a WTPI_K2_SymmetricKey_Handle defined in Crypto and Key Management layer
|
||||
* 2. This is used by both the REFERENCE implementation of hardware-backed
|
||||
* Crypto and Key Management layer 1(wtpi_crypto_and_key_management_layer1_hw.c)
|
||||
* and the TEST implementation of hardware-backed Crypto and Key Management
|
||||
* layer 2(wtpi_crypto_and_key_management_layer2_hw.c).
|
||||
*
|
||||
* Partners using the reference implementation
|
||||
* wtpi_crypto_and_key_management_layer1_hw.c and implementing their own
|
||||
* wtpi_crypto_and_key_management_interface_layer2.h may need to implement this
|
||||
* interface as well.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given a layer 1 key handle, looks up for the layer 2 key handle which is
|
||||
* paired with this layer 1 key handle. Returns NULL if the key has not been
|
||||
* loaded into the layer 2 key table. If this is the case and |reload_required|
|
||||
* is true, it loads the key to layer 2 key table and returns the layer 2 handle
|
||||
* of the loaded key.
|
||||
* Also returns NULL if there is any error during look-up.
|
||||
*/
|
||||
WTPI_K2_SymmetricKey_Handle KM_LookupKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle k1_key_handle, bool reload_required);
|
||||
|
||||
/**
|
||||
* Pairs a layer 1 key handle with a layer 2 key handle. Both handles eventually
|
||||
* point to the same key. This allows a layer 1 function with a layer 1 key
|
||||
* handle to retrieve the corresponding layer 2 key handle and then make a call
|
||||
* to the layer 2 interface.
|
||||
* Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the key handles is invalid,
|
||||
* and OEMCrypto_SUCCESS otherwise.
|
||||
*/
|
||||
OEMCryptoResult KM_PairKeyHandle(WTPI_K1_SymmetricKey_Handle k1_key_handle,
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle);
|
||||
|
||||
/**
|
||||
* Evicts a layer 2 key in slot |k2_index|. It is also responsible for unpairing
|
||||
* the handles and releasing layer 2 key handles after eviction.
|
||||
* Returns OEMCrypto_ERROR_INVALID_CONTEXT if |k2_index| is invalid,
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if no key can be evicted, and
|
||||
* OEMCrypto_SUCCESS otherwise.
|
||||
*/
|
||||
OEMCryptoResult KM_EvictLayer2KeyByIndex(uint32_t k2_index);
|
||||
|
||||
/**
|
||||
* Sets the key eviction handler. The handler takes a key slot to be evicted and
|
||||
* returns OEMCryptoResult.
|
||||
*/
|
||||
void KM_SetLayer2KeyEvictionCallback(OEMCryptoResult (*callback)(uint32_t));
|
||||
|
||||
/**
|
||||
* Returns true if |k2_index| it a valid key slot. Otherwise returns false.
|
||||
*/
|
||||
bool KM_IsLayer2KeyIndexValid(uint32_t k2_index);
|
||||
|
||||
/**
|
||||
* Gets the key slot from |key_handle|, and places it in |k2_index|.
|
||||
* Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|
||||
* |key_handle| is invalid, and OEMCrypto_SUCCESS otherwise.
|
||||
* Caller retains ownership of all pointers.
|
||||
*/
|
||||
OEMCryptoResult KM_GetLayer2KeyIndex(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
uint32_t* k2_index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_KEY_MAPPING_INTERFACE_H_ */
|
||||
77
oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.c
Normal file
77
oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "renewal_util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_endian.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "oemcrypto_overflow.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
// The largest context size we expect is a 72-byte CA Token
|
||||
#define MAX_CONTEXT_SIZE 72
|
||||
|
||||
static void SafeConcatenate(uint8_t* buffer, size_t* buffer_length,
|
||||
size_t buffer_capacity, const uint8_t* new_data,
|
||||
size_t new_data_length) {
|
||||
size_t new_buffer_length = 0;
|
||||
ABORT_IF(
|
||||
OPK_AddOverflowUX(*buffer_length, new_data_length, &new_buffer_length),
|
||||
"Overflow when incrementing buffer size");
|
||||
ABORT_IF(new_buffer_length > buffer_capacity,
|
||||
"Operation would overflow buffer");
|
||||
memcpy(&buffer[*buffer_length], new_data, new_data_length);
|
||||
*buffer_length = new_buffer_length;
|
||||
}
|
||||
|
||||
OEMCryptoResult OPKI_DeriveRenewedKeyboxKey(
|
||||
WTPI_K1_SymmetricKey_Handle renewal_key, const uint8_t* old_key,
|
||||
KeySize old_key_length, const uint8_t* context, size_t context_length,
|
||||
KeySize new_key_length, SymmetricKeyType new_key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* new_key) {
|
||||
ABORT_IF_NULL(old_key);
|
||||
ABORT_IF(old_key_length != KEY_SIZE_128 && old_key_length != KEY_SIZE_256,
|
||||
"Unexpected old key size %u", old_key_length);
|
||||
ABORT_IF_NULL(context);
|
||||
ABORT_IF_ZERO(context_length);
|
||||
ABORT_IF(context_length > MAX_CONTEXT_SIZE, "Context suspiciously big");
|
||||
ABORT_IF(new_key_length != KEY_SIZE_128 && new_key_length != KEY_SIZE_256,
|
||||
"Unexpected new key size %u", new_key_length);
|
||||
ABORT_IF_NULL(new_key);
|
||||
|
||||
// Per the Widevine renewal protocol and NIST 800-108, the full context
|
||||
// consists of the following items concatenated:
|
||||
// kLabel || 0x00 || old_key || context || new_key_length
|
||||
static const uint8_t kLabel[] = {'K', 'e', 'y', 'b', 'o', 'x', 'v', '3'};
|
||||
uint8_t full_context[sizeof(kLabel) + 1 + KEY_SIZE_256 + MAX_CONTEXT_SIZE +
|
||||
sizeof(uint32_t)] = {0};
|
||||
size_t full_context_length = 0;
|
||||
|
||||
// Concatenate kLabel
|
||||
SafeConcatenate(full_context, &full_context_length, sizeof(full_context),
|
||||
kLabel, sizeof(kLabel));
|
||||
// Skip a byte to leave a zero byte
|
||||
full_context_length++;
|
||||
// Concatenate old_key
|
||||
SafeConcatenate(full_context, &full_context_length, sizeof(full_context),
|
||||
old_key, old_key_length);
|
||||
// Concatenate initial_context
|
||||
SafeConcatenate(full_context, &full_context_length, sizeof(full_context),
|
||||
context, context_length);
|
||||
// Concatenate final_key_size (per NIST 800-108, this is in bits, as a 32-bit
|
||||
// unsigned integer, in big-endian byte order)
|
||||
const uint32_t result_length = (uint32_t)(new_key_length)*8;
|
||||
const uint32_t be_result_length = oemcrypto_htobe32(result_length);
|
||||
SafeConcatenate(full_context, &full_context_length, sizeof(full_context),
|
||||
(const uint8_t*)&be_result_length, sizeof(be_result_length));
|
||||
|
||||
return WTPI_K1_DeriveKeyFromKeyHandle(renewal_key, 1 /* counter */,
|
||||
full_context, full_context_length,
|
||||
new_key_type, new_key_length, new_key);
|
||||
}
|
||||
35
oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.h
Normal file
35
oemcrypto/opk/oemcrypto_ta/wtpi_reference/renewal_util.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_RENEWAL_UTIL_H_
|
||||
#define OEMCRYPTO_TA_RENEWAL_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Derives a new key using the key renewal process. The old key and context are
|
||||
* combined with other values matching NIST 800-108 and the Widevine key renewal
|
||||
* process to produce the final context, then that final context is used in key
|
||||
* derivation along with the renewal key in order to derive the new key.
|
||||
*/
|
||||
OEMCryptoResult OPKI_DeriveRenewedKeyboxKey(
|
||||
WTPI_K1_SymmetricKey_Handle renewal_key, const uint8_t* old_key,
|
||||
KeySize old_key_length, const uint8_t* context, size_t context_length,
|
||||
KeySize new_key_length, SymmetricKeyType new_key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* new_key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_RENEWAL_UTIL_H_ */
|
||||
135
oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.c
Normal file
135
oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "rsa_util.h"
|
||||
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
void dump_ssl_error(void) {
|
||||
int count = 0;
|
||||
unsigned long err;
|
||||
while ((err = ERR_get_error())) {
|
||||
count++;
|
||||
char buffer[120];
|
||||
ERR_error_string_n((int)err, buffer, sizeof(buffer));
|
||||
LOGE("SSL Error %d -- %lu -- %s", count, err, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckRSAKey(const RSA* rsa) {
|
||||
ABORT_IF(rsa == NULL, "rsa is NULL");
|
||||
switch (RSA_check_key(rsa)) {
|
||||
case 1: /* valid. */
|
||||
return true;
|
||||
case 0: /* not valid. */
|
||||
LOGE("RSA key not valid");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
default: /* -1 == check failed. */
|
||||
LOGE("Error checking RSA key");
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeserializePKCS8PrivateKey(const uint8_t* serialized_bytes, size_t size,
|
||||
RSA** rsa) {
|
||||
ABORT_IF(serialized_bytes == NULL || size <= 0 || rsa == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
BIO* bio = BIO_new_mem_buf(serialized_bytes, (int)size);
|
||||
if (bio == NULL) {
|
||||
LOGE("Could not allocate bio buffer");
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
EVP_PKEY* evp = NULL;
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
||||
if (pkcs8_pki == NULL) {
|
||||
LOGE("d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL");
|
||||
dump_ssl_error();
|
||||
goto cleanup;
|
||||
}
|
||||
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
||||
if (evp == NULL) {
|
||||
LOGE("EVP_PKCS82PKEY returned NULL");
|
||||
dump_ssl_error();
|
||||
goto cleanup;
|
||||
}
|
||||
*rsa = EVP_PKEY_get1_RSA(evp);
|
||||
if (*rsa == NULL) {
|
||||
LOGE("PrivateKeyInfo did not contain an RSA key");
|
||||
goto cleanup;
|
||||
}
|
||||
success = true;
|
||||
|
||||
cleanup:
|
||||
if (evp != NULL) {
|
||||
EVP_PKEY_free(evp);
|
||||
}
|
||||
if (pkcs8_pki != NULL) {
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
}
|
||||
BIO_free(bio);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool RSASignSSAPSSSHA1(RSA* rsa, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length) {
|
||||
ABORT_IF(rsa == NULL || message == NULL || message_length <= 0 ||
|
||||
signature == NULL || signature_length == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
size_t private_key_size = RSA_size(rsa);
|
||||
ABORT_IF(*signature_length < private_key_size,
|
||||
"signature_length is not long enough");
|
||||
/* Hash the message using SHA1. */
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
if (!SHA1(message, message_length, hash)) {
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
/* Add PSS padding. */
|
||||
ABORT_IF(private_key_size > KEY_SIZE_3072, "rsa size is too large");
|
||||
uint8_t padding[KEY_SIZE_3072];
|
||||
const int kPssSaltLength = 20;
|
||||
int status = RSA_padding_add_PKCS1_PSS_mgf1(rsa, padding, hash, EVP_sha1(),
|
||||
NULL, kPssSaltLength);
|
||||
if (status == 0) {
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Encrypt PSS padded digest. */
|
||||
int bytes_written = RSA_private_encrypt(private_key_size, padding, signature,
|
||||
rsa, RSA_NO_PADDING);
|
||||
if (bytes_written == -1) {
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
*signature_length = bytes_written;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RSAPrivateDecrypt(RSA* rsa, const uint8_t* in, size_t in_length,
|
||||
uint8_t* out, size_t* out_length) {
|
||||
ABORT_IF(rsa == NULL || in == NULL || in_length <= 0 || out == NULL ||
|
||||
out_length == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
size_t private_key_size = RSA_size(rsa);
|
||||
ABORT_IF(*out_length < private_key_size, "out_length is not long enough");
|
||||
int decrypted_size =
|
||||
RSA_private_decrypt(in_length, in, out, rsa, RSA_PKCS1_OAEP_PADDING);
|
||||
if (decrypted_size == -1) {
|
||||
dump_ssl_error();
|
||||
return false;
|
||||
}
|
||||
*out_length = decrypted_size;
|
||||
return true;
|
||||
}
|
||||
45
oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.h
Normal file
45
oemcrypto/opk/oemcrypto_ta/wtpi_reference/rsa_util.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_RSA_UTIL_H_
|
||||
#define OEMCRYPTO_TA_RSA_UTIL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "openssl/rsa.h"
|
||||
|
||||
/* Logs any errors reported to the thread's error queue. */
|
||||
void dump_ssl_error(void);
|
||||
|
||||
/* Checks to see that |rsa| is a valid RSA key. Returns false if |rsa| is not a
|
||||
valid key.
|
||||
Caller retains ownership of *|rsa| and it must not be NULL. */
|
||||
bool CheckRSAKey(const RSA* rsa);
|
||||
|
||||
/* Attempts to deserialize |size| bytes of |serialized_bytes| into an RSA key
|
||||
and store the result in |rsa|. |serialized_bytes| is expected to be a PKCS8
|
||||
RSA private key. Returns false if |serialized_bytes| can not be deserialized.
|
||||
|size| must be > 0.
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool DeserializePKCS8PrivateKey(const uint8_t* serialized_bytes, size_t size,
|
||||
RSA** rsa);
|
||||
|
||||
/* Signs |message_length| bytes of |message| using |rsa| and padding scheme
|
||||
RSASSA-PSS with SHA1 and places the result in |signature| and modifies
|
||||
*|signature_length| to the appropriate value.
|
||||
Returns false if the signature could not be computed.
|
||||
|message_length| must be > 0 and *|signature_length| must be > RSA_size(rsa).
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool RSASignSSAPSSSHA1(RSA* rsa, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
/* Decrypts |in_length| bytes of |in| and places it in |out| using scheme PKCS1
|
||||
OAEP and |rsa|. Modifies *|out_length| to the appropriate length.
|
||||
|in_length| must be > 0 and *|out_length| must be > RSA_size(rsa).
|
||||
Caller retains ownership of all pointers and they must not be NULL. */
|
||||
bool RSAPrivateDecrypt(RSA* rsa, const uint8_t* in, size_t in_length,
|
||||
uint8_t* out, size_t* out_length);
|
||||
|
||||
#endif /* OEMCRYPTO_TA_RSA_UTIL_H_ */
|
||||
9
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_abort.c
Normal file
9
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_abort.c
Normal file
@@ -0,0 +1,9 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "wtpi_abort_interface.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void WTPI_Abort(void) { abort(); }
|
||||
@@ -0,0 +1,213 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "wtpi_clock_interface_layer1.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_wall_clock.h"
|
||||
#include "wtpi_clock_interface_layer2.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
#include "wtpi_persistent_storage.h"
|
||||
|
||||
static bool gInitialized = false;
|
||||
// The most recent trusted time.
|
||||
static uint64_t gLastTime;
|
||||
// The last saved value of the wall clock.
|
||||
static uint64_t gSavedWallClock;
|
||||
// Difference between hardware clock and trusted clock.
|
||||
static int64_t gClockDelta;
|
||||
static uint64_t gSavedGenerationNumber;
|
||||
|
||||
typedef struct WTPI_PersistentData {
|
||||
uint64_t generation_number;
|
||||
uint64_t previous_wall_clock;
|
||||
uint64_t previous_trusted_time;
|
||||
} WTPI_PersistentData;
|
||||
#define PERSISTENT_FORMAT_VERSION_NUMBER 1
|
||||
#define PERSISTENT_DATA_SIZE (1 + sizeof(WTPI_PersistentData))
|
||||
|
||||
#define EARLIEST_REASONABLE_TIME 1587470400 // Year 2020.
|
||||
|
||||
#define PERIODIC_SAVE_TIME (60 * 15) // Save every 15 minutes.
|
||||
static uint64_t gLastSaveTime;
|
||||
|
||||
// Initialize the clock delta based on the saved wall clock time.
|
||||
static OEMCryptoResult InitializeDelta(void) {
|
||||
gClockDelta = 0;
|
||||
uint64_t hw_timer = 0;
|
||||
OEMCryptoResult status = WTPI_GetSecureTimer(&hw_timer);
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
uint64_t wall_clock = 0;
|
||||
status = OPK_GetWallClockTime(&wall_clock);
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
// If we have reasonable wall clock times, and it increased since we last
|
||||
// saved it, assume that our system time should also increase by that much.
|
||||
// This is a one-time increment because we assume that the wall clock's
|
||||
// increment is more reliable while the system is off than the hardware clock.
|
||||
if (wall_clock > EARLIEST_REASONABLE_TIME &&
|
||||
gSavedWallClock > EARLIEST_REASONABLE_TIME &&
|
||||
wall_clock > gSavedWallClock) {
|
||||
uint64_t sleep_time = wall_clock - gSavedWallClock;
|
||||
gLastTime += sleep_time;
|
||||
LOGD("Init clock from sleep time = %" PRIu64 " = %" PRIu64 " - %" PRIu64,
|
||||
sleep_time, wall_clock, gSavedWallClock);
|
||||
}
|
||||
// After that onetime increment, we should advance the clock in sync with the
|
||||
// hardware clock. Here we compute the delta between our system clock and the
|
||||
// hardware clock.
|
||||
gClockDelta = gLastTime - hw_timer;
|
||||
LOGD("Init clock with new delta: %" PRId64 " = %" PRIu64 " - %" PRIu64,
|
||||
gClockDelta, gLastTime, hw_timer);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult InitializeData(void) {
|
||||
gInitialized = true;
|
||||
size_t data_length = PERSISTENT_DATA_SIZE;
|
||||
uint8_t buffer[PERSISTENT_DATA_SIZE];
|
||||
OEMCryptoResult status = WTPI_LoadPersistentData(buffer, &data_length);
|
||||
uint8_t version_number = buffer[0];
|
||||
if (status == OPK_ERROR_NO_PERSISTENT_DATA ||
|
||||
data_length != PERSISTENT_DATA_SIZE ||
|
||||
version_number != PERSISTENT_FORMAT_VERSION_NUMBER) {
|
||||
// Note: In the future, we may wish to handle older version numbers.
|
||||
// This is the first initialization. Start the generation number at random
|
||||
// value and the clock at 0.
|
||||
status = WTPI_C1_RandomBytes((uint8_t*)(&gSavedGenerationNumber),
|
||||
sizeof(uint64_t));
|
||||
LOGD("New random generation number.");
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
gClockDelta = 0;
|
||||
gLastSaveTime = 0;
|
||||
status = WTPI_GetSecureTimer(&gLastTime);
|
||||
LOGD("Initializing new clock delta = 0, last = %" PRIu64, gLastTime);
|
||||
return status;
|
||||
} else {
|
||||
WTPI_PersistentData data;
|
||||
memcpy(&data, buffer + 1, sizeof(data));
|
||||
gSavedGenerationNumber = data.generation_number;
|
||||
gLastTime = data.previous_trusted_time;
|
||||
gSavedWallClock = data.previous_wall_clock;
|
||||
gLastSaveTime = gLastTime;
|
||||
status = InitializeDelta();
|
||||
LOGD("Loading saved generation: %" PRIu64, gSavedGenerationNumber);
|
||||
LOGD("Initializing clock w/last = %" PRIu64 ", last wall = %" PRIu64
|
||||
", delta = %" PRId64,
|
||||
gLastTime, gSavedWallClock, gClockDelta);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
static OEMCryptoResult SaveData(void) {
|
||||
if (!gInitialized) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
WTPI_PersistentData data;
|
||||
// Save the most recent wall clock value, if it's reasonable. Otherwise keep
|
||||
// the previous saved value.
|
||||
uint64_t wall_clock = 0;
|
||||
OEMCryptoResult status = OPK_GetWallClockTime(&wall_clock);
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
data.previous_wall_clock =
|
||||
wall_clock > EARLIEST_REASONABLE_TIME ? wall_clock : gSavedWallClock;
|
||||
data.previous_trusted_time = gLastTime;
|
||||
data.generation_number = gSavedGenerationNumber;
|
||||
LOGD("Store generation number = %" PRIu64, gSavedGenerationNumber);
|
||||
gLastSaveTime = gLastTime;
|
||||
LOGD("Saving wall clock = %" PRIu64 ", system time = %" PRId64 ".",
|
||||
data.previous_wall_clock, data.previous_trusted_time);
|
||||
size_t data_length = PERSISTENT_DATA_SIZE;
|
||||
uint8_t buffer[PERSISTENT_DATA_SIZE];
|
||||
buffer[0] = PERSISTENT_FORMAT_VERSION_NUMBER;
|
||||
memcpy(buffer + 1, &data, sizeof(data));
|
||||
return WTPI_StorePersistentData(buffer, data_length);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the generation number interface.
|
||||
*******************************************************************************/
|
||||
OEMCryptoResult WTPI_PrepareGenerationNumber(void) {
|
||||
return WTPI_PrepareStoredPersistentData();
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_LoadGenerationNumber(uint64_t* value) {
|
||||
LOGD("Load generation number.");
|
||||
if (!gInitialized) {
|
||||
OEMCryptoResult status = InitializeData();
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
}
|
||||
if (!value) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
*value = gSavedGenerationNumber;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_SaveGenerationNumber(uint64_t value) {
|
||||
LOGD("Save generation number.");
|
||||
if (!gInitialized) {
|
||||
// This is only done in the tests. In real life, we attempt to load the old
|
||||
// GN before we ever save a new one.
|
||||
OEMCryptoResult status = InitializeData();
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
}
|
||||
gSavedGenerationNumber = value;
|
||||
return SaveData();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the clock interface.
|
||||
*******************************************************************************/
|
||||
OEMCryptoResult WTPI_InitializeClock(void) {
|
||||
LOGD("Initialize clock.");
|
||||
gInitialized = false;
|
||||
gLastTime = 0;
|
||||
gSavedWallClock = EARLIEST_REASONABLE_TIME;
|
||||
gClockDelta = 0;
|
||||
gLastSaveTime = 0;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_TerminateClock(void) {
|
||||
LOGD("Terminate clock.");
|
||||
if (!gInitialized) return OEMCrypto_SUCCESS;
|
||||
return SaveData();
|
||||
}
|
||||
|
||||
OEMCrypto_Clock_Security_Level WTPI_GetClockType(void) { return kSecureTimer; }
|
||||
|
||||
OEMCryptoResult WTPI_GetTrustedTime(uint64_t* time_in_s) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(time_in_s);
|
||||
OEMCryptoResult status = OEMCrypto_SUCCESS;
|
||||
if (!gInitialized) {
|
||||
LOGD("Clock needs to initialize.");
|
||||
status = InitializeData();
|
||||
if (status != OEMCrypto_SUCCESS) return status;
|
||||
}
|
||||
uint64_t hw_timer = 0;
|
||||
status = WTPI_GetSecureTimer(&hw_timer);
|
||||
uint64_t now = hw_timer + gClockDelta;
|
||||
// If the hardware clock goes backwards, or the clock delta has not been
|
||||
// initialized by the saved wall clock, then now might be less than the last
|
||||
// time. In that case, we increase the delta so that our clock continues to
|
||||
// move forward.
|
||||
if (now < gLastTime) {
|
||||
gClockDelta = gLastTime - hw_timer;
|
||||
now = gLastTime;
|
||||
LOGD("Clock drift. update now = %" PRIu64 ", and delta %" PRId64
|
||||
" = %" PRIu64 " - %" PRIu64,
|
||||
now, gClockDelta, gLastTime, hw_timer);
|
||||
} else {
|
||||
gLastTime = now;
|
||||
}
|
||||
*time_in_s = now;
|
||||
if (now > gLastSaveTime + PERIODIC_SAVE_TIME) {
|
||||
LOGD("Periodic clock save. now = %" PRIu64 ", last save = %" PRIu64, now,
|
||||
gLastSaveTime);
|
||||
SaveData();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
/* The following are customizable macros we expect the TEE to implement.
|
||||
These need to be in a header so the TA can use them during compilation. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_CONFIG_MACROS_H_
|
||||
#define OEMCRYPTO_TA_WTPI_CONFIG_MACROS_H_
|
||||
|
||||
/// @addtogroup config
|
||||
/// @{
|
||||
|
||||
/**
|
||||
* Following should not be more than UINT32_MAX - 1.
|
||||
*/
|
||||
#define MAX_NUMBER_OF_SESSIONS 50
|
||||
#define MAX_NUMBER_OF_ENTITLED_KEY_SESSIONS 20
|
||||
#define MAX_NUMBER_OF_KEYS 400
|
||||
#define CONTENT_KEYS_PER_SESSION 30
|
||||
#define ENTITLEMENT_KEYS_PER_SESSION 30
|
||||
/** This is the number of usage entries in the header. */
|
||||
#define MAX_NUMBER_OF_USAGE_ENTRIES 300
|
||||
/** The number of active entries that may be loaded simultaneously. */
|
||||
#define MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES 10
|
||||
/** The number of active DRM keys that may be loaded simultaneously. */
|
||||
#define MAX_NUMBER_OF_ASYMMETRIC_KEYS 8
|
||||
/** This is the number of key slots in the layer 2 symmetric key table. */
|
||||
#define MAX_NUMBER_OF_LAYER2_KEY_TABLE_ENTRIES 8
|
||||
|
||||
/**
|
||||
* The crypto hardware may be asked to wrap a key so that it is less vulnerable
|
||||
* to attack. This is the size of a wrapped mac key.
|
||||
*/
|
||||
#define WRAPPED_MAC_KEY_SIZE 32
|
||||
|
||||
/**
|
||||
* The extra size (in bytes) that is required by WTPI_EncryptAndSign. If
|
||||
* supporting backwards-compatibility with multiple versions of WrappedData,
|
||||
* this value should be as big as the version with the largest amount of
|
||||
* overhead. */
|
||||
#define ENCRYPT_AND_SIGN_EXTRA 68
|
||||
/** Maximum size of a symmetric (AES) key. */
|
||||
#define MAX_SYMMETRIC_KEY_SIZE 32
|
||||
/** Maximum size of a wrapped symmetric (AES) key. This might be slightly larger
|
||||
than the key itself because the crypto hardware might add verification
|
||||
information to the key. */
|
||||
#define MAX_WRAPPED_SYMMETRIC_KEY_SIZE MAX_SYMMETRIC_KEY_SIZE
|
||||
/** Maximum size of a wrapped asymmetric key (ECC or RSA). This might be
|
||||
slightly larger than the key itself because the crypto hardware might add
|
||||
verification information to the key. */
|
||||
#define MAX_WRAPPED_ASYMMETRIC_KEY_SIZE \
|
||||
(PKCS8_DRM_KEY_MAX_SIZE + ENCRYPT_AND_SIGN_EXTRA)
|
||||
|
||||
/// @}
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_CONFIG_MACROS_H_ */
|
||||
110
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crc32.c
Normal file
110
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_crc32.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "wtpi_crc32_interface.h"
|
||||
|
||||
#include "oemcrypto_overflow.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
#include "wtpi_secure_buffer_access_interface.h"
|
||||
|
||||
#define INIT_CRC32 0xffffffff
|
||||
|
||||
static uint32_t wvrunningcrc32(const uint8_t* in, size_t in_length,
|
||||
uint32_t prev_crc) {
|
||||
static const 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 (in_length > 0) {
|
||||
prev_crc = (prev_crc << 8) ^ CRC32[(prev_crc >> 24) ^ ((uint32_t)(*in))];
|
||||
in++;
|
||||
in_length--;
|
||||
}
|
||||
|
||||
return prev_crc;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_Crc32Init(uint32_t* initial_hash) {
|
||||
if (initial_hash == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
*initial_hash = INIT_CRC32;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_Crc32Cont(const uint8_t* in, size_t in_length,
|
||||
uint32_t prev_crc, uint32_t* new_crc) {
|
||||
if (in == NULL || in_length == 0 || new_crc == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
*new_crc = wvrunningcrc32(in, in_length, prev_crc);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_Crc32Cont_OutputBuffer(const OPK_OutputBuffer* in,
|
||||
size_t in_offset, size_t in_length,
|
||||
uint32_t prev_crc,
|
||||
uint32_t* new_crc) {
|
||||
size_t total_size;
|
||||
if (OPK_AddOverflowUX(in_offset, in_length, &total_size)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (in == NULL || in_length == 0 || new_crc == NULL ||
|
||||
total_size > in->size) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
uint8_t* src = NULL;
|
||||
if (in->type == OPK_SECURE_OUTPUT_BUFFER) {
|
||||
OEMCryptoResult result =
|
||||
WTPI_GetSecureBufferAddress(in->buffer.secure, in_offset, &src);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
} else if (in->type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) {
|
||||
src = in->buffer.clear_insecure + in_offset;
|
||||
} else {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
*new_crc = wvrunningcrc32(src, in_length, prev_crc);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> /* THIS IS TEMPORARY UNTIL WE GET AN ALLOCATOR. */
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "key_mapping_interface.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "oemcrypto_overflow.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_config_macros.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer2.h"
|
||||
#include "wtpi_device_key_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
#include "wtpi_secure_buffer_access_interface.h"
|
||||
|
||||
/******************************************************************************
|
||||
* This is a reference implementation of Crypto and Key Management layer 1,
|
||||
* which aims to work with an underlying hardware-backed cryptography. The keys
|
||||
* in this layer are encrypted and signed by a device specific key, and are
|
||||
* stored in a pre-allocated table, before used by the hardware crypto.
|
||||
******************************************************************************/
|
||||
|
||||
/** Wrapped key data to be saved in key management layer 1 table. */
|
||||
typedef struct WrappedKeyData {
|
||||
uint8_t iv[KEY_IV_SIZE];
|
||||
uint8_t wrapped_key[MAX_WRAPPED_SYMMETRIC_KEY_SIZE];
|
||||
} WrappedKeyData;
|
||||
|
||||
/** Signed and wrapped key data to be saved in key management layer 1 table. */
|
||||
typedef struct SignedWrappedKeyData {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
WrappedKeyData wrapped_key_data;
|
||||
} SignedWrappedKeyData;
|
||||
|
||||
typedef struct WTPI_K1_SymmetricKey {
|
||||
SymmetricKeyType key_type;
|
||||
KeySize key_size;
|
||||
SignedWrappedKeyData key_data;
|
||||
bool is_loaded;
|
||||
uint32_t index_loaded;
|
||||
} WTPI_K1_SymmetricKey;
|
||||
|
||||
typedef struct wtpi_k1_symmetric_key_handle {
|
||||
uint32_t index;
|
||||
} wtpi_k1_symmetric_key_handle;
|
||||
|
||||
/**
|
||||
* Key table used by key management layer 1. Keys in the table are wrapped by a
|
||||
* device specific key. When being used, the key needs to be unwrapped and
|
||||
* verified first, and then loaded into the key management layer 2 table.
|
||||
*/
|
||||
DEFINE_OBJECT_TABLE(key_table, WTPI_K1_SymmetricKey, MAX_NUMBER_OF_KEYS, NULL);
|
||||
|
||||
static OEMCryptoResult EncryptAndSignKey(WTPI_K2_SymmetricKey_Handle key_handle,
|
||||
uint32_t context, uint8_t* out,
|
||||
size_t out_size) {
|
||||
if (key_handle == NULL || out == NULL ||
|
||||
out_size < sizeof(SignedWrappedKeyData)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
SignedWrappedKeyData* wrapped = (SignedWrappedKeyData*)out;
|
||||
/* Pick a random IV for generating keys. */
|
||||
OEMCryptoResult result = WTPI_C1_RandomBytes(
|
||||
wrapped->wrapped_key_data.iv, sizeof(wrapped->wrapped_key_data.iv));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Encrypt the buffer.
|
||||
WTPI_K2_SymmetricKey_Handle encryption_key_handle = NULL;
|
||||
result = WTPI_K2_DeriveDeviceKeyIntoHandle(
|
||||
context, ENCRYPTION_KEY, &encryption_key_handle, KEY_SIZE_128);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WTPI_K2_EncryptKeyHandle(key_handle, encryption_key_handle,
|
||||
wrapped->wrapped_key_data.iv,
|
||||
wrapped->wrapped_key_data.wrapped_key);
|
||||
WTPI_K2_FreeKeyHandle(encryption_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Compute the signature of the data past the signature block and store it
|
||||
// at the start of the output buffer.
|
||||
WTPI_K2_SymmetricKey_Handle signing_key_handle = NULL;
|
||||
result = WTPI_K2_DeriveDeviceKeyIntoHandle(DEVICE_KEY_WRAP_INTERNAL_KEY,
|
||||
MAC_KEY_CLIENT,
|
||||
&signing_key_handle, KEY_SIZE_256);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WTPI_C2_HMAC_SHA256(
|
||||
signing_key_handle, (uint8_t*)(&wrapped->wrapped_key_data),
|
||||
sizeof(wrapped->wrapped_key_data), wrapped->signature);
|
||||
WTPI_K2_FreeKeyHandle(signing_key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
static OEMCryptoResult VerifyAndDecryptKey(
|
||||
uint32_t context, const uint8_t* wrapped_data, SymmetricKeyType key_type,
|
||||
KeySize key_size, WTPI_K2_SymmetricKey_Handle* out_key_handle) {
|
||||
if (wrapped_data == NULL || out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const SignedWrappedKeyData* wrapped =
|
||||
(const SignedWrappedKeyData*)wrapped_data;
|
||||
|
||||
// Verify the signature first, before decrypting.
|
||||
WTPI_K2_SymmetricKey_Handle signing_key_handle = NULL;
|
||||
OEMCryptoResult result = WTPI_K2_DeriveDeviceKeyIntoHandle(
|
||||
DEVICE_KEY_WRAP_INTERNAL_KEY, MAC_KEY_SERVER, &signing_key_handle,
|
||||
KEY_SIZE_256);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WTPI_C2_HMAC_SHA256_Verify(
|
||||
signing_key_handle, (const uint8_t*)(&wrapped->wrapped_key_data),
|
||||
sizeof(wrapped->wrapped_key_data), wrapped->signature);
|
||||
WTPI_K2_FreeKeyHandle(signing_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Decrypt the buffer.
|
||||
WTPI_K2_SymmetricKey_Handle encryption_key_handle = NULL;
|
||||
result = WTPI_K2_DeriveDeviceKeyIntoHandle(
|
||||
context, ENCRYPTION_KEY, &encryption_key_handle, KEY_SIZE_128);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WTPI_K2_AESDecryptAndCreateKeyHandle(
|
||||
encryption_key_handle, wrapped->wrapped_key_data.wrapped_key,
|
||||
(size_t)key_size, wrapped->wrapped_key_data.iv, key_type, out_key_handle);
|
||||
WTPI_K2_FreeKeyHandle(encryption_key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool IsKeyValid(uint32_t index) {
|
||||
WTPI_K1_SymmetricKey* key = OPKI_GetFromObjectTable(&key_table, index);
|
||||
switch (key->key_type) {
|
||||
case CONTENT_KEY:
|
||||
// We cheat a little here. We also call generic crypto keys "content
|
||||
// keys", even though some of them are 256 bit HMAC keys.
|
||||
return key->key_size == KEY_SIZE_128 || key->key_size == KEY_SIZE_256;
|
||||
case ENTITLEMENT_KEY:
|
||||
case MAC_KEY_SERVER:
|
||||
case MAC_KEY_CLIENT:
|
||||
return key->key_size == KEY_SIZE_256;
|
||||
case ENCRYPTION_KEY:
|
||||
case DERIVING_KEY:
|
||||
return key->key_size == KEY_SIZE_128 || key->key_size == KEY_SIZE_256;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsKeyHandleValid(WTPI_K1_SymmetricKey_Handle key_handle) {
|
||||
return (key_handle != NULL && key_handle->index < MAX_NUMBER_OF_KEYS &&
|
||||
IsKeyValid(key_handle->index));
|
||||
}
|
||||
|
||||
static OEMCryptoResult GetKeyType(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType* type) {
|
||||
if (key_handle == NULL || type == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, key_handle->index);
|
||||
*type = key->key_type;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
KeySize* size) {
|
||||
if (key_handle == NULL || size == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, key_handle->index);
|
||||
*size = key->key_size;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the key mapping interface.
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct KeyHandlePair {
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle;
|
||||
WTPI_K1_SymmetricKey_Handle k1_key_handle;
|
||||
} KeyHandlePair;
|
||||
|
||||
// |gHandlePairTable| does the book-keeping of all allocated layer 2 key handles
|
||||
// which have a layer 1 key handle counterpart. A layer 2 key handle which
|
||||
// doesn't have a corresponding layer 1 key handle is not tracked by this table.
|
||||
// Such a key handle is a temporary handle and should be freed right after use.
|
||||
// So, it doesn't have to be tracked.
|
||||
static KeyHandlePair gHandlePairTable[MAX_NUMBER_OF_LAYER2_KEY_TABLE_ENTRIES];
|
||||
|
||||
WTPI_K2_SymmetricKey_Handle KM_LookupKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle k1_key_handle, bool reload_required) {
|
||||
ABORT_IF(!IsKeyHandleValid(k1_key_handle), "Impossible key handle.");
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle = NULL;
|
||||
uint32_t k1_index = k1_key_handle->index;
|
||||
WTPI_K1_SymmetricKey* key = OPKI_GetFromObjectTable(&key_table, k1_index);
|
||||
if (key->is_loaded) {
|
||||
uint32_t k2_index = key->index_loaded;
|
||||
k2_key_handle = gHandlePairTable[k2_index].k2_key_handle;
|
||||
}
|
||||
if (k2_key_handle == NULL && reload_required) {
|
||||
OEMCryptoResult result = VerifyAndDecryptKey(
|
||||
DEVICE_KEY_WRAP_INTERNAL_KEY, (uint8_t*)(&key->key_data), key->key_type,
|
||||
key->key_size, &k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to reload layer 2 key handle with result: %u", result);
|
||||
return NULL;
|
||||
}
|
||||
result = KM_PairKeyHandle(k1_key_handle, k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE(
|
||||
"Failed to pair layer 1 key handle with layer 2 key handle with "
|
||||
"result: %u",
|
||||
result);
|
||||
}
|
||||
}
|
||||
return k2_key_handle;
|
||||
}
|
||||
|
||||
OEMCryptoResult KM_PairKeyHandle(WTPI_K1_SymmetricKey_Handle k1_key_handle,
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle) {
|
||||
ABORT_IF(!IsKeyHandleValid(k1_key_handle) ||
|
||||
!WTPI_K2_IsKeyHandleValid(k2_key_handle),
|
||||
"Impossible key handle.");
|
||||
uint32_t k2_index;
|
||||
OEMCryptoResult result = KM_GetLayer2KeyIndex(k2_key_handle, &k2_index);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, k1_key_handle->index);
|
||||
key->is_loaded = true;
|
||||
key->index_loaded = k2_index;
|
||||
gHandlePairTable[k2_index].k2_key_handle = k2_key_handle;
|
||||
gHandlePairTable[k2_index].k1_key_handle = k1_key_handle;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static void UnpairKeyHandle(uint32_t k2_index) {
|
||||
ABORT_IF(!KM_IsLayer2KeyIndexValid(k2_index), "Impossible key index.");
|
||||
WTPI_K1_SymmetricKey_Handle k1_key_handle =
|
||||
gHandlePairTable[k2_index].k1_key_handle;
|
||||
if (IsKeyHandleValid(k1_key_handle)) {
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, k1_key_handle->index);
|
||||
key->is_loaded = false;
|
||||
key->index_loaded = MAX_NUMBER_OF_LAYER2_KEY_TABLE_ENTRIES;
|
||||
}
|
||||
gHandlePairTable[k2_index].k2_key_handle = NULL;
|
||||
gHandlePairTable[k2_index].k1_key_handle = NULL;
|
||||
}
|
||||
|
||||
OEMCryptoResult KM_EvictLayer2KeyByIndex(uint32_t k2_index) {
|
||||
if (!KM_IsLayer2KeyIndexValid(k2_index)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
gHandlePairTable[k2_index].k2_key_handle;
|
||||
if (k2_key_handle == NULL) {
|
||||
// trying to evict a layer 2 key slot which is still waiting to be paired
|
||||
// could indicate that we are running out of layer 2 key slots
|
||||
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
OEMCryptoResult result = WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
UnpairKeyHandle(k2_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult AESCTRDecryptWithKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* in, size_t in_length,
|
||||
const uint8_t* iv, size_t block_offset, uint8_t* out) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 ||
|
||||
block_offset >= AES_BLOCK_SIZE || iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_AESCTRDecrypt(k2_key_handle, in, in_length, iv, block_offset,
|
||||
out);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the layer 1 crypto interface.
|
||||
*******************************************************************************/
|
||||
|
||||
OEMCryptoResult WTPI_C1_AESCBCDecrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
size_t key_length, const uint8_t* in,
|
||||
size_t in_length, const uint8_t* iv,
|
||||
uint8_t* out) {
|
||||
if (key_handle == NULL ||
|
||||
(key_length != KEY_SIZE_128 && key_length != KEY_SIZE_256) ||
|
||||
in == NULL || in_length == 0 || in_length % AES_BLOCK_SIZE != 0 ||
|
||||
iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_AESCBCDecrypt(k2_key_handle, key_length, in, in_length, iv,
|
||||
out);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_AESCBCEncrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* in, size_t in_length,
|
||||
const uint8_t* iv, uint8_t* out) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 ||
|
||||
in_length % AES_BLOCK_SIZE != 0 || iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_AESCBCEncrypt(k2_key_handle, in, in_length, iv, out);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_SHA256(const uint8_t* message, size_t message_length,
|
||||
uint8_t* out) {
|
||||
if (message == NULL || message_length == 0 || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
return WTPI_C2_SHA256(message, message_length, out);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* out) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_HMAC_SHA1(k2_key_handle, message, message_length, out);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* message,
|
||||
size_t message_length, uint8_t* out) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_HMAC_SHA256(k2_key_handle, message, message_length, out);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* message,
|
||||
size_t message_length, const uint8_t* signature) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
signature == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_C2_HMAC_SHA256_Verify(k2_key_handle, message, message_length,
|
||||
signature);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the layer 1 key management interface.
|
||||
*******************************************************************************/
|
||||
|
||||
OEMCryptoResult WTPI_K1_InitializeKeyManagement(void) {
|
||||
OPKI_UnsafeClearObjectTable(&key_table);
|
||||
for (int i = 0; i < MAX_NUMBER_OF_LAYER2_KEY_TABLE_ENTRIES; i++) {
|
||||
memset(&gHandlePairTable[i], 0, sizeof(gHandlePairTable[i]));
|
||||
}
|
||||
OEMCryptoResult result = WTPI_K2_InitializeKeyManagement();
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
KM_SetLayer2KeyEvictionCallback(&KM_EvictLayer2KeyByIndex);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_TerminateKeyManagement(void) {
|
||||
return WTPI_K2_TerminateKeyManagement();
|
||||
}
|
||||
|
||||
/** Create layer 1 key handle from a layer 2 key handle. */
|
||||
static OEMCryptoResult K1_CreateKeyHandle(
|
||||
WTPI_K2_SymmetricKey_Handle key_handle, SymmetricKeyType key_type,
|
||||
KeySize key_size, WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (key_handle == NULL || out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!WTPI_K2_IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
uint32_t index;
|
||||
WTPI_K1_SymmetricKey* key = OPKI_AllocFromObjectTable(&key_table, &index);
|
||||
if (key == NULL) return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
|
||||
|
||||
// Encrypt and sign key
|
||||
OEMCryptoResult result =
|
||||
EncryptAndSignKey(key_handle, DEVICE_KEY_WRAP_INTERNAL_KEY,
|
||||
(uint8_t*)(&key->key_data), sizeof(key->key_data));
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
OPKI_FreeFromObjectTable(&key_table, key);
|
||||
return result;
|
||||
}
|
||||
key->key_type = key_type;
|
||||
key->key_size = key_size;
|
||||
|
||||
WTPI_K1_SymmetricKey_Handle handle =
|
||||
malloc(sizeof(wtpi_k1_symmetric_key_handle));
|
||||
if (handle == NULL) {
|
||||
OPKI_FreeFromObjectTable(&key_table, key);
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
handle->index = index;
|
||||
*out_key_handle = handle;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_CreateKeyHandle(
|
||||
const uint8_t* serialized_bytes, size_t size, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (serialized_bytes == NULL || size == 0 || out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(size);
|
||||
if (key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle = NULL;
|
||||
OEMCryptoResult result =
|
||||
WTPI_K2_CreateKeyHandle(serialized_bytes, size, key_type, &k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result =
|
||||
K1_CreateKeyHandle(k2_key_handle, key_type, key_size, out_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
result = KM_PairKeyHandle(*out_key_handle, k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle(
|
||||
uint32_t context, SymmetricKeyType out_key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size) {
|
||||
if (out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle = NULL;
|
||||
OEMCryptoResult result = WTPI_K2_DeriveDeviceKeyIntoHandle(
|
||||
context, out_key_type, &k2_key_handle, out_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = K1_CreateKeyHandle(k2_key_handle, out_key_type, out_key_size,
|
||||
out_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
result = KM_PairKeyHandle(*out_key_handle, k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key,
|
||||
size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (decrypt_key_handle == NULL || enc_key == NULL || enc_key_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(decrypt_key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (enc_key_length != KEY_SIZE_128 && enc_key_length != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_decrypt_key_handle =
|
||||
KM_LookupKeyHandle(decrypt_key_handle, true);
|
||||
if (k2_decrypt_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle = NULL;
|
||||
OEMCryptoResult result = WTPI_K2_AESDecryptAndCreateKeyHandle(
|
||||
k2_decrypt_key_handle, enc_key, enc_key_length, iv, key_type,
|
||||
&k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = K1_CreateKeyHandle(k2_key_handle, key_type, (KeySize)enc_key_length,
|
||||
out_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
result = KM_PairKeyHandle(*out_key_handle, k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys,
|
||||
size_t enc_mac_keys_length, const uint8_t* iv,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_server,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_client) {
|
||||
if (decrypt_key_handle == NULL || enc_mac_keys == NULL ||
|
||||
enc_mac_keys_length == 0 || out_mac_key_server == NULL ||
|
||||
out_mac_key_client == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(decrypt_key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (enc_mac_keys_length != 2 * MAC_KEY_SIZE) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_decrypt_key_handle =
|
||||
KM_LookupKeyHandle(decrypt_key_handle, true);
|
||||
if (k2_decrypt_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
WTPI_K2_SymmetricKey_Handle k2_server_key_handle = NULL;
|
||||
WTPI_K2_SymmetricKey_Handle k2_client_key_handle = NULL;
|
||||
result = WTPI_K2_AESDecryptAndCreateKeyHandleForMacKeys(
|
||||
k2_decrypt_key_handle, enc_mac_keys, enc_mac_keys_length, iv,
|
||||
&k2_server_key_handle, &k2_client_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = K1_CreateKeyHandle(k2_server_key_handle, MAC_KEY_SERVER,
|
||||
MAC_KEY_SIZE, out_mac_key_server);
|
||||
if (result != OEMCrypto_SUCCESS) goto cleanup;
|
||||
|
||||
result = K1_CreateKeyHandle(k2_client_key_handle, MAC_KEY_CLIENT,
|
||||
MAC_KEY_SIZE, out_mac_key_client);
|
||||
if (result != OEMCrypto_SUCCESS) goto cleanup;
|
||||
|
||||
result = KM_PairKeyHandle(*out_mac_key_server, k2_server_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) goto cleanup;
|
||||
result = KM_PairKeyHandle(*out_mac_key_client, k2_client_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) goto cleanup;
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
cleanup:;
|
||||
if (k2_server_key_handle != NULL) WTPI_K2_FreeKeyHandle(k2_server_key_handle);
|
||||
if (k2_client_key_handle != NULL) WTPI_K2_FreeKeyHandle(k2_client_key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_DeriveKeyFromKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, uint8_t counter,
|
||||
const uint8_t* context, size_t context_length,
|
||||
SymmetricKeyType out_key_type, KeySize out_key_size,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (key_handle == NULL || context == NULL || context_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
SymmetricKeyType key_type;
|
||||
OEMCryptoResult result = GetKeyType(key_handle, &key_type);
|
||||
if (result != OEMCrypto_SUCCESS || key_type != DERIVING_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_derived_key_handle = NULL;
|
||||
result = WTPI_K2_DeriveKeyFromKeyHandle(k2_key_handle, counter, context,
|
||||
context_length, out_key_type,
|
||||
out_key_size, &k2_derived_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = K1_CreateKeyHandle(k2_derived_key_handle, out_key_type, out_key_size,
|
||||
out_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_derived_key_handle);
|
||||
return result;
|
||||
}
|
||||
result = KM_PairKeyHandle(*out_key_handle, k2_derived_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_derived_key_handle);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_WrapKey(uint32_t context,
|
||||
WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType key_type, uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length) {
|
||||
if (key_handle == NULL || wrapped_key == NULL || wrapped_key_length == 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, true);
|
||||
if (k2_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return WTPI_K2_WrapKey(context, k2_key_handle, key_type, wrapped_key,
|
||||
wrapped_key_length);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_UnwrapIntoKeyHandle(
|
||||
uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length,
|
||||
SymmetricKeyType key_type, WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (wrapped_key == NULL || wrapped_key_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(wrapped_key_length);
|
||||
if (key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle = NULL;
|
||||
OEMCryptoResult result = WTPI_K2_UnwrapIntoKeyHandle(
|
||||
context, wrapped_key, wrapped_key_length, key_type, &k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result =
|
||||
K1_CreateKeyHandle(k2_key_handle, key_type, key_size, out_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
result = KM_PairKeyHandle(*out_key_handle, k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
return result;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_FreeKeyHandle(WTPI_K1_SymmetricKey_Handle key_handle) {
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
// Free paired layer 2 key handle
|
||||
WTPI_K2_SymmetricKey_Handle k2_key_handle =
|
||||
KM_LookupKeyHandle(key_handle, false);
|
||||
if (WTPI_K2_IsKeyHandleValid(k2_key_handle)) {
|
||||
uint32_t k2_index;
|
||||
result = KM_GetLayer2KeyIndex(k2_key_handle, &k2_index);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
UnpairKeyHandle(k2_index);
|
||||
result = WTPI_K2_FreeKeyHandle(k2_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
}
|
||||
// Free layer 1 key
|
||||
result = OPKI_FreeFromObjectTableByIndex(&key_table, key_handle->index);
|
||||
free(key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_CopyToOutputBuffer(
|
||||
const uint8_t* in, size_t size, const OPK_OutputBuffer* output_buffer,
|
||||
size_t output_offset) {
|
||||
size_t total_size;
|
||||
if (OPK_AddOverflowUX(output_offset, size, &total_size)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (in == NULL || output_buffer == NULL || size == 0 ||
|
||||
total_size > output_buffer->size) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
uint8_t* dest = NULL;
|
||||
if (output_buffer->type == OPK_SECURE_OUTPUT_BUFFER) {
|
||||
OEMCryptoResult result = WTPI_GetSecureBufferAddress(
|
||||
output_buffer->buffer.secure, output_offset, &dest);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
} else if (output_buffer->type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) {
|
||||
dest = output_buffer->buffer.clear_insecure + output_offset;
|
||||
} else {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
memmove(dest, in, size);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_RandomBytes(uint8_t* out, size_t size) {
|
||||
if (out == NULL || size == 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
return WTPI_C2_RandomBytes(out, size);
|
||||
}
|
||||
@@ -0,0 +1,658 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> /* THIS IS TEMPORARY UNTIL WE GET AN ALLOCATOR. */
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "crypto_util.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_object_table.h"
|
||||
#include "oemcrypto_overflow.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_config_macros.h"
|
||||
#include "wtpi_device_key_access_interface.h"
|
||||
#include "wtpi_device_key_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
#include "wtpi_secure_buffer_access_interface.h"
|
||||
|
||||
/******************************************************************************
|
||||
* This is a reference implementation of Crypto and Key Management layer 1,
|
||||
* using OpenSSL/BoringSSL. The keys in this layer are encrypted and signed by a
|
||||
* device specific key, and are stored in a pre-allocated table.
|
||||
******************************************************************************/
|
||||
|
||||
/** Wrapped key data to be saved in key management layer 1 table. */
|
||||
typedef struct WrappedKeyData {
|
||||
uint8_t iv[KEY_IV_SIZE];
|
||||
uint8_t wrapped_key[MAX_WRAPPED_SYMMETRIC_KEY_SIZE];
|
||||
} WrappedKeyData;
|
||||
|
||||
/** Signed and wrapped key data to be saved in key management layer 1 table. */
|
||||
typedef struct SignedWrappedKeyData {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
WrappedKeyData wrapped_key_data;
|
||||
} SignedWrappedKeyData;
|
||||
|
||||
typedef struct WTPI_K1_SymmetricKey {
|
||||
SymmetricKeyType key_type;
|
||||
KeySize key_size;
|
||||
SignedWrappedKeyData key_data;
|
||||
} WTPI_K1_SymmetricKey;
|
||||
|
||||
typedef struct wtpi_k1_symmetric_key_handle {
|
||||
uint32_t index;
|
||||
bool is_key_cached;
|
||||
uint8_t cached_key[KEY_SIZE_256];
|
||||
} wtpi_k1_symmetric_key_handle;
|
||||
|
||||
/**
|
||||
* Key table used by key management layer 1. Keys in the table are wrapped by a
|
||||
* device specific key. When being used, the key needs to be unwrapped and
|
||||
* verified first.
|
||||
*/
|
||||
DEFINE_OBJECT_TABLE(key_table, WTPI_K1_SymmetricKey, MAX_NUMBER_OF_KEYS, NULL);
|
||||
|
||||
static bool IsKeyValid(uint32_t index) {
|
||||
WTPI_K1_SymmetricKey* key = OPKI_GetFromObjectTable(&key_table, index);
|
||||
switch (key->key_type) {
|
||||
case CONTENT_KEY:
|
||||
// We cheat a little here. We also call generic crypto keys "content
|
||||
// keys", even though some of them are 256 bit HMAC keys.
|
||||
return key->key_size == KEY_SIZE_128 || key->key_size == KEY_SIZE_256;
|
||||
case ENTITLEMENT_KEY:
|
||||
case MAC_KEY_SERVER:
|
||||
case MAC_KEY_CLIENT:
|
||||
return key->key_size == KEY_SIZE_256;
|
||||
case ENCRYPTION_KEY:
|
||||
case DERIVING_KEY:
|
||||
return key->key_size == KEY_SIZE_128 || key->key_size == KEY_SIZE_256;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsKeyHandleValid(WTPI_K1_SymmetricKey_Handle key_handle) {
|
||||
if (key_handle == NULL || key_handle->index >= MAX_NUMBER_OF_KEYS ||
|
||||
!IsKeyValid(key_handle->index)) {
|
||||
return false;
|
||||
}
|
||||
if (!key_handle->is_key_cached) {
|
||||
for (size_t i = 0; i < sizeof(key_handle->cached_key); i++) {
|
||||
if (key_handle->cached_key[i] != 0) {
|
||||
LOGE("The key is not supposed to be cached. Something might be wrong.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static OEMCryptoResult GetKeyType(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType* type) {
|
||||
if (key_handle == NULL || type == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, key_handle->index);
|
||||
*type = key->key_type;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult DeriveFromDeviceKey(uint32_t context,
|
||||
SymmetricKeyType out_key_type,
|
||||
uint8_t* out_key,
|
||||
KeySize out_key_size) {
|
||||
ABORT_IF(out_key == NULL, "Parameters are NULL or 0");
|
||||
ABORT_IF(out_key_size != KEY_SIZE_128 && out_key_size != KEY_SIZE_256,
|
||||
"Invalid key size");
|
||||
|
||||
const uint8_t* device_key = WTPI_GetDeviceKey();
|
||||
KeySize device_key_size = WTPI_GetDeviceKeySize();
|
||||
// Prepare full context for key derivation
|
||||
// Server and client MAC keys must derive to the same key.
|
||||
const SymmetricKeyType type_temp =
|
||||
out_key_type == MAC_KEY_SERVER ? MAC_KEY_CLIENT : out_key_type;
|
||||
// Cast the type into 32 bits so it is the same size as the gap left for it in
|
||||
// full_context. This will be a no-op on most architectures.
|
||||
const uint32_t type_32 = (uint32_t)type_temp;
|
||||
// Build a full context that is unique to this starting context / key type
|
||||
// combination. We start with a context template with blanks at the beginning
|
||||
// and fill the blanks with the starting context and key type.
|
||||
uint8_t full_context[20] = {'.', '.', '.', '.', '.', '.', '.', '.', 'W', 'i',
|
||||
'd', 'e', 'v', 'i', 'n', 'e', ' ', 'O', 'P', 'K'};
|
||||
const size_t context_length = sizeof(full_context);
|
||||
memcpy(full_context, &context, 4);
|
||||
memcpy(full_context + 4, &type_32, 4);
|
||||
const uint8_t counter = 1;
|
||||
if (!OPKI_DeriveKeyWithCMAC(device_key, device_key_size, counter,
|
||||
full_context, context_length, out_key_size,
|
||||
out_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult EncryptAndSignKey(const uint8_t* key, size_t key_size,
|
||||
uint8_t* out, size_t out_size) {
|
||||
ABORT_IF(key == NULL || key_size == 0 || out == NULL,
|
||||
"Parameters are NULL or 0");
|
||||
ABORT_IF(out_size < sizeof(SignedWrappedKeyData),
|
||||
"Invalid output buffer size");
|
||||
|
||||
SignedWrappedKeyData* wrapped = (SignedWrappedKeyData*)out;
|
||||
/* Pick a random IV for generating keys. */
|
||||
OEMCryptoResult result = WTPI_C1_RandomBytes(
|
||||
wrapped->wrapped_key_data.iv, sizeof(wrapped->wrapped_key_data.iv));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Encrypt the key
|
||||
uint8_t encryption_key[KEY_SIZE_128];
|
||||
result = DeriveFromDeviceKey(DEVICE_KEY_WRAP_INTERNAL_KEY, ENCRYPTION_KEY,
|
||||
encryption_key,
|
||||
OPK_LengthToKeySize(sizeof(encryption_key)));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_AESCBCEncrypt(key, key_size, wrapped->wrapped_key_data.iv,
|
||||
encryption_key, sizeof(encryption_key),
|
||||
wrapped->wrapped_key_data.wrapped_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
// Compute the signature of the wrapped key and store it
|
||||
uint8_t signing_key[KEY_SIZE_256];
|
||||
result = DeriveFromDeviceKey(DEVICE_KEY_WRAP_INTERNAL_KEY, MAC_KEY_CLIENT,
|
||||
signing_key,
|
||||
OPK_LengthToKeySize(sizeof(signing_key)));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
const uint8_t* wrapped_key_data =
|
||||
(const uint8_t*)(&wrapped->wrapped_key_data);
|
||||
size_t wrapped_key_data_length = sizeof(wrapped->wrapped_key_data);
|
||||
if (!OPKI_HMAC_SHA256(wrapped_key_data, wrapped_key_data_length, signing_key,
|
||||
sizeof(signing_key), wrapped->signature)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static OEMCryptoResult VerifyAndDecryptKey(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, uint8_t* out_key, size_t out_size) {
|
||||
ABORT_IF(!IsKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
ABORT_IF(out_key == NULL, "Parameters are NULL or 0");
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, key_handle->index);
|
||||
if (key == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
ABORT_IF(out_size < (size_t)(key->key_size), "Invalid output buffer size");
|
||||
|
||||
const uint8_t* wrapped_data = (const uint8_t*)(&key->key_data);
|
||||
if (wrapped_data == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
const SignedWrappedKeyData* wrapped =
|
||||
(const SignedWrappedKeyData*)wrapped_data;
|
||||
|
||||
// Verify the signature first, before decrypting
|
||||
uint8_t signing_key[KEY_SIZE_256];
|
||||
OEMCryptoResult result = DeriveFromDeviceKey(
|
||||
DEVICE_KEY_WRAP_INTERNAL_KEY, MAC_KEY_SERVER, signing_key,
|
||||
OPK_LengthToKeySize(sizeof(signing_key)));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
const uint8_t* wrapped_key_data =
|
||||
(const uint8_t*)(&wrapped->wrapped_key_data);
|
||||
size_t wrapped_key_data_length = sizeof(wrapped->wrapped_key_data);
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
if (!OPKI_HMAC_SHA256(wrapped_key_data, wrapped_key_data_length, signing_key,
|
||||
sizeof(signing_key), computed_signature)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (memcmp(wrapped->signature, computed_signature, SHA256_DIGEST_LENGTH) !=
|
||||
0) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
// Decrypt the key
|
||||
uint8_t decryption_key[KEY_SIZE_128];
|
||||
result = DeriveFromDeviceKey(DEVICE_KEY_WRAP_INTERNAL_KEY, ENCRYPTION_KEY,
|
||||
decryption_key,
|
||||
OPK_LengthToKeySize(sizeof(decryption_key)));
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_AESCBCDecrypt(wrapped->wrapped_key_data.wrapped_key,
|
||||
(size_t)(key->key_size), wrapped->wrapped_key_data.iv,
|
||||
decryption_key, sizeof(decryption_key), out_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* Caller to ensure the inputs are valid. */
|
||||
static OEMCryptoResult PrepareCachedKey(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
size_t size) {
|
||||
if (key_handle->is_key_cached) return OEMCrypto_SUCCESS;
|
||||
OEMCryptoResult result =
|
||||
VerifyAndDecryptKey(key_handle, key_handle->cached_key, size);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
memset(key_handle->cached_key, 0, sizeof(key_handle->cached_key));
|
||||
return result;
|
||||
}
|
||||
key_handle->is_key_cached = true;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_GetKeySize(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
KeySize* size) {
|
||||
if (key_handle == NULL || size == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_K1_SymmetricKey* key =
|
||||
OPKI_GetFromObjectTable(&key_table, key_handle->index);
|
||||
*size = key->key_size;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult AESCTRDecryptWithKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* in, size_t in_length,
|
||||
const uint8_t* iv, size_t block_offset, uint8_t* out) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 ||
|
||||
block_offset >= AES_BLOCK_SIZE || iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
KeySize key_size;
|
||||
OEMCryptoResult result = WTPI_K1_GetKeySize(key_handle, &key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(key_handle, (size_t)key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_AESCTRDecrypt(in, in_length, iv, key_handle->cached_key,
|
||||
(size_t)key_size, block_offset, out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the layer 1 crypto interface.
|
||||
*******************************************************************************/
|
||||
|
||||
OEMCryptoResult WTPI_C1_AESCBCDecrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
size_t key_length, const uint8_t* in,
|
||||
size_t in_length, const uint8_t* iv,
|
||||
uint8_t* out) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 ||
|
||||
in_length % AES_BLOCK_SIZE != 0 || iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(key_length);
|
||||
if (key_size != KEY_SIZE_128 && key_length != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
OEMCryptoResult result = PrepareCachedKey(key_handle, (size_t)key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_AESCBCDecrypt(in, in_length, iv, key_handle->cached_key, key_length,
|
||||
out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_AESCBCEncrypt(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* in, size_t in_length,
|
||||
const uint8_t* iv, uint8_t* out) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 ||
|
||||
in_length % AES_BLOCK_SIZE != 0 || iv == NULL || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
KeySize encryption_key_size;
|
||||
OEMCryptoResult result = WTPI_K1_GetKeySize(key_handle, &encryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(key_handle, (size_t)encryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_AESCBCEncrypt(in, in_length, iv, key_handle->cached_key,
|
||||
(size_t)encryption_key_size, out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_SHA256(const uint8_t* message, size_t message_length,
|
||||
uint8_t* out) {
|
||||
if (message == NULL || message_length == 0 || out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!SHA256(message, message_length, out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA1(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* out) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
KeySize hmac_key_size;
|
||||
OEMCryptoResult result = WTPI_K1_GetKeySize(key_handle, &hmac_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(key_handle, (size_t)hmac_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_HMAC_SHA1(message, message_length, key_handle->cached_key,
|
||||
(size_t)hmac_key_size, out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256(WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
const uint8_t* message,
|
||||
size_t message_length, uint8_t* out) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
out == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
KeySize hmac_key_size;
|
||||
OEMCryptoResult result = WTPI_K1_GetKeySize(key_handle, &hmac_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(key_handle, (size_t)hmac_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (!OPKI_HMAC_SHA256(message, message_length, key_handle->cached_key,
|
||||
(size_t)hmac_key_size, out)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_HMAC_SHA256_Verify(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, const uint8_t* message,
|
||||
size_t message_length, const uint8_t* signature) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
signature == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
OEMCryptoResult result = WTPI_C1_HMAC_SHA256(
|
||||
key_handle, message, message_length, computed_signature);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH) != 0) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
The following implement the layer 1 key management interface.
|
||||
*******************************************************************************/
|
||||
|
||||
OEMCryptoResult WTPI_K1_InitializeKeyManagement(void) {
|
||||
OPKI_UnsafeClearObjectTable(&key_table);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_TerminateKeyManagement(void) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_CreateKeyHandle(
|
||||
const uint8_t* serialized_bytes, size_t size, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (serialized_bytes == NULL || size == 0 || out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(size);
|
||||
if (key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
uint32_t index;
|
||||
WTPI_K1_SymmetricKey* key = OPKI_AllocFromObjectTable(&key_table, &index);
|
||||
if (key == NULL) return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
|
||||
OEMCryptoResult result =
|
||||
EncryptAndSignKey(serialized_bytes, size, (uint8_t*)(&key->key_data),
|
||||
sizeof(key->key_data));
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
OPKI_FreeFromObjectTable(&key_table, key);
|
||||
return result;
|
||||
}
|
||||
key->key_type = key_type;
|
||||
key->key_size = key_size;
|
||||
|
||||
WTPI_K1_SymmetricKey_Handle handle =
|
||||
malloc(sizeof(wtpi_k1_symmetric_key_handle));
|
||||
if (handle == NULL) {
|
||||
OPKI_FreeFromObjectTable(&key_table, key);
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
handle->index = index;
|
||||
handle->is_key_cached = false;
|
||||
memset(handle->cached_key, 0, sizeof(handle->cached_key));
|
||||
*out_key_handle = handle;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_DeriveDeviceKeyIntoHandle(
|
||||
uint32_t context, SymmetricKeyType out_key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle, KeySize out_key_size) {
|
||||
if (out_key_handle == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
uint8_t derived_key[KEY_SIZE_256];
|
||||
OEMCryptoResult result =
|
||||
DeriveFromDeviceKey(context, out_key_type, derived_key, out_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
return WTPI_K1_CreateKeyHandle(derived_key, (size_t)out_key_size,
|
||||
out_key_type, out_key_handle);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_key,
|
||||
size_t enc_key_length, const uint8_t* iv, SymmetricKeyType key_type,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (decrypt_key_handle == NULL || enc_key == NULL || enc_key_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(decrypt_key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(enc_key_length);
|
||||
if (key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
KeySize decryption_key_size;
|
||||
OEMCryptoResult result =
|
||||
WTPI_K1_GetKeySize(decrypt_key_handle, &decryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(decrypt_key_handle, (size_t)decryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
uint8_t key[KEY_SIZE_256];
|
||||
if (!OPKI_AESCBCDecrypt(enc_key, enc_key_length, iv,
|
||||
decrypt_key_handle->cached_key,
|
||||
(size_t)decryption_key_size, key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
return WTPI_K1_CreateKeyHandle(key, enc_key_length, key_type, out_key_handle);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_AESDecryptAndCreateKeyHandleForMacKeys(
|
||||
WTPI_K1_SymmetricKey_Handle decrypt_key_handle, const uint8_t* enc_mac_keys,
|
||||
size_t enc_mac_keys_length, const uint8_t* iv,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_server,
|
||||
WTPI_K1_SymmetricKey_Handle* out_mac_key_client) {
|
||||
if (decrypt_key_handle == NULL || enc_mac_keys == NULL ||
|
||||
enc_mac_keys_length == 0 || out_mac_key_server == NULL ||
|
||||
out_mac_key_client == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(decrypt_key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (enc_mac_keys_length != 2 * MAC_KEY_SIZE) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
KeySize decryption_key_size;
|
||||
OEMCryptoResult result =
|
||||
WTPI_K1_GetKeySize(decrypt_key_handle, &decryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = PrepareCachedKey(decrypt_key_handle, (size_t)decryption_key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
uint8_t mac_keys[2 * MAC_KEY_SIZE];
|
||||
if (!OPKI_AESCBCDecrypt(enc_mac_keys, enc_mac_keys_length, iv,
|
||||
decrypt_key_handle->cached_key,
|
||||
(size_t)decryption_key_size, mac_keys)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
result = WTPI_K1_CreateKeyHandle(mac_keys, MAC_KEY_SIZE, MAC_KEY_SERVER,
|
||||
out_mac_key_server);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WTPI_K1_CreateKeyHandle(mac_keys + MAC_KEY_SIZE, MAC_KEY_SIZE,
|
||||
MAC_KEY_CLIENT, out_mac_key_client);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_DeriveKeyFromKeyHandle(
|
||||
WTPI_K1_SymmetricKey_Handle key_handle, uint8_t counter,
|
||||
const uint8_t* context, size_t context_length,
|
||||
SymmetricKeyType out_key_type, KeySize out_key_size,
|
||||
WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (key_handle == NULL || context == NULL || context_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
SymmetricKeyType key_type;
|
||||
OEMCryptoResult result = GetKeyType(key_handle, &key_type);
|
||||
if (result != OEMCrypto_SUCCESS || key_type != DERIVING_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
KeySize key_size;
|
||||
result = WTPI_K1_GetKeySize(key_handle, &key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = PrepareCachedKey(key_handle, (size_t)key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
uint8_t derived_key[KEY_SIZE_256];
|
||||
if (!OPKI_DeriveKeyWithCMAC(key_handle->cached_key, key_size, counter,
|
||||
context, context_length, out_key_size,
|
||||
derived_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
return WTPI_K1_CreateKeyHandle(derived_key, (size_t)out_key_size,
|
||||
out_key_type, out_key_handle);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_WrapKey(uint32_t context,
|
||||
WTPI_K1_SymmetricKey_Handle key_handle,
|
||||
SymmetricKeyType key_type, uint8_t* wrapped_key,
|
||||
size_t wrapped_key_length) {
|
||||
if (key_handle == NULL || wrapped_key == NULL || wrapped_key_length == 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
KeySize key_size;
|
||||
OEMCryptoResult result = WTPI_K1_GetKeySize(key_handle, &key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (wrapped_key_length < (size_t)key_size) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
result = PrepareCachedKey(key_handle, (size_t)key_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
/* TODO(b/158766099): encrypt the data instead of memcpy. */
|
||||
memcpy(wrapped_key, key_handle->cached_key, (size_t)key_size);
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_UnwrapIntoKeyHandle(
|
||||
uint32_t context, const uint8_t* wrapped_key, size_t wrapped_key_length,
|
||||
SymmetricKeyType key_type, WTPI_K1_SymmetricKey_Handle* out_key_handle) {
|
||||
if (wrapped_key == NULL || wrapped_key_length == 0 ||
|
||||
out_key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
const KeySize key_size = OPK_LengthToKeySize(wrapped_key_length);
|
||||
if (key_size != KEY_SIZE_128 && key_size != KEY_SIZE_256) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
/* TODO(b/158766099): decrypt the key data before creating key handle. */
|
||||
|
||||
return WTPI_K1_CreateKeyHandle(wrapped_key, wrapped_key_length, key_type,
|
||||
out_key_handle);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_K1_FreeKeyHandle(WTPI_K1_SymmetricKey_Handle key_handle) {
|
||||
if (!IsKeyHandleValid(key_handle)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
||||
result = OPKI_FreeFromObjectTableByIndex(&key_table, key_handle->index);
|
||||
memset(key_handle->cached_key, 0, sizeof(key_handle->cached_key));
|
||||
free(key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_CopyToOutputBuffer(
|
||||
const uint8_t* in, size_t size, const OPK_OutputBuffer* output_buffer,
|
||||
size_t output_offset) {
|
||||
size_t total_size;
|
||||
if (OPK_AddOverflowUX(output_offset, size, &total_size)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (in == NULL || output_buffer == NULL || size == 0 ||
|
||||
total_size > output_buffer->size) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
uint8_t* dest = NULL;
|
||||
if (output_buffer->type == OPK_SECURE_OUTPUT_BUFFER) {
|
||||
OEMCryptoResult result = WTPI_GetSecureBufferAddress(
|
||||
output_buffer->buffer.secure, output_offset, &dest);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
} else if (output_buffer->type == OPK_CLEAR_INSECURE_OUTPUT_BUFFER) {
|
||||
dest = output_buffer->buffer.clear_insecure + output_offset;
|
||||
} else {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
memmove(dest, in, size);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_C1_RandomBytes(uint8_t* out, size_t size) {
|
||||
if (out == NULL || size == 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
if (RAND_bytes(out, size) != 1) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
/* Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine License
|
||||
Agreement. */
|
||||
|
||||
#include "wtpi_crypto_asymmetric_interface.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "cose_util.h"
|
||||
#include "ecc_util.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_compiler_attributes.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "openssl/curve25519.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hkdf.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/x509.h"
|
||||
#include "rsa_util.h"
|
||||
#include "wtpi_abort_interface.h"
|
||||
#include "wtpi_device_key_access_interface.h"
|
||||
#include "wtpi_device_key_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
|
||||
typedef struct tee_asymmetric_key_handle {
|
||||
AsymmetricKeyType key_type;
|
||||
RSA* rsa_key;
|
||||
EC_KEY* ecc_key;
|
||||
// Consider to use EVP_KEY instead of serialized key.
|
||||
uint8_t ed25519_key[ED25519_PRIVATE_KEY_LEN];
|
||||
} tee_asymmetric_key_handle;
|
||||
|
||||
// Returns true as long as one byte of ed25519_key is non-zero.
|
||||
static bool HasED25519Key(WTPI_AsymmetricKey_Handle key_handle) {
|
||||
for (size_t i = 0; i < ED25519_PRIVATE_KEY_LEN; ++i) {
|
||||
if (key_handle->ed25519_key[i] != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsAsymmetricKeyHandleValid(WTPI_AsymmetricKey_Handle key_handle) {
|
||||
if (key_handle == NULL) return false;
|
||||
switch (key_handle->key_type) {
|
||||
case DRM_RSA_PRIVATE_KEY: {
|
||||
if (key_handle->rsa_key == NULL) return false;
|
||||
if (key_handle->ecc_key != NULL) return false;
|
||||
if (HasED25519Key(key_handle)) return false;
|
||||
break;
|
||||
}
|
||||
case DRM_ECC_PRIVATE_KEY: {
|
||||
if (key_handle->ecc_key == NULL) return false;
|
||||
if (key_handle->rsa_key != NULL) return false;
|
||||
if (HasED25519Key(key_handle)) return false;
|
||||
break;
|
||||
}
|
||||
case PROV40_ED25519_PRIVATE_KEY: {
|
||||
if (key_handle->ecc_key != NULL) return false;
|
||||
if (key_handle->rsa_key != NULL) return false;
|
||||
if (!HasED25519Key(key_handle)) return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsSupportedAsymmetricKeyType(AsymmetricKeyType key_type) {
|
||||
return (key_type == DRM_RSA_PRIVATE_KEY || key_type == DRM_ECC_PRIVATE_KEY ||
|
||||
key_type == PROV40_ED25519_PRIVATE_KEY);
|
||||
}
|
||||
|
||||
static bool CreateAsymmetricRSAKeyHandle(const uint8_t* serialized_bytes,
|
||||
size_t size,
|
||||
WTPI_AsymmetricKey_Handle handle) {
|
||||
if (serialized_bytes == NULL || size == 0 || handle == NULL) {
|
||||
return false;
|
||||
}
|
||||
RSA* rsa = NULL;
|
||||
if (!DeserializePKCS8PrivateKey(serialized_bytes, size, &rsa)) {
|
||||
return false;
|
||||
}
|
||||
if (!CheckRSAKey(rsa)) {
|
||||
RSA_free(rsa);
|
||||
return false;
|
||||
}
|
||||
handle->key_type = DRM_RSA_PRIVATE_KEY;
|
||||
handle->rsa_key = rsa;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateAsymmetricECCKeyHandle(const uint8_t* serialized_bytes,
|
||||
size_t size,
|
||||
WTPI_AsymmetricKey_Handle handle) {
|
||||
EC_KEY* ecc_key = NULL;
|
||||
if (!DeserializeECCPrivateKey(serialized_bytes, size, &ecc_key)) {
|
||||
return false;
|
||||
}
|
||||
/* DeserializeECCPrivateKey will check the key after deserializing. */
|
||||
handle->key_type = DRM_ECC_PRIVATE_KEY;
|
||||
handle->ecc_key = ecc_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateAsymmetricED25519KeyHandle(const uint8_t* serialized_bytes,
|
||||
size_t size,
|
||||
WTPI_AsymmetricKey_Handle handle) {
|
||||
if (serialized_bytes == NULL || size != ED25519_PRIVATE_KEY_LEN) {
|
||||
return false;
|
||||
}
|
||||
handle->key_type = PROV40_ED25519_PRIVATE_KEY;
|
||||
memcpy(handle->ed25519_key, serialized_bytes, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_CreateAsymmetricKeyHandle(
|
||||
const uint8_t* serialized_bytes, size_t size, AsymmetricKeyType key_type,
|
||||
WTPI_AsymmetricKey_Handle* key_handle) {
|
||||
if (serialized_bytes == NULL || size == 0 ||
|
||||
!IsSupportedAsymmetricKeyType(key_type) || key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
WTPI_AsymmetricKey_Handle handle = malloc(sizeof(tee_asymmetric_key_handle));
|
||||
if (handle == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
switch (key_type) {
|
||||
case DRM_RSA_PRIVATE_KEY: {
|
||||
if (!CreateAsymmetricRSAKeyHandle(serialized_bytes, size, handle))
|
||||
goto cleanup;
|
||||
break;
|
||||
}
|
||||
case DRM_ECC_PRIVATE_KEY: {
|
||||
if (!CreateAsymmetricECCKeyHandle(serialized_bytes, size, handle))
|
||||
goto cleanup;
|
||||
break;
|
||||
}
|
||||
case PROV40_ED25519_PRIVATE_KEY: {
|
||||
if (!CreateAsymmetricED25519KeyHandle(serialized_bytes, size, handle))
|
||||
goto cleanup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*key_handle = handle;
|
||||
return OEMCrypto_SUCCESS;
|
||||
cleanup:
|
||||
if (handle) {
|
||||
free(handle);
|
||||
}
|
||||
return OEMCrypto_ERROR_INVALID_KEY;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_FreeAsymmetricKeyHandle(
|
||||
WTPI_AsymmetricKey_Handle key_handle) {
|
||||
if (key_handle == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
switch (key_handle->key_type) {
|
||||
case DRM_RSA_PRIVATE_KEY: {
|
||||
RSA_free(key_handle->rsa_key);
|
||||
key_handle->rsa_key = NULL;
|
||||
break;
|
||||
}
|
||||
case DRM_ECC_PRIVATE_KEY: {
|
||||
EC_KEY_free(key_handle->ecc_key);
|
||||
key_handle->ecc_key = NULL;
|
||||
break;
|
||||
}
|
||||
case PROV40_ED25519_PRIVATE_KEY: {
|
||||
memset(key_handle->ed25519_key, 0, sizeof(key_handle->ed25519_key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(key_handle);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_RSASign(WTPI_AsymmetricKey_Handle key_handle,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
signature_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
if (key_handle->key_type != DRM_RSA_PRIVATE_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
RSA* rsa = key_handle->rsa_key;
|
||||
const size_t max_signature_size = (size_t)RSA_size(rsa);
|
||||
if (signature == NULL || *signature_length < max_signature_size) {
|
||||
*signature_length = max_signature_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
/* This is the standard padding scheme used for license requests. */
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
size_t local_signature_length = *signature_length;
|
||||
if (!RSASignSSAPSSSHA1(rsa, message, message_length, signature,
|
||||
&local_signature_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
*signature_length = local_signature_length;
|
||||
/* This is the alternate padding scheme used by cast receivers only. */
|
||||
} else if (padding_scheme == kSign_PKCS1_Block1) {
|
||||
/* Pad the message with PKCS1 padding, and then encrypt. */
|
||||
const int encrypt_res = RSA_private_encrypt(
|
||||
message_length, message, signature, rsa, RSA_PKCS1_PADDING);
|
||||
if (encrypt_res <= 0) {
|
||||
dump_ssl_error();
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
*signature_length = (size_t)encrypt_res;
|
||||
} else { /* Bad RSA_Padding_Scheme */
|
||||
return OEMCrypto_ERROR_INVALID_KEY;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_RSADecrypt(WTPI_AsymmetricKey_Handle key_handle,
|
||||
const uint8_t* in, size_t in_length,
|
||||
uint8_t* out, size_t* out_length) {
|
||||
if (key_handle == NULL || in == NULL || in_length == 0 || out == NULL ||
|
||||
out_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
if (key_handle->key_type != DRM_RSA_PRIVATE_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
RSA* rsa = key_handle->rsa_key;
|
||||
const size_t max_cleartext_size = (size_t)RSA_size(rsa);
|
||||
if (*out_length < max_cleartext_size) {
|
||||
*out_length = max_cleartext_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (!RSAPrivateDecrypt(rsa, in, in_length, out, out_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_ECCSign(WTPI_AsymmetricKey_Handle key_handle,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length) {
|
||||
if (key_handle == NULL || message == NULL || message_length == 0 ||
|
||||
signature_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
if (key_handle->key_type != DRM_ECC_PRIVATE_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
EC_KEY* ecc_key = key_handle->ecc_key;
|
||||
const size_t max_signature_size = ECDSA_size(ecc_key);
|
||||
if (signature == NULL || *signature_length < max_signature_size) {
|
||||
*signature_length = max_signature_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (!ECCSignECDSA(ecc_key, message, message_length, signature,
|
||||
signature_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_ECCDeriveSessionKey(WTPI_AsymmetricKey_Handle key_handle,
|
||||
const uint8_t* key_source,
|
||||
size_t key_source_length,
|
||||
uint8_t* session_key,
|
||||
size_t* session_key_length) {
|
||||
if (key_handle == NULL || key_source == NULL || key_source_length == 0 ||
|
||||
session_key_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
if (key_handle->key_type != DRM_ECC_PRIVATE_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (session_key == NULL || *session_key_length < KEY_SIZE_256) {
|
||||
*session_key_length = KEY_SIZE_256;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
EC_KEY* ecc_key = key_handle->ecc_key;
|
||||
if (!ECCWidevineECDHSessionKey(ecc_key, key_source, key_source_length,
|
||||
session_key, session_key_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_GetSignatureSize(WTPI_AsymmetricKey_Handle key_handle,
|
||||
size_t* key_size) {
|
||||
if (key_handle == NULL || key_size == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key_handle), "Impossible key handle.");
|
||||
*key_size = 0;
|
||||
switch (key_handle->key_type) {
|
||||
case DRM_RSA_PRIVATE_KEY:
|
||||
*key_size = RSA_size(key_handle->rsa_key);
|
||||
break;
|
||||
case DRM_ECC_PRIVATE_KEY:
|
||||
*key_size = ECDSA_size(key_handle->ecc_key);
|
||||
break;
|
||||
case PROV40_ED25519_PRIVATE_KEY:
|
||||
*key_size = ED25519_SIGNATURE_LEN;
|
||||
break;
|
||||
}
|
||||
if (*key_size == 0) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_ED25519Sign(WTPI_AsymmetricKey_Handle key,
|
||||
const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length) {
|
||||
if (key == NULL || message == NULL || message_length == 0 ||
|
||||
signature_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ABORT_IF(!IsAsymmetricKeyHandleValid(key), "Impossible key handle.");
|
||||
if (key->key_type != PROV40_ED25519_PRIVATE_KEY) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (signature == NULL || *signature_length < ED25519_SIGNATURE_LEN) {
|
||||
*signature_length = ED25519_SIGNATURE_LEN;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (ED25519_sign(signature, message, message_length, key->ed25519_key) != 1) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
*signature_length = ED25519_SIGNATURE_LEN;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the device specific asymmetric key pair that is used in
|
||||
* provisioning 4. This key must be unique per individual device. The key must
|
||||
* be identical across reboots.
|
||||
* Caller retains ownership of all parameters.
|
||||
*/
|
||||
static OEMCryptoResult GetDeviceAsymmetricKeyIntoHandle(
|
||||
AsymmetricKeyType* key_type, WTPI_AsymmetricKey_Handle* private_key_handle,
|
||||
uint8_t* public_key, size_t* public_key_length) {
|
||||
ABORT_IF_NULL(key_type);
|
||||
ABORT_IF_NULL(private_key_handle);
|
||||
ABORT_IF_NULL(public_key_length);
|
||||
if (public_key == NULL || *public_key_length < ED25519_PUBLIC_KEY_LEN) {
|
||||
*public_key_length = ED25519_PUBLIC_KEY_LEN;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
const uint8_t* device_key = WTPI_GetDeviceKey();
|
||||
const KeySize device_key_size = WTPI_GetDeviceKeySize();
|
||||
if (device_key == NULL || device_key_size == 0) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
const int32_t context = DEVICE_KEY_PROV40_SEED;
|
||||
const uint32_t type_32 = (uint32_t)DERIVING_KEY;
|
||||
// Build a full context that is unique to this starting context / key type
|
||||
// combination. We start with a context template with blanks at the beginning
|
||||
// and fill the blanks with the starting context and key type.
|
||||
uint8_t full_context[20] = {'.', '.', '.', '.', '.', '.', '.', '.', 'W', 'i',
|
||||
'd', 'e', 'v', 'i', 'n', 'e', ' ', 'O', 'P', 'K'};
|
||||
memcpy(full_context, &context, 4);
|
||||
memcpy(full_context + 4, &type_32, 4);
|
||||
|
||||
uint8_t seed[32];
|
||||
if (HKDF(seed, sizeof(seed), EVP_sha256(), device_key, device_key_size,
|
||||
/*salt=*/NULL, /*salt_len=*/0, full_context,
|
||||
sizeof(full_context)) != 1) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
uint8_t private_key_temp[ED25519_PRIVATE_KEY_LEN];
|
||||
ED25519_keypair_from_seed(public_key, private_key_temp, seed);
|
||||
*public_key_length = ED25519_PUBLIC_KEY_LEN;
|
||||
*key_type = PROV40_ED25519_PRIVATE_KEY;
|
||||
return WTPI_CreateAsymmetricKeyHandle(private_key_temp,
|
||||
sizeof(private_key_temp), *key_type,
|
||||
private_key_handle);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_GetBootCertificateChain(uint8_t* out, size_t* out_length) {
|
||||
if (out_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
// The |out| buffer length check is delegated to the BuildBootCertificateChain
|
||||
// call below.
|
||||
|
||||
AsymmetricKeyType key_type;
|
||||
WTPI_AsymmetricKey_Handle private_key_handle;
|
||||
uint8_t public_key[ED25519_PUBLIC_KEY_LEN];
|
||||
size_t public_key_length = sizeof(public_key);
|
||||
OEMCryptoResult result = GetDeviceAsymmetricKeyIntoHandle(
|
||||
&key_type, &private_key_handle, public_key, &public_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
// Only ED key is expected for now.
|
||||
if (key_type != PROV40_ED25519_PRIVATE_KEY) {
|
||||
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
result = BuildBootCertificateChain(public_key, public_key_length, key_type,
|
||||
private_key_handle, out, out_length);
|
||||
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair(
|
||||
AsymmetricKeyType* key_type, uint8_t* wrapped_private_key,
|
||||
size_t* wrapped_private_key_length, uint8_t* public_key,
|
||||
size_t* public_key_length) {
|
||||
if (key_type == NULL || wrapped_private_key_length == NULL ||
|
||||
public_key_length == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// This implementation generates ECC key. An alternative is RSA key.
|
||||
*key_type = DRM_ECC_PRIVATE_KEY;
|
||||
// Check buffer sizes.
|
||||
size_t required_wrapped_private_key_length = 0;
|
||||
OEMCryptoResult result = WTPI_GetWrappedAsymmetricKeySize(
|
||||
PKCS8_ECC_KEY_MAX_SIZE + AES_BLOCK_SIZE, *key_type,
|
||||
&required_wrapped_private_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
const size_t required_public_key_length = PKCS8_ECC_KEY_MAX_SIZE;
|
||||
if (wrapped_private_key == NULL ||
|
||||
*wrapped_private_key_length < required_wrapped_private_key_length ||
|
||||
public_key == NULL || *public_key_length < required_public_key_length) {
|
||||
*wrapped_private_key_length = required_wrapped_private_key_length;
|
||||
*public_key_length = required_public_key_length;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
uint8_t clear_private_key[PKCS8_ECC_KEY_MAX_SIZE + AES_BLOCK_SIZE];
|
||||
size_t clear_private_key_length = sizeof(clear_private_key);
|
||||
if (!NewEccKeyPair(clear_private_key, &clear_private_key_length, public_key,
|
||||
public_key_length)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
// Add padding.
|
||||
const uint8_t padding =
|
||||
AES_BLOCK_SIZE - (clear_private_key_length % AES_BLOCK_SIZE);
|
||||
memset(clear_private_key + clear_private_key_length, padding, padding);
|
||||
clear_private_key_length += padding;
|
||||
|
||||
size_t actual_wrapped_private_key_length = 0;
|
||||
result = WTPI_GetWrappedAsymmetricKeySize(clear_private_key_length, *key_type,
|
||||
&actual_wrapped_private_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (*wrapped_private_key_length < actual_wrapped_private_key_length) {
|
||||
// This should not happen as we have checked buffer size.
|
||||
*wrapped_private_key_length = actual_wrapped_private_key_length;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
*wrapped_private_key_length = actual_wrapped_private_key_length;
|
||||
return WTPI_WrapAsymmetricKey(wrapped_private_key,
|
||||
*wrapped_private_key_length, *key_type,
|
||||
clear_private_key, clear_private_key_length);
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_DeviceKeyCoseSign1(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (message == NULL || message_length == 0 || signature_length == 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
// The message is expected to be an EC or RSA certificate public key.
|
||||
// Notice that the signature contains |message| itself.
|
||||
if (message_length > 500) {
|
||||
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
||||
}
|
||||
const size_t kRequiredSignatureBufferSize = 1024;
|
||||
if (signature == NULL || *signature_length < kRequiredSignatureBufferSize) {
|
||||
*signature_length = kRequiredSignatureBufferSize;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
AsymmetricKeyType key_type;
|
||||
WTPI_AsymmetricKey_Handle private_key_handle;
|
||||
uint8_t public_key_unused[ED25519_PUBLIC_KEY_LEN];
|
||||
size_t public_key_length_unused = sizeof(public_key_unused);
|
||||
OEMCryptoResult result = GetDeviceAsymmetricKeyIntoHandle(
|
||||
&key_type, &private_key_handle, public_key_unused,
|
||||
&public_key_length_unused);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
// Only ED key is expected for now.
|
||||
if (key_type != PROV40_ED25519_PRIVATE_KEY) {
|
||||
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
size_t encoded_size = 0;
|
||||
result =
|
||||
DiceCoseSignAndEncodeSign1(message, message_length, private_key_handle,
|
||||
*signature_length, signature, &encoded_size);
|
||||
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
*signature_length = encoded_size;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user