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:
Fred Gylys-Colwell
2022-04-13 19:36:27 -07:00
parent 044a89ef55
commit 0a16cb2594
308 changed files with 58159 additions and 857 deletions

121
CHANGELOG.md Normal file
View 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
View 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
View 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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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',

View File

@@ -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',

View File

@@ -3,6 +3,8 @@
// License Agreement.
#include "fuzzing/odk_fuzz_helper.h"
#include <string>
#include "odk.h"
namespace oemcrypto_core_message {

View File

@@ -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
View 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
View 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.

View 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

View 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

View 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)',
],
},
]
}

View File

@@ -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`.

View 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',
],
},
],
}

File diff suppressed because it is too large Load Diff

View 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_ */

View 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;
}

View 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_ */

View 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_

View 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_ */

View 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_ */

View 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;
}

View 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_ */

View File

@@ -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;
}

View 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. */
#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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_

View 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;
}

View 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_ */

View 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_ */

View 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_

View 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;
}

View 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_

View 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));
}

View 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_ */

View 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

View 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_ */

View 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;
}

View 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_

File diff suppressed because it is too large Load Diff

View 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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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)',
],
},
},
],
}

File diff suppressed because it is too large Load Diff

View 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 headers 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_ */

View 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;
}

View 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_ */

View 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.

View 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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View 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_ */

View 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_ */

View File

@@ -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 devices
* 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 hardwares smaller key table as
* needed.
*
* * If you dont 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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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 WTPIs 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_ */

View File

@@ -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_ */

View 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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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 tables 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_ */

View 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_ */

View File

@@ -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_ */

View 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_ */

View 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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View File

@@ -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_ */

View 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);
}

View 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_ */

View 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;
}

View 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_ */

View 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(); }

View File

@@ -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;
}

View 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. */
/* 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_ */

View 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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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